返回

this 在 JavaScript 中的指向(三)

前端

在 JavaScript 的世界里,this 就像一个变色龙,它会根据上下文环境改变自身的指向,这给开发者带来了不少挑战。为了驯服这只“变色龙”,JavaScript 提供了多种绑定 this 的方法,其中一种就是显式绑定,它允许开发者直接掌控 this 的指向,不再受制于上下文环境的变化。

显式绑定主要通过 call()apply()bind() 这三个函数来实现。它们就像三个工具,帮助开发者将 this 牢牢地绑定到指定的对象上。

call() 和 apply()

这两个函数的功能非常相似,都是用来调用函数并改变函数内部 this 的指向。它们的区别在于传参方式的不同。call() 方法接收函数的参数列表,而 apply() 方法接收一个包含所有参数的数组。

举个例子,假设我们有一个 greet() 函数,它会输出一句问候语,其中包含了 this.name

function greet(greeting) {
  console.log(greeting + ', my name is ' + this.name);
}

现在,我们有两个对象 person1person2,它们都有 name 属性:

const person1 = { name: 'John' };
const person2 = { name: 'Jane' };

如果我们直接调用 greet() 函数,this 会指向全局对象(在浏览器环境中是 window 对象),输出的结果会是 undefined

greet('Hello'); // Hello, my name is undefined

但是,如果我们使用 call()apply() 方法,就可以将 this 绑定到 person1person2 上:

greet.call(person1, 'Hello'); // Hello, my name is John
greet.apply(person2, ['Hi']); // Hi, my name is Jane

bind()

bind() 方法与 call()apply() 不同,它并不会立即调用函数,而是返回一个新的函数,这个新函数的 this 值被永久地绑定到了指定的对象上。

例如,我们可以使用 bind() 方法创建一个新的函数 greetJohn,它的 this 值永远指向 person1

const greetJohn = greet.bind(person1);
greetJohn('Hello'); // Hello, my name is John

显式绑定的应用场景

显式绑定在很多场景下都非常有用,例如:

  • 在事件处理函数中,确保 this 指向正确的元素。
  • 在回调函数中,确保 this 指向正确的对象。
  • 创建一个函数的多个副本,每个副本都有不同的 this 值。

箭头函数与显式绑定

需要注意的是,箭头函数的 this 值是在定义时就被确定了,它不会被 call()apply()bind() 方法改变。箭头函数的 this 值指向它所在词法作用域中的 this 值。

例如:

const person = {
  name: 'John',
  greet: () => {
    console.log('Hello, my name is ' + this.name);
  }
};

person.greet(); // Hello, my name is undefined 

在这个例子中,greet() 函数是一个箭头函数,它的 this 值指向全局对象,而不是 person 对象。

总结

显式绑定是 JavaScript 中控制 this 指向的一种重要方法,它可以帮助开发者避免很多与 this 相关的问题。call()apply()bind() 方法是实现显式绑定的三种工具,它们各有特点,开发者可以根据实际情况选择合适的方法。

常见问题解答

1. call()apply() 的区别是什么?

call()apply() 的主要区别在于传参方式。call() 方法接收函数的参数列表,而 apply() 方法接收一个包含所有参数的数组。

2. bind() 方法有什么作用?

bind() 方法返回一个新的函数,这个新函数的 this 值被永久地绑定到了指定的对象上。

3. 箭头函数的 this 值是如何确定的?

箭头函数的 this 值是在定义时就被确定了,它不会被 call()apply()bind() 方法改变。箭头函数的 this 值指向它所在词法作用域中的 this 值。

4. 什么时候应该使用显式绑定?

在需要明确控制 this 指向的场景下,例如在事件处理函数、回调函数或创建函数副本时,应该使用显式绑定。

5. 显式绑定有什么缺点?

显式绑定可能会使代码变得更复杂,可读性降低。而且,如果使用不当,可能会导致难以调试的错误。