)
在前端面试中「手动实现 bind 方法」是高频考察点 —— 它不仅能验证你对this绑定、函数原型、构造函数等核心知识点的理解还能体现代码的健壮性思维。本文会从 bind 的原生特性出发拆解手写 bind 的核心逻辑结合完整代码逐行解析让你彻底搞懂背后的原理。一、先搞懂原生 bind 到底做了什么在手写之前先明确原生Function.prototype.bind的核心特性这是我们手写的「对标标准」返回新函数bind 不会立即执行原函数而是返回一个绑定了this的新函数绑定 this 指向指定新函数执行时的this上下文且绑定后无法被 call/apply 修改支持预置参数柯里化调用 bind 时可传入部分参数新函数执行时可传入剩余参数兼容构造函数场景如果绑定后的函数被当作构造函数new 调用this 会指向实例而非 bind 传入的对象保留原型链绑定后的新函数能继承原函数的原型属性。举个原生 bind 的例子const person { name: 张三 }; function sayHi(age, gender) { console.log(我是${this.name}${age}岁${gender}); } // 绑定this预置参数age18 const bindSayHi sayHi.bind(person, 18); // 执行时传入剩余参数gender bindSayHi(男); // 输出我是张三18岁男二、手写 bind 完整代码带详细注释基于原生特性我们实现一个「功能对齐、鲁棒性强」的myBind方法先看完整代码Function.prototype.myBind function (obj, ...args) { // 1. 保存原函数this 指向调用 myBind 的原函数比如上面的 sayHi const fn this; // 2. 处理绑定对象null/undefined 时绑定到 window否则转为对象兼容基本类型 obj (obj ! null obj ! undefined) ? Object(obj) : window; // 3. 定义返回的新函数 F支持接收剩余参数 const F function (...rest) { // 4. 关键区分构造函数场景new 调用和普通调用 if (this instanceof F) { // new 调用时this 指向 F 的实例原函数作为构造函数执行 return new fn(...args, ...rest); } // 普通调用时用 call 绑定 this 到 obj合并预置参数和新参数 return fn.call(obj, ...args, ...rest); }; // 5. 修复原型链让新函数 F 继承原函数的原型 F.prototype Object.create(fn.prototype); // 修正 constructor 指向避免原型链混乱 F.prototype.constructor F; // 6. 返回绑定后的新函数 return F; };三、逐行拆解核心知识点与实现逻辑1. 保存原函数const fn this知识点函数的this指向 —— 当调用fn.myBind(obj)时this指向调用myBind的原函数fn比如上面的sayHi。作用后续执行新函数时需要通过这个变量调用原函数避免this丢失。2. 处理绑定对象obj Object(obj) || window知识点this的绑定规则 基本类型包装对象。细节如果传入的obj是null/undefined按原生 bind 规则this绑定到全局对象浏览器中是window如果传入的是基本类型比如123、abc用Object()转为包装对象比如Number {123}避免this绑定到基本类型基本类型无法作为this指向。3. 定义新函数 F接收剩余参数知识点剩余参数...rest 函数柯里化。作用myBind调用时传入的...args是「预置参数」新函数 F 执行时传入的...rest是「剩余参数」最终合并传给原函数比如fn(...args, ...rest)实现柯里化。4. 区分构造函数 / 普通调用这是手写 bind 最核心的难点也是面试高频考点知识点new操作符的原理 instanceof检测实例关系。逻辑解析当用new调用绑定后的函数比如new bindSayHi()this应该指向新创建的实例而非myBind传入的objthis instanceof F判断当前this是否是 F 的实例即是否通过new调用如果是构造函数场景用new fn(...args, ...rest)执行原函数此时原函数的this指向实例如果是普通场景用fn.call(obj, ...args, ...rest)执行原函数强制绑定this到obj。5. 修复原型链避免断裂知识点原型链继承 Object.create原理。问题如果直接F.prototype fn.prototype修改 F 的原型会污染原函数的原型解决方案F.prototype Object.create(fn.prototype)创建一个以fn.prototype为原型的空对象作为 F 的原型实现「继承原型但不污染原原型」F.prototype.constructor FObject.create会导致 F.prototype 的 constructor 指向fn手动修正为 F保证原型链的正确性。四、测试验证手写 myBind 是否符合预期我们用原生 bind 的测试场景验证myBindconst person { name: 张三 }; function sayHi(age, gender) { console.log(我是${this.name}${age}岁${gender}); // 构造函数场景验证 this 指向实例 this.age age; } // 1. 普通调用场景 const myBindSayHi sayHi.myBind(person, 18); myBindSayHi(男); // 输出我是张三18岁男 // 2. 构造函数场景 const instance new myBindSayHi(女); console.log(instance.age); // 输出18this 指向实例而非 person console.log(instance instanceof sayHi); // 输出true继承原函数原型五、核心知识点总结手写 bind 的过程本质是对以下 JS 核心知识点的综合运用this 绑定规则区分普通调用、构造函数调用的 this 指向函数的原型与原型链通过Object.create实现原型继承修正 constructor 指向剩余参数与柯里化合并 bind 预置参数和新函数执行参数new 操作符原理new调用时 this 指向实例且会继承原构造函数的原型基本类型包装对象处理绑定对象为基本类型的边界场景。六、面试加分点在面试中手写 bind 时除了写出核心逻辑还可以补充这些「鲁棒性细节」体现你的工程思维校验调用 myBind 的是否是函数if (typeof this ! function) throw new TypeError(not a function)兼容严格模式严格模式下null/undefined不会转为 window解释Object.create替代方案比如自定义空函数中转。最后手动实现 bind 不是「背代码」而是理解每一行背后的 JS 设计思想 —— 比如this的动态指向、原型链的继承逻辑、构造函数的特性。掌握这些底层逻辑不仅能搞定面试更能在日常开发中快速定位「this 丢失」「原型链混乱」等问题。