The slice function in the Array prototype expects this
to reference the array on which it should work. In other words, if you have a real array:
var myArray = [1, 2, 3];
and you call slice()
:
var sliced = myArray.slice(1);
Then in this call to slice()
, this
refers to the array "myArray". As Rainos notes in a comment:
myArray.slice(1)
coincides with
myArray.slice.call(myArray, 1);
Thus, when you use call()
to call a function and pass its arguments
as a context object, the slice()
code works on arguments
. Other parameters passed through .call()
are just the parameter or parameters for slice()
. In my example above, note that I passed 1 function.
Now, as for your second question, the .invoke()
function first isolates the arguments passed after the first two. This means that when you use _.invoke()
, you pass two or more arguments to it: the first is a list for work, the second is a method, and (optional) subsequent arguments are passed to the method for each element of the list.
This call to _.map()
is complicated (and actually I think it's a little nonsense). It iterates through the list, calling a function for each value in the list. This function allows you to first determine whether the parameter "method" is actually a function. If so, then it calls this function through .apply()
with the list item as context. If the βmethodβ is not a function, it assumes that it is the property name of each list item and that the properties are functions.
So, for example, with a simple list, this is pretty simple:
var myList = [1, 2, 3]; var result = _.invoke(myList, function(n) { return this * n; }, 2);
This will give the result [2, 4, 6]
, because the function I passed multiplies my context object ( this
) with the passed parameter, and I passed 2 in the _.invoke()
call.
With a more complex list, I can use the second flavor of _.invoke()
and call the method for each object in the list:
var getName = function(prefix) { return prefix + " " + this.name; }; var list = [ { name: "Bob", getName: getName }, { name: "Sam", getName: getName }, { name: "Lou", getName: getName } ]; var result = _.invoke(list, "getName", "Congressman");
This will call the getName function for each object in the list and return a list of results. The effect will be a list ["Congressman Bob", "Congressman Sam", "Congressman Lou"]
.
Now about this nonsense. In the code for _.invoke()
:
return _.map(obj, function(value) { return (method.call ? method || value : value[method]).apply(value, args); });
This is a subexpresion method || value
method || value
will always return the value of the "method" or, at least, almost always prohibits any exotic trick. If method.call
is true, then the method
reference must also be true. Also, if it were my code, I would check the method
outside the _.map()
callback so that the solution would not need to be repeated over and over again. Maybe something like:
return _.map(obj, method.call ? function(value) { method.apply(value, args); } : function(value) { value[method].apply(value, args); } );