Relating to the following mock code (_ relates to the lodash library):
var containerFunction = function () {
var opts = {data: value};
_.map(lines, functionForEach);
}
var functionForEach = function (line) {
Do something for each line passed in from the maps function.
Need to access opts in this scope.
return value;
}
The argument line is received from the map function, but what would be the best way of passing the opts argument to the functionForEach function while keeping the rough pattern above (if possible).
I thought something like:
_.map(lines, functionForEach(opts))
or something similar might work but obviously doesn't.
Any suggestions?
Relating to the following mock code (_ relates to the lodash library):
var containerFunction = function () {
var opts = {data: value};
_.map(lines, functionForEach);
}
var functionForEach = function (line) {
Do something for each line passed in from the maps function.
Need to access opts in this scope.
return value;
}
The argument line is received from the map function, but what would be the best way of passing the opts argument to the functionForEach function while keeping the rough pattern above (if possible).
I thought something like:
_.map(lines, functionForEach(opts))
or something similar might work but obviously doesn't.
Any suggestions?
Share Improve this question asked Dec 22, 2015 at 16:57 SeonixxSeonixx 4685 silver badges17 bronze badges 6-
Put
functionForEach
insidecontainerFunction
. If you must, passopts
as the thirdcontext
(thisArg
) argument to_.map
and access it usingthis
insidefunctionForEach
. – user663031 Commented Dec 22, 2015 at 17:00 - I didn't want to nest the function as I get the impression its frowned upon from a performance and memory perspective. – Seonixx Commented Dec 22, 2015 at 17:06
- 1 Where did you get such an impression? Even if that were the case, which it's not, the first priority should be getting nice clean code working, then worry about performance. – user663031 Commented Dec 22, 2015 at 17:09
- I was reading about this earlier. Correct me if I'm wrong, but nested functions result in each nested function being created and destroyed every time the parent function is called. I know it probably doesn't make a lot of difference unless the code is being run on a large scale system, but I'm trying to factor in as many considerations as I go... – Seonixx Commented Dec 22, 2015 at 17:20
- Today's engine optimizations mean you don't have to worry about such things if in fact they were ever an issue. It's always puzzled me why folks relatively early in their JS learning curve spend so much time worrying about performance questions, most of which are based on urban myths. – user663031 Commented Dec 22, 2015 at 17:21
3 Answers
Reset to default 7You have three alternatives:
Put
functionForEach
insidecontainerFunction
. If you are not going to usefunctionForEach
elsewhere, this makes the most sense.Write it as:
var containerFunction = function () { var opts = {data: value}; _.map(lines, function(elt) { return functionForEach(elt, opts); }); } var functionForEach = function (line, opts) { Do something for each line passed in from the maps function. Need to access opts in this scope. return value; }
- If you must, pass
opts
as the third (thisArg
) argument to_.map
and access it usingthis
insidefunctionForEachvar
.
Lodash has good utilities for function position, including partially-applying arguments. However, this isn't actually so straightforward. Let's say we want to use partialRight():
function functionForEach(item, i) { return item * i; }
_.map([1, 2, 3], _.partialRight(functionForEach, 2));
// → [0, 2, 6]
Oops. That's not correct. The reason is that we're not getting the arguments passed to functionForEach()
as we expect them. We want the i
argument to always have a value of 2
and the item
is the mapped collection value.
Let's try something else:
_.map([1, 2, 3], _.ary(_.partialRight(functionForEach, 2), 1));
// → [2, 4, 6]
That's better. The problem is that map() passes the item as the first argument, along with 2 other arguments (the index and the collection itself). Since we don't care about these latter two arguments, we can use ary() to pose a new function that ignores them.
Here's another approach:
_.map([1, 2, 3], _.partial(_.rearg(functionForEach, 1, 0), 2));
// → [2, 4, 6]
This time, we're using rearg() to change the order of our partial() function arguments. This approach I find to be less intuitive than simple going right for partialRight()
.
One final example:
_.map([1, 2, 3], _.flow(_.identity, _.partialRight(functionForEach, 2)));
// → [2, 4, 6]
Here, we're using the flow() higher-order function to pose our callback. This function takes the arguments supplied to it, and chains together the functions we pass to it - the output of the last function is the input to the next. We're using the identity() function here because it simply returns the first argument passed to it, which is exactly what we want.
This last approach is similar to the ary()
approach - it's doing the same thing. The flow()
approach is superior if we ever wanted to build on this behavior by passing in more functions, before or after functionForEach()
.
Take a look at _.partial
. Docs:
partial
var containerFunction = function() {
var opts = { data: value };
_.map(lines, _.partial(functionForEach, opts));
};
var functionForEach = function (opts, line) { ... };
_.partial
returns a new function with the arguments prepended to the functionForEach. In the example above, _.partial
returns the following function signature:
function (line) { ... }
Calling the return function with a line calls functionForEach with line and opts defaulted to whatever you passed in as the argument in _.partial
.