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

箭头函数继承外层 this 详解

🎯 核心理解

箭头函数没有自己的 this,它会"照抄"定义它时外层的 this

┌─────────────────────────────────────────────────────────────────────────┐
│                      箭头函数的 this 从哪来?                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  普通函数:调用时决定 this                                              │
│           谁调用我,this 就是谁                                         │
│                                                                         │
│  箭头函数:定义时决定 this                                              │
│           外层的 this 是谁,我就是谁                                    │
│           (像照镜子一样,外层是什么,我就照出来什么)                   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

📖 详细示例

示例1:最简单的情况

// ========== 普通函数 ==========
const obj = {name: 'Alice',greet: function() {console.log(this.name);  // 'Alice'// 普通函数:obj 调用了我,所以 this = obj}
};
obj.greet();// ========== 箭头函数 ==========
const obj2 = {name: 'Alice',greet: () => {console.log(this.name);  // undefined(或 window.name)// 箭头函数:我外层是谁?是 obj2 这个对象// 但 obj2 不是函数作用域,所以再往外找 → window// 所以 this = window}
};
obj2.greet();

图解:

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  // 箭头函数的外层是对象字面量,不是函数作用域                           │
│  // 所以再往外找 → 全局作用域 → this = window                          │
│                                                                         │
│  全局作用域 {                                                           │
│      this = window                                                      │
│                                                                         │
│      const obj2 = {                                                     │
│          name: 'Alice',                                                 │
│          greet: () => {     ← 箭头函数                                  │
│              console.log(this.name);  // this = window                 │
│          }                     ↑                                        │
│      }                         │                                        │
│  }                        继承外层的 this                               │
│                              = window                                   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

示例2:箭头函数在函数内部

const obj = {name: 'Alice',greet: function() {// 这里 this = obj(因为是 obj.greet() 调用)const arrowFn = () => {console.log(this.name);  // 'Alice'// 箭头函数:外层是 greet 函数// greet 的 this = obj// 所以我的 this = obj};arrowFn();}
};obj.greet();  // 'Alice'

图解:

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  obj.greet() 调用时:                                                   │
│                                                                         │
│  greet: function() {          ← 普通函数                                │
│      this = obj               ← 因为是 obj.greet() 调用                 │
│                                                                         │
│      const arrowFn = () => {  ← 箭头函数                                │
│          console.log(this.name);                                        │
│          // this = ?                                                    │
│          // 外层是 greet 函数,greet 的 this = obj                      │
│          // 所以箭头函数的 this = obj                                   │
│      }                                                                  │
│  }                                                                      │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

示例3:对比普通函数和箭头函数

const obj = {name: 'Alice',// ========== 普通函数 ==========greetNormal: function() {console.log('普通函数:', this.name);  // 'Alice'setTimeout(function() {console.log('setTimeout 普通函数:', this.name);// undefined// 这是普通函数,独立调用,this = window}, 100);},// ========== 箭头函数 ==========greetArrow: function() {console.log('外层函数:', this.name);  // 'Alice'setTimeout(() => {console.log('setTimeout 箭头函数:', this.name);// 'Alice'// 箭头函数的外层是 greetArrow// greetArrow 的 this = obj// 所以箭头函数的 this = obj}, 100);}
};obj.greetNormal();  // this 丢失
obj.greetArrow();   // this 保留

执行结果:

普通函数: Alice
setTimeout 普通函数: undefined    ← this 丢失外层函数: Alice
setTimeout 箭头函数: Alice        ← this 保留

示例4:嵌套箭头函数

const obj = {name: 'Alice',outer: function() {// this = objconst middle = () => {// 箭头函数:继承 outer 的 this = objconsole.log('middle:', this.name);  // 'Alice'const inner = () => {// 箭头函数:继承 middle 的 this = objconsole.log('inner:', this.name);  // 'Alice'};inner();};middle();}
};obj.outer();
// middle: Alice
// inner: Alice

图解:

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  obj.outer() 调用:                                                     │
│                                                                         │
│  outer: function() {          ← this = obj                              │
│                                                                         │
│      middle = () => {         ← 箭头函数                                │
│          this = 继承 outer 的 this = obj                                │
│                                                                         │
│          inner = () => {      ← 箭头函数                                │
│              this = 继承 middle 的 this = obj                           │
│          }                                                              │
│      }                                                                  │
│  }                                                                      │
│                                                                         │
│  一路往上继承,最终都是 obj                                             │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

示例5:箭头函数的 this 在定义时就确定了

const obj = {name: 'Alice',greet: function() {// this = objconst arrow = () => {console.log(this.name);  // 'Alice'};return arrow;}
};const fn = obj.greet();  // 返回箭头函数,此时 this 已经固定为 obj
fn();  // 'Alice'(即使在全局调用,this 仍然是 obj)// 尝试用 call 改变 this(无效)
fn.call({ name: 'Bob' });  // 仍然是 'Alice'

重点: 箭头函数的 this定义时就确定了,之后无法改变。


示例6:实际应用场景

场景1:数组遍历时保留 this

const obj = {name: 'Alice',friends: ['Bob', 'Charlie'],showFriends: function() {// this = obj// ❌ 普通函数:this 丢失this.friends.forEach(function(friend) {console.log(`${this.name} knows ${friend}`);// undefined knows Bob// undefined knows Charlie});// ✅ 箭头函数:this 保留this.friends.forEach((friend) => {console.log(`${this.name} knows ${friend}`);// Alice knows Bob// Alice knows Charlie});}
};obj.showFriends();

场景2:定时器中保留 this

class Timer {constructor() {this.seconds = 0;}// ❌ 普通函数:this 丢失startBad() {setInterval(function() {this.seconds++;  // this = window,出错console.log(this.seconds);}, 1000);}// ✅ 箭头函数:this 保留startGood() {setInterval(() => {this.seconds++;  // this = Timer 实例console.log(this.seconds);}, 1000);}
}const timer = new Timer();
timer.startGood();

场景3:事件处理

class Button {constructor() {this.name = 'MyButton';this.setupEvent();}setupEvent() {const btn = document.getElementById('btn');// ❌ 普通函数:this = DOM 元素btn.addEventListener('click', function() {console.log(this.name);  // undefined(this = <button>)});// ✅ 箭头函数:this = Button 实例btn.addEventListener('click', () => {console.log(this.name);  // 'MyButton'});}
}

🔑 关键总结

┌─────────────────────────────────────────────────────────────────────────┐
│                      箭头函数 this 的三个关键点                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  1. 定义时确定,不是调用时确定                                          │
│                                                                         │
│     const arrow = () => { console.log(this); };                         │
│     // 这一刻,this 就确定了(外层是谁就是谁)                          │
│                                                                         │
│  2. 往外找最近的函数作用域                                              │
│                                                                         │
│     如果外层是对象 {} → 继续往外找                                      │
│     如果外层是函数 function → 这个函数的 this 就是箭头函数的 this       │
│                                                                         │
│  3. 一旦确定,无法改变                                                  │
│                                                                         │
│     arrow.call(obj);  // 无效                                           │
│     arrow.apply(obj); // 无效                                           │
│     arrow.bind(obj);  // 无效                                           │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

📝 练习题

const obj = {name: 'Alice',fn1: function() {console.log('fn1:', this.name);const fn2 = () => {console.log('fn2:', this.name);};return fn2;},fn3: () => {console.log('fn3:', this.name);}
};// 输出什么?
obj.fn1()();   // fn1: Alice, fn2: Alice
obj.fn3();     // fn3: undefinedconst fn2 = obj.fn1();
fn2();         // fn2: Alice(箭头函数 this 已固定)const fn3 = obj.fn3;
fn3();         // fn3: undefined(箭头函数 this = window)

快速记忆口诀

箭头函数没有 this,
往外照抄第一层。
定义时候就确定,
call/apply 没法改。
http://www.jsqmd.com/news/570125/

相关文章:

  • Gemini 3.1镜像深度推理实战:解构多模态长视频理解与结构化知识抽取
  • FPGA数字钟课程设计还能这么玩?从基础功能到智能扩展(附完整工程文件)
  • DeOldify企业级应用:构建自动化老照片修复平台
  • 告别QtCreator!用VSCode+Qt 5.14.2开发GUI应用,这份保姆级配置指南请收好
  • 青岛西装定制哪家靠谱?2026五大品牌硬核数据对比,谁更专业一目了然 - 速递信息
  • ORCAD/pspice仿真技巧:如何高效绘制电路的幅频与相频特性曲线
  • IGV基因组可视化实战:从BAM到TDF的高效转换技巧
  • Hunyuan-MT-7B部署教程:Pixel Language Portal在国产操作系统(OpenEuler)兼容性验证
  • react(二)useEffect 和 useRef
  • YOLO12在智能相册中的应用:自动标注80类常见物体,解放双手
  • 探讨西安做侘寂风装修公司,哪家口碑好值得推荐 - myqiye
  • 71款移动应用隐私违规,个人信息安全谁来守护?
  • LaTeX与丹青识画结合:自动化生成学术论文中的艺术品分析报告
  • 007-PDF处理实战:解析、合并与加密自动化
  • 水质分析仪哪家质量好?哪家靠谱?哪个品牌好?青岛格林诺尔水质分析仪的检测原理与核心优势 - 品牌推荐大师
  • 2026年技术革新|深度解析青岛格林诺尔烟气分析仪的核心技术——紫外差分吸收光谱 - 品牌推荐大师
  • JetBrains IDE + Luma MCP:为你的项目生成 AI 视频
  • LeetCode 2751. 机器人碰撞 详细技术解析(栈模拟+排序)
  • Pixel Dimension Fissioner 微信小程序集成开发:打造个人像素头像生成工具
  • 【PLM合集】190余份PLM产品生命周期管理方案、可行性研究报告、ERP、CRM、MES、OA、SRM、WMS、APS系统集成方案
  • Intv_AI_MK11 集成 MySQL 数据库:智能客服对话数据存储与查询实战
  • ffmpegGUI:让专业视频处理触手可及的跨平台工具
  • AI时代:重塑核心竞争力
  • 别再只让电机转起来了!用ESP32读取霍尔编码器,给你的推杆项目加上‘眼睛’和‘大脑’
  • 保姆级教程:在Windows 10/11上搞定IAR 8.10 for 8051开发环境(附CC2530工程编译验证)
  • LFM2.5-1.2B-Thinking-GGUF快速部署:仅需1条命令启动32K上下文服务
  • 从玩具车到机器人:手把手教你用STM32和编码器实现精准的电机测距(附完整代码)
  • 还在为植物大战僵尸资源不足烦恼?这款开源修改器让游戏体验焕然一新
  • 千问3.5-9B视觉模型快速部署指南:单卡RTX 4090D实测可用
  • qModMaster:工业通信调试的开源ModBus主站解决方案