聊一聊如何更改函数执行时的上下文(bind, call, apply)

199 阅读3分钟

前言

JavaScript 中的函数是一个对象,它可以有自己的属性和方法,也可以通过调用来执行一段代码。在调用函数时,函数的上下文即 this 值会被自动设置为函数的调用者。但是,在某些情况下,我们希望能够手动改变函数的上下文,这时候就可以使用 callapplybind 方法。

如何使用 & 区别

call, apply, bind 都可以用来更改函数执行时的上下文对象(即 this 的值)。它们的区别在于传递参数的方式和返回结果的不同。

callapply 用法类似,第一个参数是要指定的上下文对象,之后的参数是传递给函数的参数列表。它们的区别在于传递参数的方式,call 是依次列出参数,apply 则是把参数放在数组中传递。

bind 则是返回一个新的函数,把指定的上下文对象和参数绑定到原函数上,这个新函数可以在后面任意时刻调用,调用时 this 的值和绑定的上下文对象一致。

举个例子

// call 示例
const person = {
  name: 'Allen',
  greet: function (age) {
    console.log(`Hello, my name is ${this.name} and I'm ${age} years old.`);
  }
};

const cat = {
  name: 'damao'
};

person.greet.call(cat, 5); // 输出 "Hello, my name is damao and I'm 5 years old."

// apply 示例
const dog = {
  name: 'wong'
};

person.greet.apply(dog, [7]); // 输出 "Hello, my name is wong and I'm 7 years old."

// bind 示例
const boundFunction = person.greet.bind(cat, 3);
boundFunction(); // 输出 "Hello, my name is damao and I'm 3 years old."

在这个例子中,person 对象有一个 greet 方法,用来输出自我介绍的语句。callapply 都是在 catdog 对象上调用 greet 方法,以修改 this 的值。而 bind 则是将 cat 对象和一个 age 参数绑定到 person.greet 上,生成一个新的函数 boundFunction,随后调用该函数以输出相应的语句。

何时使用

  1. 需要显式地绑定this对象;(文中已经有示例了)
  2. 在将函数作为参数传递给另一个函数时,需要将上下文从一个对象更改为另一个对象;
    function printAge() {
      console.log(this.age);
    }
    
    const person = {
      name: 'Allen',
      age: 30
    };
    
    const anotherPerson = {
      name: 'Celia',
      age: 25
    };
    
    function callAndPrintAge(fn) {
      fn.call(this);
    }
    
    callAndPrintAge.call(anotherPerson, printAge); // 25
    
    在这个例子中,我们有两个对象:personanotherPerson,以及一个printAge函数。我们创建了一个名为callAndPrintAge的函数,它接受一个函数作为参数,并将它的上下文设置为第一个参数(使用call方法)。我们调用callAndPrintAge,并将它的上下文设置为anotherPerson对象,以便我们可以从另一个对象中打印年龄。
  3. 当需要调用一个类似数组的对象上的函数时,可以使用apply方法来避免编写重复的代码。
    const arrayLikeObject = {
      0: 'foo',
      1: 'bar',
      2: 'baz',
      length: 3
    };
    
    Array.prototype.join.apply(arrayLikeObject, ['|']); // 'foo|bar|baz'
    
    这个例子中,我们有一个类似数组的对象,它有3个属性:0、1和2,以及一个长度为3的length属性。我们使用apply()方法在类似数组的对象上调用数组的join()方法,将参数作为数组传递。由于我们不能直接调用类似数组的对象上的join()方法,所以我们使用apply方法来绕过这个问题,这样我们就可以像在数组上一样调用这个方法了。

参考资料

Understanding Bind, Call and Apply in JavaScript - Webtips