JavaScript call() 和原型 - 切片函数

JavaScript call() and Prototype - Slice Function

我正在 JavaScript 阅读 slice 上的 MDN Article。除了标题为 Array-Like Objects.

的部分中的第二个示例外,我了解所有内容

它说我们可以通过使 slice 成为我们自己的函数来简化第一个示例:

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]

我不明白的是 call 怎么会在第二行的 prototype 之后。

我通常以 Array.prototype.slice.call(arguments) 或类似的形式看到它。

我不明白前两行的流程以及它们如何生成这个有效的 slice 函数。

Function.prototype.call()MDN article 帮助我解决了这个问题。

我能回答的最简单的方式:

In javascript, a Function has a method called call. A function is an object, and all objects inherit methods and properties from their prototype.

所以你的例子 Array.prototype.slice.call(arguments) 显示你在切片函数上调用 call 方法。

你疑惑的代码中第二行:var slice = Function.prototype.call.bind(unboundSlice);显示属于Function原型的call方法

如果您仍然感到困惑,请查看 JavaScript Prototypes

1 函数是对象。

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."

换句话说,回到最简单的方式来回答这个问题:在javascript中,一个Function有一个方法叫做call.

至于理解 bind 的作用,this 文章中的 that = this.bind 示例有助于理解正在发生的事情。

如果这让您感到困惑,请确保您理解 context and scope

sliceArray.prototype 的 属性,并且它期望其 this 对象是 Array-like。您可以在没有自己的切片函数的类数组对象(具有长度 属性 并具有可以索引的属性)上使用它,如下所示:

Array.prototype.slice.call(arraylikething);

需要大量输入,所以我们可以创建一个函数来完成同样的事情:

var slice = function(arraylikething){
    return Array.prototype.slice.call(arraylikething);
};

JavaScript 提供 Function.prototype.bind 将函数绑定到指定的 this 对象。所以我们可以更容易地完成同样的事情:

var slice = Function.prototype.call.bind(Array.prototype.slice);

bind 创建一个新函数 returns call 的结果,其 this 对象设置为 Array.prototype.slice,与我们所做的相同手动在上面,相当于你的代码。

在第一行中,Array.prototype.slice(这是一个方法)只是通过unboundSlice引用。您本质上是 'extracting' 来自 Array.prototype.

的 slice 方法

第二行,Function.prototype.call也是一样,也是ALL函数的一个方法。 (它在 Function.prototype 中定义,并被所有函数继承)。

接下来,通过使用 .bind(unboundSlice),Call 函数的 this 值被绑定到对 Array.prototype.slice 的引用,这基本上导致与 Array.prototype.slice.call() 相同的结果,其中call 也有它的 this 绑定到 slice,因为它是它的一个方法,而且因为它被这样调用。

最后,通过var slice;

引用了绑定的调用方法

这里的总体思路是您可以在另一个上下文(全局范围)中使用数组方法(切片)的功能。

所以现在,当 call 已经是 slice 的方法时,您不再调用 call,而是将 slice 绑定为 this 的值 call 以实现相同的行为。

Chris Dillinger 的回答正确且内容丰富。但这是另一种思考方式。本质上,你被要求定义

Function.prototype.call.bind(Array.prototype.slice)

哪个可以这样看:

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`

唯一有点棘手的步骤是第 4 步,您必须意识到所有函数都从其原型链继承 call 方法,因此对它们调用 call 只是一种替代方法调用函数本身的方法。

tl;博士:

var slice = Function.prototype.call.bind(unboundSlice);

是简写法:

var slice = function(value, start, end) {
  return unboundSlice.call(value, start, end);
};

我们再考虑一下这一行:

Array.prototype.slice.call(arguments)

.slice是数组方法,提取数组的子集。它对 this 的值进行操作。 .call 是每个函数都有的方法,它可以让您设置函数执行的 this 值。因此,上面这行代码让我们将 slice 作为 arguments 的方法来执行,而不必改变 arguments 本身。我们可以做到

arguments.slice = Array.prototype.slice;
arguments.slice();

但这并不干净。

现在正在看

Function.prototype.call.bind(unboundSlice);

如前所述,.call 每个 函数都有的方法。它还对 this 进行操作,这应该是一个函数。它调用 this 并将该函数的 this 值设置为第一个参数。您可以认为 call 类似于

function call(thisValue, arg1, arg2, ...) {
   return this.apply(thisValue, [arg1, arg2, ...]);
}

注意它如何将 this 作为函数调用。

.bind也是每个函数都有的方法。它 returns 一个新函数,其 this 值固定为您传入的第一个参数。

让我们考虑一下 call.bind(unboundSlice) 的结果函数是什么样的:

function boundCall(thisValue, arg1, arg2, ...) {
   return unboundSlice.apply(thisValue, [arg1, arg2, ...]);
}

我们只是用unboundSlice替换了thisboundCall 现在总是调用 unboundSlice.