JavaScript 中 call、apply、bind 的区别

先理解核心:它们都是干啥的?

想象你借了一本书(函数),但你想让别人(指定对象)来读这本书的某个章节(执行函数)。

  • 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 用得最少。但面试爱问区别,所以三个都得懂。

相关推荐
dialect_1 小时前
KMP算法
开发语言·c++·算法
jktantan1 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
前端·javascript·vue.js
总要学点什么2 小时前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
2601_962885722 小时前
quotes 字段全解:涨跌幅、振幅、换手率的口径与单位
java·前端·spring
excel3 小时前
encodeURI 和 encodeURIComponent 有什么区别?
前端
前端精髓3 小时前
调用openrouter的免费API模型
java·前端·javascript
赛博守夜人3 小时前
AI安全攻防与治理之十四:AI 安全测试不能脱离传统安全,Web、API、移动端与模型攻击面的组合拳
前端·人工智能·安全
开开心心_Every3 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
chenbingjie_c3 小时前
C++ 多态完全指南:从虚函数重写到虚函数表原理
开发语言·c++
吴卫斌3 小时前
定投ETF实战指南(四):行业/主题ETF——定投的“进攻端“
开发语言·python·股票·量化