7-2、详细说说bind、call、apply的区别?实现bind、call、apply?

张开发
2026/4/3 13:15:29 15 分钟阅读
7-2、详细说说bind、call、apply的区别?实现bind、call、apply?
目录一、三者的核心区别二、深入解析2.1 call 详解2.2 apply 详解2.3 bind 详解三、手写实现3.1 手写 call3.2 手写 apply3.3 手写 bind四、经典面试题解析题目1this指向问题题目2bind的连续调用题目3箭头函数与bind题目4实际应用场景五、面试怎么回答才精彩 标准回答模板 加分项一、三者的核心区别特性callapplybind执行时机立即执行立即执行返回新函数不立即执行参数形式逐个传参fn.call(obj, arg1, arg2)数组传参fn.apply(obj, [arg1, arg2])逐个传参fn.bind(obj, arg1, arg2)返回值函数执行结果函数执行结果绑定this后的新函数使用场景立即调用参数明确立即调用参数是数组需要保存函数延迟执行一句话总结call和apply都是立即执行函数并改变this区别在于传参方式bind返回一个新函数不立即执行。二、深入解析2.1 call 详解const person { name: Alice, greet: function(greeting, punctuation) { console.log(${greeting}, Im ${this.name}${punctuation}); } }; const anotherPerson { name: Bob }; // 使用call改变this指向 person.greet.call(anotherPerson, Hello, !); // 输出: Hello, Im Bob!特点参数列表形式传递立即执行第一个参数是this指向的对象2.2 apply 详解const numbers [5, 6, 2, 3, 7]; // 经典应用求数组最大值 const max Math.max.apply(null, numbers); console.log(max); // 7 // 等价于 const max2 Math.max.call(null, 5, 6, 2, 3, 7);特点参数以数组形式传递适合参数数量不确定或已经是数组的场景2.3 bind 详解const module { x: 42, getX: function() { return this.x; } }; const unboundGetX module.getX; console.log(unboundGetX()); // undefined (this指向全局) const boundGetX unboundGetX.bind(module); console.log(boundGetX()); // 42 // bind可以预设参数柯里化 function multiply(a, b) { return a * b; } const double multiply.bind(null, 2); console.log(double(5)); // 10特点返回新函数不立即执行可以分步传参偏函数应用绑定后的this无法再次改变三、手写实现3.1 手写 callFunction.prototype.myCall function(context, ...args) { // 1. 处理context为null/undefined的情况 context context || window; // 浏览器环境 // context context || globalThis; // 通用写法 // 2. 将函数设为对象的属性使用Symbol避免属性冲突 const fnSymbol Symbol(fn); context[fnSymbol] this; // 3. 执行函数 const result context[fnSymbol](...args); // 4. 删除临时属性 delete context[fnSymbol]; // 5. 返回结果 return result; }; // 测试 function greet(greeting, punctuation) { console.log(${greeting}, Im ${this.name}${punctuation}); } const person { name: Charlie }; greet.myCall(person, Hi, !!!); // Hi, Im Charlie!!!3.2 手写 applyFunction.prototype.myApply function(context, argsArray) { // 1. 处理context context context || window; // 2. 处理参数apply接收数组 argsArray argsArray || []; // 3. 将函数设为对象的属性 const fnSymbol Symbol(fn); context[fnSymbol] this; // 4. 执行函数 const result context[fnSymbol](...argsArray); // 5. 删除临时属性 delete context[fnSymbol]; // 6. 返回结果 return result; }; // 测试 function sum(a, b, c) { console.log(this.prefix, a b c); } const obj { prefix: Result: }; sum.myApply(obj, [1, 2, 3]); // Result: 63.3 手写 bindFunction.prototype.myBind function(context, ...bindArgs) { // 保存原函数 const fn this; // 返回一个新函数 return function(...callArgs) { // 合并bind时的参数和调用时的参数 return fn.apply(context, [...bindArgs, ...callArgs]); }; }; // 进阶版支持new操作符 Function.prototype.myBind2 function(context, ...bindArgs) { const fn this; const boundFunction function(...callArgs) { // 如果是通过new调用this指向实例忽略传入的context // 否则使用传入的context return fn.apply( this instanceof boundFunction ? this : context, [...bindArgs, ...callArgs] ); }; // 维护原型链 if (fn.prototype) { boundFunction.prototype Object.create(fn.prototype); } return boundFunction; }; // 测试 function Point(x, y) { this.x x; this.y y; } const YAxisPoint Point.myBind2(null, 0); const point new YAxisPoint(5); console.log(point); // Point { x: 0, y: 5 }四、经典面试题解析题目1this指向问题var name Global; const obj { name: Object, getName: function() { return this.name; } }; const getName obj.getName; console.log(getName()); // Global (this指向window) console.log(obj.getName()); // Object (this指向obj) console.log(getName.call(obj)); // Object (改变this指向)题目2bind的连续调用function fn() { console.log(this.name); } const obj1 { name: obj1 }; const obj2 { name: obj2 }; const boundFn fn.bind(obj1).bind(obj2); boundFn(); // 输出: obj1 // 解析bind只生效一次第一次绑定后无法改变题目3箭头函数与bindconst obj { name: Object, arrow: () { console.log(this.name); }, normal: function() { console.log(this.name); } }; const newObj { name: NewObject }; obj.arrow.call(newObj); // undefined (箭头函数this无法改变) obj.normal.call(newObj); // NewObject (普通函数this可以改变)题目4实际应用场景// 场景1数组借用方法 const arrayLike { 0: a, 1: b, 2: c, length: 3 }; const arr Array.prototype.slice.call(arrayLike); console.log(arr); // [a, b, c] // 场景2获取数据类型 function getType(obj) { return Object.prototype.toString.call(obj).slice(8, -1); } console.log(getType([])); // Array console.log(getType({})); // Object // 场景3事件处理中保持this class Button { constructor(text) { this.text text; this.handleClick this.handleClick.bind(this); } handleClick() { console.log(Button ${this.text} clicked); } } const btn new Button(Submit); document.addEventListener(click, btn.handleClick); // this正确指向btn实例五、面试怎么回答才精彩 标准回答模板第一层基础概念call、apply、bind都是用来改变函数执行时this指向的方法。它们的主要区别在于执行时机call和apply立即执行函数bind返回一个新函数参数传递call逐个传参apply用数组传参bind逐个传参但支持分步传参第二层实现原理它们的核心原理是将函数作为目标对象的临时方法来调用从而改变this指向。我可以手写实现这三个方法...简要说明实现思路展示核心代码第三层应用场景在实际开发中call/apply类数组转数组、求数组最大值、类型判断bindReact类组件事件绑定、防抖节流、偏函数应用第四层注意事项需要注意的是箭头函数的this无法通过这些方法改变bind只生效一次多次bind只有第一次有效在严格模式下this不会自动转为window对象 加分项提到ES6的替代方案// 使用扩展运算符替代apply Math.max(...numbers); // 使用箭头函数替代bind onClick{() this.handleClick()}性能对比bind会创建新函数有一定性能开销call/apply直接执行性能更好实际项目经验在我之前的项目中使用bind解决了React组件中this丢失的问题...

更多文章