了解 Function.call.bind - 循序渐进

Understanding Function.call.bind - step-by-step

本文关键字:循序渐进 bind call Function 了解      更新时间:2023-09-26

一切都从这个问题开始

然后是@MinusFour的回答

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

我想了解,引擎盖下发生了什么,我的好奇心因此提出了这个问题。

实现什么?理解"Function.call.bind"。

分步方法相同

从 MDN 开始

注意:我在这里使用NodeJS的

1)

var adder = new Function('a', 'b', 'return a + b');
console.log(adder(2, 6));

**输出**

8

这是意料之中的,没什么好看

2)

这是我们的最终目标,从有界调用函数myFunc功能 ( 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())

输出

function anonymous() { function myFunc(a, b) {   console.log(a + b); } }

意料之中! 上面的代码什么都不做,因为我叫"匿名",它什么也没做。

4)

var adder = Function.call(myFunc);
console.log(adder.toString())

输出

function anonymous() {
}

预期!。 '.call'调用'Function''this'设置为'myFunc',没有任何参数或函数体。所以一个空的匿名函数是输出。现在,我可以"var adder = Function.call(myFunc,myFunc);"从步骤3创建相同的函数

目前为止,一切都好

5)

var adder = Function.call.bind(myFunc);
console.log(adder.toString())   
adder(2,6); 

输出

function () { [native code] }
1 6 undefined NaN

这里第一个参数没有传递给'myFunc'函数。这被视为函数'adder'(有界Function.call)的'this'

现在我

明白了(或者我误解了?)直到现在,但是那时下面的代码是如何工作的?

var slice = Function.call.bind(Array.prototype.slice);
function fn(){
    var arr = slice(arguments);
}

在我的情况下,adder 的第一个参数被丢弃(或者Function.call认为它是'this'的),上面的slice也应该发生同样的情况,对吗?

无论如何,我想记录下来以供参考

怕你走错了方向。这一行:

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

从不呼叫Function,也从不安排稍后调用。Function唯一用于那里的是它的call属性。 Function可能是ObjectDateRegExp或任何其他功能,或者可能已被Function.prototype;无所谓。 Function.prototype会更直接,可能更少令人困惑。

这有点难以解释,因为它涉及处理this的两层,其中this在不同时间是不同的事情:

call函数调用具有特定this值的函数,该值是作为其第一个参数的,并传递您为其提供的任何其他参数。例如:

function foo(arg) {
    console.log("this.name = " + this.name + ", arg = " + arg);
}
var obj = {name: "bar"};
foo.call(obj, "glarb"); // "this.name = bar, arg = glarb"

在那里,因为我们在foo上打电话给callcall打电话给foothis设置为obj并传递"glarb"

call根据call调用期间this知道它应该调用什么函数。 foo.call设置this callfoo。这可能会令人困惑,所以让我们绘制一下:

  • foo.call(obj, "glarb")呼叫call
    • call看到了this = foo,论点obj"glarb"
    • call调用thisfoo):
      • foo看到this = obj和单个参数"glarb"

关于slice,你通常会看到call使用它来从类似于数组的东西创建一个数组,而这个数组并不是一个真正的数组:

var divArray = Array.prototype.slice.call(document.querySelectorAll("div"));

var divArray = [].slice.call(document.querySelectorAll("div"));

在那里,我们调用callthis设置为 Array.prototype.slice(或 [].slice ,这是相同的函数),并将 querySelectorAll 返回的集合作为第一个参数传入。 call调用它认为this的函数,使用它的第一个参数作为该调用的this,并传递任何其他参数。

所以这是第一层this的东西。

bind 是函数具有的另一个函数,它与call相似但不同:call使用给定的this和参数调用目标函数,bind创建并返回一个新函数,如果您调用它,该函数将执行此操作。回到我们foo的例子:

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"

这称为事物(obj"glarb"参数)绑定到foo

call不同,由于bind创建了一个新函数,我们可以稍后添加参数:

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"

好了,现在我们有了所有的工作部分。那么代码中发生了什么?让我们将其分成几部分:

// 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在你的问题中只是被称为slice,但我使用callBoundToSlice以避免混淆。将rawSlice绑定到callthis处理的第一层,它决定了call将看到什么this。呼叫callBoundToSlice将呼叫callthis设置为 rawSlice 。然后call将调用它视为thisrawSlice)的函数,使用其第一个参数作为调用期间this的值(this处理的第二层)并传递任何进一步的参数。

因此,我们的forEach包含来自querySelectorAll的集合现在看起来像这样:

callBoundToSlice(document.querySelectorAll("div")).forEach(function(div) {
    // Each div here
});

这会将querySelectorAll返回的集合传递到callBoundToSlice, 调用 call this作为rawSlice,调用Array.prototype.slice集合设置thisArray.prototype.slice使用this来复制数组。


综上所述,使用 slice 将类似数组的对象转换为真正的数组有点过时了。ES2015 引入了 Array.from 方法,该方法可以在还没有它的 JavaScript 引擎上进行填充/填充:

var divArray = Array.from(document.querySelectorAll("div"));