基于本文回答
0
评论

JavaScript中call, apply, bind 的作用和区别

知识点图片

call, apply, bind都能改变this指向。callapply立即执行,仅参数形式不同。bind不立即执行,而是返回一个新函数。

我们来详细解析一下 JavaScript 中的 call, apply, 和 bind。这三者是 JS 中非常重要的概念,尤其在处理函数上下文 this 的指向时。

核心共同点:改变 this 指向

call, apply, bind 这三个函数都是 Function.prototype 上的方法,这意味着任何函数都可以调用它们。

它们最核心的作用都是一样的:改变函数执行时的 this 上下文

我们先从一个简单的例子开始,理解为什么需要改变 this

javascript
const person = {
  name: '张三',
  sayHello: function() {
    console.log(`你好,我是 ${this.name}`);
  }
};

const anotherPerson = {
  name: '李四'
};

person.sayHello(); // 输出: 你好,我是 张三

// 如果我们想让 anotherPerson 也“借用” sayHello 这个方法,直接调用会出问题
// const say = person.sayHello;
// say(); // 在非严格模式下,this 指向 window,输出 "你好,我是 "

call, apply, bind 就是为了解决这类问题而生的,它们可以显式地指定 sayHello 函数在执行时,内部的 this 应该指向谁。


1. call()

  • 作用:调用一个函数,并将其 this 值设置为提供的对象。同时,可以逐个传入参数。
  • 特点立即执行函数。
  • 语法function.call(thisArg, arg1, arg2, ...)

示例:

javascript
const person = {
  name: '张三'
};

function sayHello(greeting, punctuation) {
  console.log(`${greeting},我是 ${this.name}${punctuation}`);
}

// 使用 call 来调用 sayHello,并指定 this 为 person 对象
// 后面的参数 '你好' 和 '!' 会依次传递给 sayHello 函数
sayHello.call(person, '你好', '!'); // 输出: 你好,我是 张三!

2. apply()

  • 作用:调用一个函数,并将其 this 值设置为提供的对象。同时,参数必须以一个数组(或类数组对象)的形式传入。
  • 特点立即执行函数。
  • 语法function.apply(thisArg, [argsArray])

示例:

applycall 的作用几乎完全一样,唯一的区别就是传参的方式。

javascript
const person = {
  name: '张三'
};

function sayHello(greeting, punctuation) {
  console.log(`${greeting},我是 ${this.name}${punctuation}`);
}

// 使用 apply 来调用 sayHello,并指定 this 为 person 对象
// 后面的参数以数组形式 ['你好', '!'] 传递
sayHello.apply(person, ['你好', '!']); // 输出: 你好,我是 张三!

apply 的一个经典妙用:
利用 apply 参数是数组的特性,可以方便地操作数组,例如求数组中的最大/最小值。

javascript
const numbers = [5, 6, 2, 3, 7];

// Math.max 本身不接受数组作为参数,它接受的是一串数字 Math.max(arg1, arg2, ...)
// 使用 apply 可以巧妙地将数组展开并作为参数传入
const max = Math.max.apply(null, numbers); // null 表示 this 不重要
const min = Math.min.apply(null, numbers);

console.log(max); // 7
console.log(min); // 2
// (在 ES6 中,可以使用扩展运算符更方便地实现: Math.max(...numbers))

3. bind()

  • 作用创建一个新函数。这个新函数的 this 会被永久地绑定到 bind() 的第一个参数。bind() 也可以预先设置部分参数。
  • 特点不立即执行,而是返回一个绑定了 this 的新函数
  • 语法const newFunction = function.bind(thisArg, arg1, arg2, ...)

示例:

javascript
const person = {
  name: '张三'
};

function sayHello(greeting, punctuation) {
  console.log(`${greeting},我是 ${this.name}${punctuation}`);
}

// 使用 bind 创建一个新函数 greetZhangSan
// 这个新函数的 this 永远指向 person 对象
const greetZhangSan = sayHello.bind(person, '你好');

// bind 不会立即执行,我们需要手动调用返回的新函数
greetZhangSan('。'); // 输出: 你好,我是 张三。

const greetZhangSanAgain = sayHello.bind(person);
greetZhangSanAgain('嗨', '!'); // 输出: 嗨,我是 张三!

bind 在异步场景(如事件监听、setTimeout)中非常有用,因为这些场景下 this 很容易丢失。

bind 的经典应用场景:

javascript
class Logger {
  constructor() {
    this.count = 0;
  }

  logMessage() {
    // 这里的 this 期望指向 Logger 的实例
    console.log('点击次数:', ++this.count);
  }
}

const logger = new Logger();

// 错误的做法:
// 当 click 事件触发时,logMessage 内部的 this 会指向 button 元素本身,而不是 logger 实例
// document.querySelector('button').addEventListener('click', logger.logMessage); 
// 这会导致 this.count 是 undefined

// 正确的做法:
// 使用 bind 将 logMessage 的 this 永久绑定到 logger 实例上
const boundLogMessage = logger.logMessage.bind(logger);
document.querySelector('button').addEventListener('click', boundLogMessage);
// 每次点击按钮,都会正确输出: 点击次数: 1, 点击次数: 2, ...

核心区别总结

特性 call() apply() bind()
执行时机 立即执行 立即执行 返回新函数,不立即执行
参数传递 逐个传递参数 数组或类数组对象的形式传递 逐个传递参数(可以预设部分参数,也叫柯里化)
返回值 函数的执行结果 函数的执行结果 一个绑定了 this 和预设参数的新函数
核心记忆 Call for Comma (逗号分隔参数) Apply for Array (数组参数) Bind for Bound function (返回绑定函数)

总结

  1. 如果你想立即调用一个函数并改变其 this 指向,并且参数是逐个传入的,使用 call
  2. 如果你想立即调用一个函数并改变其 this 指向,并且参数是以数组形式传入的,使用 apply
  3. 如果你想创建一个新函数,使其 this 永久指向某个对象,并且稍后再调用它(例如用于回调函数、事件处理),使用 bind
右滑查看面试常问