先理解核心:它们都是干啥的?
想象你借了一本书(函数),但你想让别人(指定对象)来读这本书的某个章节(执行函数)。
-
call:现在就让人读,你一个个告诉他读哪几页
-
apply:现在就让人读,你把页数写在一张纸条上交给他
-
bind:不急着读,先告诉他要读哪几页,给他一张"读书券",以后随时可以让他读
我的"顿悟三连"
第一层困惑:它们好像都一样?
刚学的时候,看代码:
javascript
fn.call(obj, 1, 2, 3)
fn.apply(obj, [1, 2, 3])
fn.bind(obj, 1, 2, 3)()
内心OS:不就是参数写法不一样吗?搞这么复杂干嘛!
第二层顿悟:原来是"立即执行"和"稍后执行"的区别
直到有一次写事件监听:
javascript
// 错误示范 ❌
button.onclick = user.sayHello.call(user);
// 页面加载完立刻就执行了!不是我想要的
// 正确示范 ✅
button.onclick = user.sayHello.bind(user);
// 点击时才执行,完美!
这一刻我懂了:bind 是"发号施令"但不立即行动,call/apply 是"立刻执行"。
第三层顿悟:什么时候用 call,什么时候用 apply?
有次写代码找数组最大值:
javascript
javascript
// 用 apply 多优雅
Math.max.apply(null, [1, 2, 3, 4, 5]);
// 用 call 就尴尬了
Math.max.call(null, 1, 2, 3, 4, 5); // 也行,但参数多时累死
这一刻我悟了:
-
已有数组 → apply(直接把数组扔进去)
-
参数明确 → call(看得清楚,不用转数组)
终极对比表(我背了这一张就够了)
| 维度 | call | apply | bind |
|---|---|---|---|
| 执行时机 | 立即执行 | 立即执行 | 返回新函数,不执行 |
| 参数形式 | 逐个传入 a,b,c |
数组传入 [a,b,c] |
逐个传入,可分批传 |
| 返回值 | 函数结果 | 函数结果 | 新函数 |
| 经典场景 | 继承父类属性 | 找数组最大/小值 | 事件绑定、setTimeout |
| 记忆口诀 | C all = Comma(逗号) | A pply = Array(数组) | B ind = Back(稍后) |
面试官最爱问的"坑"
Q:bind 之后还能改 this 吗?
javascript
const obj = { name: 'obj' };
const obj2 = { name: 'obj2' };
function test() { console.log(this.name); }
const bound = test.bind(obj);
bound(); // 'obj'
bound.call(obj2); // 还是 'obj'!bind 绑定了就改不了了
Q:箭头函数能用 call/apply/bind 吗?
javascript
const arrow = () => { console.log(this); };
// 没用!箭头函数的 this 是定义时就定死的
arrow.call({ name: 'test' }); // this 还是原来的
Q:手写一个 bind 看看?
javascript
Function.prototype.myBind = function(context, ...bindArgs) {
const fn = this;
return function(...callArgs) {
return fn.apply(context, [...bindArgs, ...callArgs]);
};
};
我的最终心得
不要死记硬背,要理解设计意图:
-
call:适合参数数量固定,想写得直观时
-
apply:适合参数已经在数组里,或者数量不确定时
-
bind:适合不急着执行,或者要传给事件监听、定时器时
最常用的反而是 apply (处理数组)和 bind(处理 this 指向),call 用得最少。但面试爱问区别,所以三个都得懂。