关于 JavaScript 中的 apply、call 和 bind 的使用区别

JavaScript 中的applycallbind都是用来动态改变函数运行时上下文的。下面分别对它们进行详细讲解。

apply

apply()是 Function 对象自带的方法,用于修改函数调用时this指向的对象,同时还可以将函数的参数以数组形式传递给它。

1
2
3
4
5
6
7
8
function foo() {
console.log(this.a);
console.log(arguments);
}

const obj = { a: 1 };

foo.apply(obj, [2, 3]); // 输出:1 [2, 3]

在上述例子中,我们在foo函数的原本调用:foo() 中没有显式地指明它的this值。但是,在调用foo.apply(obj, [2, 3])时,foo()方法就像被附加到了obj后一样,this.a被正确地设置为了 1。上例中的第二个参数就是foo需要的实参。

apply()方法的另一个常见用法就是借用函数:

1
2
3
4
5
6
7
8
9
function add(a, b) {
return a + b;
}

function substract(a, b) {
return a - b;
}

console.log(add.apply(substract, [3, 1])); // 输出:4

这里我们使用apply方法把add函数中的代码应用到了substract函数的作用域中,并且把它的参数传递进去。

在类数组对象(比如 arguments)中使用apply方法就可以轻松地将参数传入一个函数:

1
2
3
4
5
6
7
function func() {
Array.prototype.forEach.call(arguments, function (arg) {
console.log(arg);
});
}

func("Hello", "world", "!"); // 输出:'Hello' 'world' '!'

上例中,我们通过使用apply()方法,将arguments对象作为数组使用,并传递给了Array.prototype.forEach()方法。这样我们就可以在arguments对象上调用forEach()方法了。

call

call()apply()用法相同,只是它接受的参数必须直接列出来(即不是以数组的形式传递),这使得它略微快一些。用法如下:

1
2
3
4
5
6
7
function foo(something) {
console.log(this.a, something);
}

const obj = { a: 1 };

foo.call(obj, 2); // 输出:1 2

以上代码实现的跟apply很类似,只是提供参数的方式略有不同。当您需要将参数表示为一个连续序列而不是数组时,这种方式会更加方便。

bind

bind()方法与apply()call()方法不同,它并不会立即执行目标函数。相反,bind()方法返回一个新函数,且它的 this 值被永久固定在了绑定到的对象上。于是绑定后的函数可以像原始函数一样被传递和调用。

1
2
3
4
5
6
7
8
9
10
11
function f(something) {
console.log(this.a, something);
return this.a + something;
}

const obj = { a: 1 };
const fn = f.bind(obj);

fn(2); // 输出:1 2

fn(); // 输出:1 undefined

这里的this.a值被永久绑定到了obj对象上。

apply、call 和 bind 的使用场景

以下是在实际情况中的一些应用场景:

  • apply通常用于不知道函数参数数量时,以数组形式传递参数。而对于已知参数数量的函数,则可以直接使用call()
  • bind()一般用来固定一个函数中需要用到的上下文(比如this)变量。它在类成员函数定义时非常有用,也可以用作事件处理函数。