可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm reading the MDN Article on slice
in JavaScript. I understand everything except the 2nd example in the section titled Array-Like Objects.
It says we can simplify the first example by making slice
our own function as so:
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
function list() {
return slice(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
What I don't understand is how call
can come right after prototype
on the second line.
I usually see it in the form of Array.prototype.slice.call(arguments)
or something of that sort.
I don't understand the flow of the first two lines and how they generate this working slice
function.
回答1:
tl;dr:
var slice = Function.prototype.call.bind(unboundSlice);
is a short way of writing:
var slice = function(value, start, end) {
return unboundSlice.call(value, start, end);
};
Let's think about this line for second:
Array.prototype.slice.call(arguments)
.slice
is an array method to extract a subset of the array. It operates on the value of this
. .call
is a method every function has, it lets you set the this
value for a function execution. So, the above line lets us execute slice
as a method of arguments
, without having to mutate arguments
itself. We could have done
arguments.slice = Array.prototype.slice;
arguments.slice();
but that is not as clean.
Now looking at
Function.prototype.call.bind(unboundSlice);
As said, .call
is a method that every function has. It also operates on this
, which is expected to be a function. It calls this
and sets the this
value of that function to the first argument. You could think of call
as being similar to
function call(thisValue, arg1, arg2, ...) {
return this.apply(thisValue, [arg1, arg2, ...]);
}
Note how it calls this
as a function.
.bind
is also a method every function has. It returns a new function which has its this
value fixed to the first argument you pass in.
Let's consider what the resulting function of call.bind(unboundSlice)
would look like:
function boundCall(thisValue, arg1, arg2, ...) {
return unboundSlice.apply(thisValue, [arg1, arg2, ...]);
}
We simply replaced this
with unboundSlice
. boundCall
will now always call unboundSlice
.
回答2:
The MDN article for Function.prototype.call()
helped me wrap my head around this.
The most simplistic way I can answer:
In javascript, a Function has a method called call
. A function is an
object, and all objects inherit methods and properties from their
prototype.
So your example of Array.prototype.slice.call(arguments)
shows you calling the call method on the slice function.
The second line in the code that you are confused about: var slice = Function.prototype.call.bind(unboundSlice);
shows the call method belonging to the Function prototype.
Checkout JavaScript Prototypes if you are still confused.
1 Functions are objects.
2 "Every JavaScript object has a prototype."
3 "The prototype is also an object."
4 "All JavaScript objects inherit their properties and methods from their prototype."
In other words, back to the most simplistic way to answer this: In javascript, a Function has a method called call
.
As for understanding what bind
does, the that = this
vs .bind
example in this article helps make sense of what is going on.
If that was confusing, then make sure you understand context and scope
回答3:
slice
is a property of Array.prototype
, and it expects its this
object to be Array-like. You can use it on Array-like objects (that have a length property and have properties that you can index) that don't have their own slice function like so:
Array.prototype.slice.call(arraylikething);
That's a lot of typing, so we can make a function to do the same thing:
var slice = function(arraylikething){
return Array.prototype.slice.call(arraylikething);
};
JavaScript provides Function.prototype.bind
to bind functions to a specified this
object. So we can accomplish the same thing a bit more easily:
var slice = Function.prototype.call.bind(Array.prototype.slice);
bind
creates a new function that returns the result of call
with its this
object set to Array.prototype.slice
, the same as what we did manually above, and equivalent to your code.
回答4:
The answer from Chris Dillinger is correct and informative. But here's another way to think about it. You're being asked, in essence, to define
Function.prototype.call.bind(Array.prototype.slice)
Which you can look at this way:
fn.bind(context)
==> function(...args) {return context.fn(...args);}
// 1. definition of `bind` (oversimplified, but enough for this case)
fn.bind(unboundSlice)
==> function(...args) {return unboundSlice.fn(...args);}
// 2. substitute `unboundSlice` for `context`
Function.prototype.call.bind(unboundSlice)
==> function(...args) {return unboundSlice[Function.prototype.call](...args);}
// 3. substitute `Function.prototype.call` for `fn`.
Function.prototype.call.bind(unboundSlice)
==> function(...args) {return unboundSlice[.call(...args);}
// 4. walk the prototype chain
Function.prototype.call.bind(Array.prototype.slice)
==> function(...args) {return Array.prototype.slice.call(...args);}
// 5. substitue `Array.prototype.slice` for `unboundSlice`
The only step that's even slightly tricky is step 4, where you have to realize that all functions inherit the call
method from their prototype chain, so invoking call
on them is merely an alternative means of invoking the functions themselves.
回答5:
In the first line, Array.prototype.slice
(which is a method) is simply referenced via unboundSlice
. You're essentially 'extracting' the slice method from Array.prototype
.
In the second line, the same thing happens for Function.prototype.call
, which is also a method of ALL functions. (it's defined in Function.prototype
, and inherited by all functions).
Next, by using .bind(unboundSlice)
the Call function's this
value is bound to the reference to Array.prototype.slice
, which essentially results in the same thing as Array.prototype.slice.call()
, where call also has its this
bound to slice
, because of it being a method of it, AND because it's being called like that.
Lastly, the bound call method is referenced via var slice
;
The general idea here is that you're able to use the functionality of an array method (slice) in another context (the global scope).
So now, instead of calling call
when it was already a method of slice
, you're binding slice
to be the this
value of call
in order to achieve the same behaviour.