Reputation: 2968
Everything started with this Question
Then an answer from @MinusFour
var slice = Function.call.bind(Array.prototype.slice);
I wanted to understand, whats happening under the hood, my curiosity hence this Question.
what to achieve ? understanding of "Function.call.bind
".
Step-by-step approach for the same
Started with MDN
NOTE : I am using NodeJS here
1)
var adder = new Function('a', 'b', 'return a + b');
console.log(adder(2, 6));
**OUTPUT **
8
This is expected, Nothing Fancy
2)
This is our end goal , calling function myFunc
from the bounded
function (Function.call.bind(myFunc)
)
function myFunc(a, b) {
console.log(arguments.length, a, b, a + b);
}
3)
var adder = Function(myFunc);
console.log(adder.toString())
OUTPUT
function anonymous() { function myFunc(a, b) { console.log(a + b); } }
Expected! above code does nothing, because i am calling 'anonymous' , and it does nothing.
4)
var adder = Function.call(myFunc);
console.log(adder.toString())
OUTPUT
function anonymous() {
}
Expected!. '.call'
calls 'Function'
, with 'this'
set to 'myFunc'
and with out any param or function body. so an empty anonymous function is the output. Now, I can do "var adder = Function.call(myFunc,myFunc);"
to create the same function from step-3
So far so good
5)
var adder = Function.call.bind(myFunc);
console.log(adder.toString())
adder(2,6);
OUTPUT
function () { [native code] }
1 6 undefined NaN
Here first param is not passed to the 'myFunc'
function.
this is taken as 'this'
for function 'adder'
(the bounded Function.call
) ?
Now I understand(or did I misunderstood?) until now, but then How does below code works ?
var slice = Function.call.bind(Array.prototype.slice);
function fn(){
var arr = slice(arguments);
}
in my case first param to adder is discarded(or Function.call
consider it as 'this'
for it), same should happen with slice
above right ?
Anyway, i wanted to document it for a reference
Upvotes: 1
Views: 278
Reputation: 1074335
I'm afraid you've gone off in slightly the wrong direction. This line:
var slice = Function.call.bind(Array.prototype.slice);
never calls Function
and never arranges for it to be called later. The only thing Function
is being used for there is its call
property. Function
could have been Object
or Date
or RegExp
or any other function, or could have been Function.prototype
; doesn't matter. Function.prototype
would have been more direct and possibly less confusing.
This is a bit tricky to explain because it involves two layers of dealing with this
, where this
is different things at different times:
The call
function calls functions with a specific this
value you give it as its first argument, passing along any other arguments you give it. For example:
function foo(arg) {
console.log("this.name = " + this.name + ", arg = " + arg);
}
var obj = {name: "bar"};
foo.call(obj, "glarb"); // "this.name = bar, arg = glarb"
There, because we called call
on foo
, call
called foo
with this
set to obj
and passing along the "glarb"
argment.
call
knows what function it should call based on what this
is during the call
call. foo.call
sets this
during call
to foo
. That can be confusing, so let's diagram it:
foo.call(obj, "glarb")
calls call
:
call
sees this = foo
and the arguments obj
and "glarb"
call
calls this
(which is foo
):
foo
sees this = obj
and the single argument "glarb"
With regard to slice
, you normally see call
used with it used to create an array from something array-like that isn't really an array:
var divArray = Array.prototype.slice.call(document.querySelectorAll("div"));
or
var divArray = [].slice.call(document.querySelectorAll("div"));
There, we call call
with this
set to Array.prototype.slice
(or [].slice
, which is the same function) and passing in the collection returned by querySelectorAll
as the first argument. call
calls the function it sees as this
, using its first argument as this
for that call, and passing along any others.
So that's the first layer of this
stuff.
bind
is another function that functions have, and it's similar to call
but different: Where call
calls the target function with a given this
and arguments, bind
creates and returns a new function that will do that if you call it. Going back to our foo
example:
function foo(arg) {
console.log("this.name = " + this.name + ", arg = " + arg);
}
var obj = {name: "bar"};
var fooWithObjAndGlarb = foo.bind(obj, "glarb");
fooWithObjAndGlarb(); // "this.name = bar, arg = glarb"
This is called binding things (obj
and the "glarb"
argument) to foo
.
Unlike call
, since bind
creates a new function, we can add arguments later:
function foo(arg) {
console.log("this.name = " + this.name + ", arg = " + arg);
}
var obj = {name: "bar"};
var fooWithObj = foo.bind(obj);
fooWithObj("glarb"); // "this.name = bar, arg = glarb"
Okay, now we have all our working pieces. So what's happening in your code? Let's break it into parts:
// Get a reference to the `call` function from the `call` property
// on `Function`. The reason `Function` has a `call` property is that
// `Function` is, itself, a function, which means its prototype is
// `Function.prototype`, which has `call` on it.
var call = Function.call;
// Get a reference to the `slice` function from `Array.prototype`'s `slice` property:
var rawSlice = Array.prototype.slice;
// Create a *bound* copy of `call` that, when called, will call
// `call` with `this` set to `rawSlice`
var callBoundToSlice = call.bind(rawSlice);
(callBoundToSlice
is just called slice
in your question, but I'm using callBoundToSlice
to avoid confusion.) Binding rawSlice
to call
was the first layer of this
handling, determining what call
will see as this
. Calling callBoundToSlice
will call call
with this
set to rawSlice
. Then call
will call the function it sees as this
(rawSlice
), using its first argument as the value for this
during the call (the second layer of this
handling) and passing on any further arguments.
So our forEach
with a collection from querySelectorAll
can now look like this:
callBoundToSlice(document.querySelectorAll("div")).forEach(function(div) {
// Each div here
});
That passes the collecton returned by querySelectorAll
into callBoundToSlice
, which calls call
with this
as rawSlice
, which calls Array.prototype.slice
with this
set to the collection. Array.prototype.slice
uses this
to copy the array.
All of that said, using slice
to turn array-like objects into true arrays is a bit out of date. ES2015 introduces the Array.from
method, which can be shimmed/polyfilled on JavaScript engines that don't have it yet:
var divArray = Array.from(document.querySelectorAll("div"));
Upvotes: 3