this 关键字
按调用方式解释 this 绑定规则,覆盖默认绑定、隐式绑定、显式绑定、new 绑定与箭头函数词法 this。
#type / concept
#status / growing
#tech / dev
#resource / javascript
#resource / ecmascript
[!info] 关联笔记
- 所属:类型与对象模型 MOC
- 路线:JavaScript 学习路线
- 前置:函数 · 对象
- 相关:原型链 · 闭包
this 关键字
这个概念为什么出现
回调里 this 突然变成 undefined,事件处理函数里读不到组件字段,setTimeout 后方法丢失接收者——这些问题几乎都不是“语法记错”,而是 this 由调用方式决定 没建立起来。
[!abstract] 一句话理解 普通函数的
this在调用时绑定;箭头函数的this在定义时词法继承外层。
最小可运行示例
场景:库存服务对象的方法被当成回调传递
const inventoryService = {
warehouse: 'A仓',
label(sku) {
// 业务意图:打印仓库前缀;教学点:谁调用,this 就指向谁
return this.warehouse + '::' + sku
},
}
console.log(inventoryService.label('SKU-1')) // 隐式绑定:A仓::SKU-1
const detached = inventoryService.label
try {
// 严格模式 / 模块顶层下常为 TypeError 或无法读 warehouse
console.log(detached('SKU-2'))
} catch (e) {
console.log('detached failed:', e.message)
}
console.log(detached.call({ warehouse: 'B仓' }, 'SKU-3')) // 显式绑定
console.log(inventoryService.label.bind({ warehouse: 'C仓' })('SKU-4'))
const arrowService = {
warehouse: 'A仓',
label: (sku) => {
// 箭头函数:this 不绑定到 arrowService
return (typeof this === 'undefined' ? 'no-this' : this.warehouse) + '::' + sku
},
}
建议运行:
node --input-type=module -e "/* 粘贴为模块可观察严格 this */"
# 或把文件存为 mjs/cjs 分别观察
结合场景关注点
- 取方法再调用会丢接收者。
call/apply/bind显式指定 this。- 箭头函数不能靠
bind改 this 语义来“变成方法接收者”。
核心规则(普通函数)
| 调用形式 | this 直觉 |
|---|---|
fn() | undefined(严格)/ 全局对象(非严格、非模块) |
obj.fn() | obj |
fn.call(x) / apply / bind | 显式为 x |
new Fn() | 新创建的实例 |
| DOM 事件处理器(历史) | 监听元素(注意框架封装差异) |
优先级教学口诀:new > 显式 > 隐式 > 默认(细节与 bound function 组合以规范为准)。
箭头函数
- 没有自己的
this绑定 - 常用于回调中保留外层 this
- 不适合需要动态接收者的原型方法主路径
常见误区
[!warning] 常见误区:this 指向函数定义所在对象 普通函数看调用,不看定义位置。
工程实践
- class 字段箭头:固定 this,但方法在实例上,注意内存。
- 回调优先传
() => obj.method()或obj.method.bind(obj)。 - React 事件里 class 组件历史绑定问题已被 hooks 函数组件弱化,但仍要理解。
本节总结
this 是调用约定,不是词法地址(箭头除外)。先判断怎么被叫,再读 this。
自测题
- 为什么
const f = obj.m; f()危险? - 箭头函数方法能被子类动态 this 复用吗?
参考答案
- 丢失隐式绑定,this 变默认绑定。
- 通常不能按“接收者对象”动态绑定;它捕获定义时外层 this。
延伸阅读
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| MDN this | 文档 | 绑定规则 |
| ECMA-262 | 规范 | OrdinaryCallBindThis 等 |