call()、apply()、bind() 是 JavaScript 中 Function.prototype 上的三个非常重要且高频考查的方法,它们都能改变函数运行时的 this 指向,但在使用方式和效果上有明显区别。
| 特性 | call() | apply() | bind() |
|---|---|---|---|
| 作用 | 立即执行函数 + 改变 this | 立即执行函数 + 改变 this | 只改变 this,不立即执行,返回新函数 |
| 是否立即执行 | 是 | 是 | 否 |
| 参数传递方式 | 逐个参数(逗号分隔) | 数组(或类数组) | 逐个参数(支持分两次传参) |
| 第一个参数 | 要绑定的 this(可传 null/undefined) | 要绑定的 this(可传 null/undefined) | 要绑定的 this(可传 null/undefined) |
| 返回值 | 函数执行后的返回值 | 函数执行后的返回值 | 绑定了 this 的新函数 |
| 后续参数是否可追加 | 不可(一次性传完) | 不可(一次性传完) | 可以(bind 后还能继续传参) |
| 常用场景 | 借用方法、链式调用 | 处理类数组、Math.max.apply() | 事件处理、柯里化、固定 this |
代码对比(最直观的区别)
function say(greeting, punctuation) {
console.log(`${greeting}, 我是 ${this.name}${punctuation}`);
}
const person = { name: "小明" };
// 1. call —— 立即执行,参数逐个传入
say.call(person, "你好", "!");
// 你好, 我是 小明!
// 2. apply —— 立即执行,参数用数组
say.apply(person, ["早上好", "~~"]);
// 早上好, 我是 小明~~
// 3. bind —— 不执行,返回新函数
const sayHiToPerson = say.bind(person, "嘿");
// 可以先绑定部分参数(柯里化)
sayHiToPerson("!");
// 嘿, 我是 小明!
sayHiToPerson("呀~");
// 嘿, 我是 小明呀~
经典使用场景对比
| 场景 | 推荐使用 | 为什么选它 |
|---|---|---|
| 借用数组方法(push、slice等) | call / apply | 需要立即执行 |
| 求数组最大/最小值 | apply | Math.max.apply(null, arr)(老写法) |
| Math.max(…arr) 的旧浏览器兼容写法 | apply | 数组展开运算符出现之前最常用 |
| setTimeout / setInterval 固定 this | bind | 回调函数需要延迟执行 |
| React / Vue 事件绑定 | bind | 常用于 constructor 中提前绑定 this |
| 函数柯里化(partial application) | bind | 可以分步传参 |
| 快速借用 console.log.apply | apply | console.log.apply(console, arguments) |
口诀总结(面试背这个很快)
- call:打电话(Call)→ 参数像打电话一样一个个说,马上打
- apply:申请(Apply)→ 参数要打包成数组交上去,马上批
- bind:绑定(Bind)→ 只绑住 this 和部分参数,不马上用,留着以后再调用
希望这个对比够清晰!如果想看手写 call/apply/bind 的实现代码也可以告诉我~