、apply() 与 bind() 完整指南)
教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载Function.prototype.call()、Function.prototype.apply()与Function.prototype.bind()是 JavaScript 中用于控制函数执行上下文this与参数传递的三大核心方法。本篇指南源自 30 seconds of code 仓库的 function-call-apply-bind.md 一文将系统讲解三者的区别、各自的调用语法与适用场景并结合仓库内 this.md、call-function-once.md、partially-apply-function.md 等关联文档给出可复制的实战工具函数。读完本文你将彻底分清三者差异并掌握绑定对象方法、统一绑定所有方法、指定函数上下文等高频开发技巧。函数方法理解this与一等公民函数在 JavaScript 中函数是一等公民对象first-class objects它们可以像其他任何对象一样被传递、赋值、作为参数传入其他函数也可以拥有自己的属性和方法。正因如此Function对象才内置了call()、apply()与bind()等方法它们的共同职责是设置函数调用时的this值并传递参数。要真正用好这三个方法首先需要理解this的取值规则。仓库中的 this.md 给出了简明总结默认情况下this指向全局对象非严格模式下函数内的this指向全局对象严格模式下函数内的this为undefined箭头函数中的this保留其外层词法上下文的this作为对象方法调用时this指向调用该方法的对象构造函数调用时this绑定到正在构造的新对象事件处理器中this绑定到挂载监听器的元素。由此可以看出函数内部this的值并不是定义时决定的而是取决于调用方式。call()、apply()与bind()正是让你可以显式指定这个调用上下文的手段。该主题在仓库中属于核心函数概念之一function.yaml 将其归类为 JavaScript 函数文章集合且this相关文章同时被收录在 interviews.yaml 的 JavaScript 面试题集合中是面试与日常开发的高频考点。Function.prototype.call()逐个传入参数Function.prototype.call()用于以给定的this上下文调用函数参数需要**逐个individually**传入。function printThisAndData(...data) { console.log(this.data, ...data); } const obj { data: 0 }; const data [1, 2, 3]; printThisAndData.call(obj, data); // logs: 0 [1, 2, 3] printThisAndData.call(obj, ...data); // logs: 0 1 2 3关键点call()的第一个参数是this要绑定到的对象后续参数以逗号分隔、逐一展开的方式传入被调用函数上例中data是一个数组call(obj, data)会把整个数组作为单个参数传递因此函数内部console.log输出0 [1, 2, 3]this.data为0...data展开后只剩data这一个数组参数若改用展开语法call(obj, ...data)数组被展开为1、2、3三个独立参数输出则为0 1 2 3。Function.prototype.apply()以数组形式传入参数Function.prototype.apply()与call()几乎完全相同——同样以给定this上下文调用函数——唯一的区别在于参数必须以数组array形式提供。function printThisAndData(...data) { console.log(this.data, ...data); } const obj { data: 0 }; const data [1, 2, 3]; printThisAndData.apply(obj, data); // logs: 0 1 2 3 printThisAndData.apply(obj, ...data); // Throws a TypeError关键点apply()的第二个参数必须是可迭代/类数组对象通常就是一个数组apply(obj, data)会把data数组中的元素1、2、3逐一展开为被调用函数的参数输出0 1 2 3如果尝试把数组再次展开传给apply()如apply(obj, ...data)会抛出TypeError因为apply()期望收到的是数组本身而不是分散的多个参数。什么时候选apply()需要以数组形式批量传参时apply()最方便。例如仓库 call-function-once.md 中的once()工具就利用apply()把事件处理器的参数数组透传给原函数同时保留其this上下文const once fn { let called false; return function(...args) { if (called) return; called true; return fn.apply(this, args); }; }; const startApp function(event) { console.log(this, event); // document.body, MouseEvent }; document.body.addEventListener(click, once(startApp)); // only runs startApp once upon click这里必须用function关键字而非箭头函数才能让闭包内函数的this跟随调用者再通过fn.apply(this, args)将事件监听器的this这里是document.body和事件对象一并透传给startApp。这正是apply()在真实 DOM 场景中的典型用法。Function.prototype.bind()返回绑定好this的新函数bind()与前两者的差异最大它不会立即调用函数而是返回一个新函数新函数的this被永久绑定到传入的上下文对象同时预先传入的参数会被**前置prepended**到最终调用时的参数列表之前。function printThisAndData(...data) { console.log(this.data, ...data); } const obj { data: 0 }; const data [1, 2, 3]; const printObjAndData printThisAndData.bind(obj); printObjAndData(data); // logs: 0 [1, 2, 3] printObjAndData(...data); // logs: 0 1 2 3 const printObjTwoAndData printThisAndData.bind(obj, 2); printObjTwoAndData(data); // logs: 0 2 [1, 2, 3] printObjTwoAndData(...data); // logs: 0 2 1 2 3关键点bind()返回的是新函数原函数不受影响已绑定函数的this不可再被改写——即使之后再用call()/apply()也无法覆盖bind(obj, 2)中的2被永久前置调用printObjTwoAndData(data)时实际参数顺序是2, data数组作为整体展开调用时则是2, 1, 2, 3这也是部分应用partial application的一种原生实现先固定一部分参数生成一个参数更少的新函数。仓库中的 this.md 在 Bindingthis 一节同样印证了这一行为——bind()让this永久绑定到其第一个参数function f() { return this.foo; } const x f.bind({foo: hello}); console.log(x()); // hello而call()/apply()的绑定仅对本次调用生效function f() { return this.foo; } console.log(f.call({foo: hi})); // hi实战一将对象指定键上的方法绑定到对象你可以用Function.prototype.apply()创建一个工具函数让它调用对象**指定键key**上的方法并可选地在参数前追加额外的参数。这正是仓库 bindKey 片段 的实现思路const bindKey (context, fn, ...boundArgs) (...args) context[fn].apply(context, [...boundArgs, ...args]); const freddy { user: fred, greet: function(greeting, punctuation) { return greeting this.user punctuation; } }; const freddyBound bindKey(freddy, greet); console.log(freddyBound(hi, !)); // hi fred!要点拆解bindKey接收三个部分目标对象context、方法名字符串fn、以及预置参数boundArgs返回的新函数在调用时通过context[fn]取出方法再调用context[fn].apply(context, [...boundArgs, ...args])从而保证方法内的this指向context参数顺序为预置参数在前、新参数在后这与bind()的前置行为一致但完全用apply()实现可读性更强。实战二绑定对象的所有方法同理你可以把对象的全部方法一次性绑定到对象自身并覆盖overwrite原方法。技巧与上面相同只不过需要用Array.prototype.forEach()遍历给定的方法列表const bindAll (obj, ...fns) fns.forEach( fn ( (f obj[fn]), (obj[fn] function() { return f.apply(obj); }) ) ); let view { label: docs, click: function() { console.log(clicked this.label); } }; bindAll(view, click); document.body.addEventListener(click, view.click); // Log clicked docs when clicked.要点拆解bindAll接收对象和一组方法名用forEach逐个处理先将原方法引用保存到临时变量f再用一个新函数覆盖obj[fn]新函数内部通过f.apply(obj)保证无论方法以何种方式被调用例如作为事件监听器直接传入其this都稳定指向原对象这是解决从对象中取出方法后再调用this丢失问题的经典方案尤其适用于将对象方法直接挂载为 DOM 事件监听器的场景。实战三为任意函数指定执行上下文你也可以创建这样的工具让任意函数在指定上下文中执行并可选地前置额外参数。实现仍然依赖Function.prototype.apply()const bind (fn, context, ...boundArgs) (...args) fn.apply(context, [...boundArgs, ...args]); function greet(greeting, punctuation) { return greeting this.user punctuation; } const freddy { user: fred }; const freddyBound bind(greet, freddy); console.log(freddyBound(hi, !)); // hi fred!要点拆解与bindKey的区别在于这里直接传入函数本身而非对象 方法名适用面更广fn.apply(context, [...boundArgs, ...args])与bindKey内部逻辑完全同构只是上下文来源不同该模式与仓库 partially-apply-function.md 中讨论的参数前置/后置思路一脉相承——那里用fn(...partials, ...args)前置参数、fn(...args, ...partials)后置参数而这里的bind在固定上下文的同时也完成了参数前置。三者对比与选型建议方法调用时机this绑定参数形式返回值能否再次改写thiscall()立即调用本次调用绑定逐个传入函数执行结果每次调用可不同apply()立即调用本次调用绑定数组/类数组函数执行结果每次调用可不同bind()返回新函数永久绑定预置参数前置可搭配后续参数新函数不能已永久锁定选型速记参数数量已知且较少 →call()参数本身就是数组或想直接透传参数列表→apply()需要把函数打包后延迟调用、传给事件监听器或回调且要求this稳定 →bind()。结语与延伸阅读call()、apply()与bind()是 JavaScript 函数体系的基石方法。本指南在 30 seconds of code 仓库原始文档的基础上结合 this.md 中的this取值规则、call-function-once.md 中的真实事件监听器应用、以及 partially-apply-function.md 中的部分应用思想构成了完整的学习闭环。继续深入可参考仓库内的相关文档this.md系统理解this在全局、函数、对象方法、箭头函数、事件处理器中的不同取值call-function-once.mdapply()透传this与参数的实战partially-apply-function.md参数前置/后置的部分应用技术arrow-functions.md理解箭头函数为何不能也不应被call/apply/bind改变thisinterviews.yaml上述主题均被收录于 JavaScript 面试题集合适合考前复盘。赞分享教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载相关推荐30-seconds-of-code 详解用 JavaScript 实现防抖函数Debounce30 seconds of code 详解用 JavaScript 实现防抖函数Debounce 防抖Debouncing 是前端开发中限制函数调用频教程文档opencodex WebSocket 生命周期显式化策略Bun idleTimeout 常量、服务端常量与回归测试实战opencodex WebSocket 生命周期显式化策略Bun idleTimeout 常量、服务端常量与回归测试实战 opencodex 作为面向 Ope教程文档30-seconds-of-interviews 系列深入理解 JavaScript 函数式编程范式30 seconds of interviews 系列深入理解 JavaScript 函数式编程范式 导读 本文以 30 seconds of intervi教程前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考