青训营X豆包MarsCode 对this指向和call、bind使用的理解

89 阅读6分钟

this 对call apply bind 的理解

能为我详细解析一下这段代码吗

这段代码是使用 JavaScript 实现的一个通用的 slice 函数,可以对任何具有 length 属性并且元素可以通过索引访问的对象使用 slice 操作。

让我们逐步解析这段代码:

var slice = Function.prototype.call.bind(Array.prototype.slice);
  1. Array.prototype.sliceslice 是 JavaScript 数组的一个方法。Array.prototype.slice 是该方法的引用,可以用来从数组或类数组对象中提取一部分元素,形成一个新的数组。
  2. Function.prototype.callcall 是所有函数都可以调用的一个方法。它的作用是以指定的 this 值和参数来调用一个函数。例如,func.call(thisArg, arg1, arg2, ...) 会将 functhis 绑定到 thisArg,并传入 arg1, arg2 作为参数调用该函数。
  3. Function.prototype.call.bind(Array.prototype.slice)bind 是一个方法,用来创建一个新的函数,该函数的 this 永远指向给定的值。在这里,Function.prototype.call.bind(Array.prototype.slice) 的作用是创建一个新的函数,这个新函数在调用时会将 Array.prototype.slice 作为 callthis 值。
  4. var slice = Function.prototype.call.bind(Array.prototype.slice); : 最终,这段代码会生成一个函数 slice,它可以接受任何类数组对象,并在上面调用 Array.prototype.slice。这样,slice 就可以用于字符串、arguments 对象,甚至其他类数组的对象。

使用示例

function example() {
    var args = slice(arguments, 1, 3);
    console.log(args);
}
​
example(0, 1, 2, 3); // 输出: [1, 2]

在这个例子中,slice(arguments, 1, 3)arguments 对象从索引 13 截取出来(不包含索引 3),返回 [1, 2]

我对实现还是有点不太明白,新的slice函数相当于什么

理解这段代码后,可以把新的 slice 函数等价地理解成一个通用的 Array.prototype.slice 调用,它允许对任何具有 length 属性的类数组对象进行切片操作,而不仅限于数组。

下面是一个更简单的方式来理解新的 slice 函数:


var slice = function(arrayLike, start, end) {
    return Array.prototype.slice.call(arrayLike, start, end);
};

通过这段代码,slice 函数就相当于 Array.prototype.slice.call 的封装,它接受三个参数:

  1. arrayLike:需要进行切片的类数组对象,比如 arguments 对象或 NodeList 等。
  2. start:切片的起始索引。
  3. end:切片的结束索引(不包括此索引)。

你可以把新的 slice 函数看成是 Array.prototype.slice.call 的一种简化写法。通过 bind,我们将 Array.prototype.slice 函数绑定到了 Function.prototype.call,这样只要调用 slice(arrayLike, start, end),就等同于 Array.prototype.slice.call(arrayLike, start, end)。这个技巧让 slice 函数可以像普通函数一样直接使用,不再需要手动调用 call

具体例子

假设有一个 arguments 对象,通常不能直接调用 arguments.slice(),因为 arguments 不是一个真正的数组,而是一个类数组对象:


function example() {
    var args = slice(arguments, 1, 3); // 使用新的 slice 函数
    console.log(args); // 输出: [1, 2]
}
​
example(0, 1, 2, 3);

在这个例子中,slice(arguments, 1, 3) 等效于 Array.prototype.slice.call(arguments, 1, 3)。新的 slice 函数为类数组对象提供了与数组相同的切片操作能力。

总结

新的 slice 函数相当于:


function slice(arrayLike, start, end) {
    return Array.prototype.slice.call(arrayLike, start, end);
}

这样,你就可以方便地对任何类数组对象使用切片操作,而不用每次都写 Array.prototype.slice.call(...)

能解释下bind为什么能达到这种效果吗

bind 在这里的作用是让我们创建了一个新的函数,并提前指定了该函数的 this 指向。这是因为 Function.prototype.call.bind(Array.prototype.slice) 的写法让 Array.prototype.slicethis 值绑定到了 Function.prototype.call 上,使得最终生成的 slice 函数可以直接对类数组对象调用 Array.prototype.slice。我们来深入理解这个过程。

关键部分拆解

让我们逐步拆解 Function.prototype.call.bind(Array.prototype.slice)

  1. Function.prototype.callcall 是所有函数对象的一个方法,它的作用是显式地指定 this,然后调用该函数。例如:

    
    func.call(thisArg, arg1, arg2, ...);
    

    这段代码会把 func 中的 this 设置为 thisArg,并传入 arg1, arg2 等参数。

  2. bind 方法bind 是 JavaScript 中的一个方法,可以创建一个新函数,并把 this 永久绑定到某个指定的对象上。它的语法如下:

    
    var newFunc = originalFunc.bind(thisArg, arg1, arg2, ...);
    

    使用 bind 后,newFunc 会在调用时把 this 设为 thisArg,并固定传入的参数为 arg1, arg2

  3. 应用 bindFunction.prototype.call:在 Function.prototype.call.bind(Array.prototype.slice) 中,我们用 bind 创建了一个新的函数,这个新函数的 this 永远绑定到了 Array.prototype.slice 上。

生成的 slice 函数的作用

Function.prototype.call.bind(Array.prototype.slice) 生成新的 slice 函数后,slice 实际上是一个预设了 thisArray.prototype.slice 的函数。调用 slice(arrayLike, start, end) 时,等价于执行:


Array.prototype.slice.call(arrayLike, start, end);

具体过程如下:

  • Function.prototype.call 的作用是显式地指定 this 并调用某个函数。
  • bindcallthis 绑定为 Array.prototype.slice,即 slice 函数内部会始终把 Array.prototype.slice 当作目标函数。
  • 当你调用 slice(arrayLike, start, end),这相当于调用 Array.prototype.slice.call(arrayLike, start, end),从而对 arrayLike 执行切片操作。

为什么 bind 能达到这种效果?

bind 能达到这种效果是因为它会创建一个新的函数,并将新函数的 this 绑定到指定的对象(这里是 Array.prototype.slice)。之后,每次调用这个新函数时,都会用这个固定的 this 值(即 Array.prototype.slice)。

这样,我们得到了一个通用的 slice 函数,可以直接作用于任何类数组对象,而不必每次都调用 Array.prototype.slice.call(...)

代码对比

bind 后的效果:


var slice = Function.prototype.call.bind(Array.prototype.slice);
slice(arguments, 1, 3); // 相当于 Array.prototype.slice.call(arguments, 1, 3);

与手动调用 call 的效果等价:


Array.prototype.slice.call(arguments, 1, 3);

总结

通过 bind,我们把 Function.prototype.callthis 绑定到 Array.prototype.slice 上,创建了一个简化版的 slice 函数,可以直接对类数组对象调用 Array.prototype.slice

这段代码中call的作用是什么:

在这段代码中,call 的作用是Array.prototype.slice 可以直接作用于类数组对象,而不局限于数组对象本身。具体来说,call 允许我们在执行 Array.prototype.slice 时,显式地指定 this,使得我们可以把任意类数组对象当作 this 传入 slice 方法。

背景

Array.prototype.slice 方法通常只能用于数组,因为它的 this 默认是一个数组对象。但是,在 JavaScript 中,有一些“类数组”对象(如 arguments 对象、NodeList 等)也具有 length 属性和索引元素。如果想对这些类数组对象使用 Array.prototype.slice,我们需要一种方式来指定 this

call 的作用

call 是 JavaScript 的一个内置方法,它允许我们显式地指定函数的 this,并立即执行该函数。例如:


Array.prototype.slice.call(arrayLike, start, end);

在这行代码中,call

  1. Array.prototype.slicethis 指定为 arrayLike 对象,使得 slicearrayLike 上执行,而不是在某个数组上。
  2. arrayLike 可以是任何具有 length 属性和索引的类数组对象,如 argumentsNodeList 或其他手动创建的类数组对象。

整体代码的工作方式

通过 Function.prototype.call.bind(Array.prototype.slice),我们生成了一个新的函数 slice。这个 slice 函数的效果是把传入的第一个参数作为 this 来调用 Array.prototype.slice,从而可以对任何类数组对象使用 slice 功能。

示例

假设我们有以下代码:


function example() {
    var args = slice(arguments, 1, 3); // 使用新的 slice 函数
    console.log(args);
}
​
example(0, 1, 2, 3); // 输出: [1, 2]

slice(arguments, 1, 3) 中:

  • arguments 被作为 this 传递给 Array.prototype.slice,因此 slice 方法可以在 arguments 对象上操作,提取索引 13 之间的元素。
  • call 的作用是允许 Array.prototype.slicearguments 当作 this 进行处理,实现类数组对象的切片操作。

总结

在这段代码中,call 的主要作用是将类数组对象 arrayLike 指定为 Array.prototype.slicethis,从而让 slice 方法可以作用于任意类数组对象,实现通用的切片功能。