一、类比板块
(胡桃蹲在柜台摆弄招魂幡,手里两份账单对比,平板打开 MC 弹窗回调代码,晃着小辫子开口)
各位学徒、MC 脚本开发者,上一节我们掌握箭头函数各种简写格式,看着代码变清爽,但很多人写回调时会踩this指向大坑 —— 普通函数和箭头函数的this完全不一样,界面点击、定时器逻辑经常出现拿不到目标对象的 bug。本节彻底拆解箭头函数this规则,分清两种函数的核心区别,解决 MC GUI 回调、账单统计里最常见的 this 报错。
往生堂类比:普通 function 函数的 this 好比现场临时工,谁调用它,它就认谁做主;箭头函数的 this 是入职固定学徒,刚定义时绑定好归属,不管后面谁调用,永远认外层上下文,不会随便更换主人。如果用普通函数写台账统计,换调用场景 this 就乱了;箭头函数全程沿用外层账单对象,取值稳定不出错。
MC 基岩 GUI 类比:写道具弹窗点击事件、定时刷新背包脚本时,普通函数回调内 this 会指向全局 / 点击元素,拿不到道具配置;箭头函数直接继承外层 GUI 对象的 this,不用额外存_this/that做中转,界面逻辑更稳定。
二、正文板块
1. 前置:普通 function 函数的 this 规则(对比参照)
传统函数this由调用方式决定,执行时动态变更,基础 this 知识回看:https://www.ihuaben.com/book/14226653.html
全局直接调用:this === window(浏览器)
对象方法调用:this指向调用的对象
DOM/MC 界面事件回调:this指向触发元素
new 构造调用:this指向新建实例
示例(往生堂账单对象):
javascript
运行
const bill = {
price: 2000,
show: function(){
console.log(this.price); // this 是bill对象
}
}
bill.show(); // 输出2000
弊端:如果把方法单独取出执行,this 丢失上下文,直接变成全局:
javascript
运行
const fn = bill.show;
fn(); // undefined,this变成window
2. 箭头函数核心特性:无独立 this,继承外层作用域
箭头函数自身不创建 this,定义瞬间捕获外层最近一层普通函数 / 全局的 this,后续无论怎么调用,this 永远不变,不会随执行场景改变。
语法核心结论:箭头函数 this = 定义位置外层 this。
案例 1:对象内定时器(MC 高频踩坑场景)
ES2 老式写法,需要用let that = this中转保存上下文:
javascript
运行
// MC道具面板旧代码
const itemPanel = {
stack: 64,
refresh: function(){
let that = this; // 提前存储外层this
// 普通函数定时器,this指向全局
setTimeout(function(){
console.log(that.stack); // 必须靠that中转才能读取64
}, 1000)
}
}
itemPanel.refresh();
箭头函数优化,无需中转,直接继承外层 this:
javascript
运行
const itemPanel = {
stack: 64,
refresh: function(){
// 箭头函数直接继承refresh内的this(itemPanel)
setTimeout(() => {
console.log(this.stack); // 64,正常读取
}, 1000)
}
}
itemPanel.refresh();
案例 2:往生堂账单批量遍历回调
普通 function 回调 this 指向全局,读取不到账单基础价格:
javascript
运行
const hall = {
base: 1200,
priceList: [150, 800],
calc: function(){
// 普通function,this为window
const total = this.priceList.reduce(function(sum, p){
return sum + p + this.base; // this.base undefined
}, 0)
console.log(total);
}
}
hall.calc();
替换箭头函数,继承外层 hall 对象 this:
javascript
运行
const hall = {
base: 1200,
priceList: [150, 800],
calc: function(){
// 箭头函数捕获外层this(hall)
const total = this.priceList.reduce((sum, p) => {
return sum + p + this.base;
}, 0)
console.log(total);
}
}
hall.calc(); // 正常输出2150
3. 致命误区:不要用箭头函数作为对象方法
对象属性如果直接赋值箭头函数,外层 this 是全局 window,永远读取不到对象自身属性,属于高频错误写法:
javascript
运行
// 错误示范
const tool = {
name: "桃木符",
print: () => {
console.log(this.name); // this是全局,输出空/undefined
}
}
tool.print();
原因:箭头函数定义在全局层级,外层 this 为 window,和 tool 对象无关;对象方法必须使用普通 function。
4. 多层嵌套 this 传递演示
多层函数嵌套,箭头函数逐层向上继承最近普通函数的 this:
javascript
运行
const ui = {
width: 360,
run: function(){
// 第一层箭头,this=ui
const inner = () => {
// 第二层箭头,继续继承ui的this
setTimeout(() => {
console.log(this.width); // 360
}, 500)
}
inner();
}
}
ui.run();
5. 配套衍生限制(this 附带规则)
箭头函数无arguments,不定参数只能用...rest;
不能使用call/apply/bind修改 this,调用无效;
javascript
运行
const fn = () => console.log(this);
fn.call({name:"测试"}); // this依旧是全局,无法修改
不能作为构造函数 new,会直接报错;
6. 选型快速判断
定时器、数组 map/filter/reduce、GUI 异步回调 → 优先箭头函数,稳定继承外层 this;
对象方法、类方法、需要动态 this 的事件回调 → 必须普通 function。
三、课后练习
基础题
写出代码输出结果
javascript
运行
const bag = {
num: 64,
log: function(){
setTimeout(()=>{
console.log(this.num);
}, 100)
}
}
bag.log();
改错:指出下方代码错误原因
javascript
运行
const goods = {
price: 1200,
show: () => console.log(this.price)
}
goods.show();
进阶题
将下方需要 that 中转的老式代码,改写为箭头函数消除临时变量 that
javascript
运行
const panel = {
w: 400,
timer: function(){
let that = this;
setTimeout(function(){
console.log(that.w);
}, 200)
}
}
panel.timer();
编写 MC 道具对象:包含 stack=32,普通函数 calc,内部用 reduce + 箭头函数累加数组 [16,32] 并加上 this.stack,打印总和。
参考答案
基础题
输出 64
show 为箭头函数,定义外层 this 是全局 window,无法获取 goods 内部 price 属性;对象方法禁止使用箭头函数,改为普通 function。
进阶题
简化箭头版本
javascript
运行
const panel = {
w: 400,
timer: function(){
setTimeout(()=>{
console.log(this.w);
}, 200)
}
}
panel.timer();
MC 完整脚本
javascript
运行
const item = {
stack: 32,
numArr: [16, 32],
calc: function(){
const sum = this.numArr.reduce((s, n) => s + n + this.stack, 0);
console.log(sum);
}
}
item.calc();