var array1 = [1, 4, 9, 16];
map1=array1.map(Function.call,Number);
Why the output of map1 is [0,1,2,3], what this map function is doing?
Array.prototype.map
takes two arguments: the first is a function which is called with each item in the array, the second, rarely-used argument is a this
value which the first argument is called with. For example:
[1,2,3].map(function() { return this.x; }, {x: 3}) // returns [3,3,3]
Function.prototype.call
is a function that is on all functions: (Function
is itself a function, so Function.call
is Function.prototype.call
), which allows the function to be called with a specific this
and a specific set of arguments (passed individually, not as an array in the case of Function.prototype.apply
). For example:
function someFunction(y) {
return this + y;
}
someFunction.call(2, 3) // Returns 5, since this=2 and y=3
But there's an extra detail in the real example, because it's not doing someFunction.call
, it's doing Function.call
: what's going on is Function.prototype.call
is using this
to determine what function gets called: if you rebind this
, a different function gets called. You can rebind this
with, well... Function.prototype.call
.
So this is equivalent to the previous example:
function someFunction(y) {
return this + y;
}
Function.call.call(someFunction, 2, 3)
The second call to .call
rebinds the first one, so that it invokes someFunction
instead of Function
, and passes it the arguments 2 and 3: it boils down to exactly someFunction.call(2, 3)
, which we've already seen.
Going back to the real example so we can put things together, we have
[3,6,9].map(Function.call, Number)
The second argument does binding, just like .call
, so this is equivalent to:
[3,6,9].map((...args) => Function.call.call(Number, ...args))
which as we just saw, is equivalent to the more straight forward:
[3,6,9].map((...args) => Number.call(...args))
So, now, what are ...args
? For each call, they're the item in the array, the position in the array, and the whole array. So really this expands to:
[
Number.call(3, 0, [3,6,9]),
Number.call(6, 1, [3,6,9]),
Number.call(9, 2, [3,6,9])
]
Now remember, Number.call
's first argument is this
, while the other two arguments are provided to the function being called, Number
. As far as I know, Number
doesn't use this
, so it essentially just throws the first argument away, so it's:
[
Number(0, [3,6,9]),
Number(1, [3,6,9]),
Number(2, [3,6,9])
]
Number
, only takes a single argument though, which it casts to a Number. That's trivial, though since it's argument already is a number. So that's how you get [1,2,3]
. It's just the indexes, being needlessly cast to numbers, with a lot of function indirection.