当前位置: 首页 > news >正文

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

目录

一、三者的核心区别

二、深入解析

2.1 call 详解

2.2 apply 详解

2.3 bind 详解

三、手写实现

3.1 手写 call

3.2 手写 apply

3.3 手写 bind

四、经典面试题解析

题目1:this指向问题

题目2:bind的连续调用

题目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}, I'm ${this.name}${punctuation}`); } }; const anotherPerson = { name: 'Bob' }; // 使用call改变this指向 person.greet.call(anotherPerson, 'Hello', '!'); // 输出: Hello, I'm 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 手写 call

Function.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}, I'm ${this.name}${punctuation}`); } const person = { name: 'Charlie' }; greet.myCall(person, 'Hi', '!!!'); // Hi, I'm Charlie!!!

3.2 手写 apply

Function.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: 6

3.3 手写 bind

Function.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 }

四、经典面试题解析

题目1:this指向问题

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指向)

题目2:bind的连续调用

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:箭头函数与bind

const 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:类数组转数组、求数组最大值、类型判断
  • bind:React类组件事件绑定、防抖节流、偏函数应用"

第四层:注意事项

"需要注意的是:

  • 箭头函数的this无法通过这些方法改变
  • bind只生效一次,多次bind只有第一次有效
  • 在严格模式下,this不会自动转为window对象"

💡 加分项

  1. 提到ES6+的替代方案

    // 使用扩展运算符替代apply Math.max(...numbers); // 使用箭头函数替代bind onClick={() => this.handleClick()}
  2. 性能对比

    • bind会创建新函数,有一定性能开销
    • call/apply直接执行,性能更好
  3. 实际项目经验

    • "在我之前的项目中,使用bind解决了React组件中this丢失的问题..."
http://www.jsqmd.com/news/581330/

相关文章:

  • ST-DBSCAN实战指南:从入门到精通的时空数据分析技术
  • 实战应用:基于快马平台构建OAuth 2.0的token交换与用户登录流程
  • Python轻松实现某德地图可视化功能
  • 写业务代码必备:9 个被低估的 Python 高效工具库
  • RexUniNLU场景应用:快速构建一个自动化新闻事件抽取工具
  • AI-大模型场景安全性测试
  • Zotero PDF Translate 离线翻译支持:LibreTranslate集成方案与学术场景价值
  • PhotoShop(PS)下载安装指南
  • PyInstxtractor深度实战:解锁PyInstaller加密包逆向分析技术
  • AudioSeal Pixel Studio实操手册:多声道WAV文件水印嵌入兼容性测试报告
  • 如何搭建企业级IP归属地查询平台?
  • SEO_2024年最新SEO策略与方法全面介绍
  • 2026年浙江玻璃液膜蒸发器来图定制,费用多少钱 - 工业设备
  • 如何快速使用fre:ac音频转换工具:新手完整入门指南
  • 新手福音:在快马上手把手学vlookup跨表格匹配(含避坑指南)
  • Qwen3-ForcedAligner-0.6BGPU部署避坑指南:常见OOM错误与解决方案
  • Phi-3-mini-4k-instruct-gguf效果实测:单卡3090上并发3路问答的延迟与显存占用
  • Phi-4-mini-reasoning数学推理benchmark:GSM8K、MATH、AMC实测准确率报告
  • 选购玻璃液膜蒸发器厂要注意什么 - 工业品网
  • 3分钟掌握QQ音乐解密神器qmcdump:轻松转换加密音频格式
  • 量化交易策略开发新范式:StockSharp平台从问题到价值的实现路径
  • Qwen3.5-2B轻量实战:在24GB显存服务器上并发处理8路图文请求
  • 模拟(数青蛙)(5)
  • 创新型GTA模组管理器:高效实现安全管理与动态加载的完整指南
  • Python数据分析神器DuckDB保姆级使用入门指南
  • Linux grep 命令的使用指南
  • 国外行星波动探测数据网站
  • 安徽糕点西点培训学院价格多少钱 - 工业品牌热点
  • AI赋能内网穿透:让快马智能体为你量身定制安全高效的穿透策略
  • Hunyuan-MT-7B部署案例:媒体机构构建多语种新闻快讯自动编译流水线