B02 this 与原型链
B02 this 与原型链
摘要:
this的取值由调用形式而非定义位置决定,箭头函数例外,它继承外层this。原型链决定属性查找路径,实例自身属性优先,方法放在原型上可共享。混用回调与对象方法、丢失方法引用、误用new是三类高频错误。本文给出四种调用形式的绑定规则、绑定优先级与原型链的三层结构。
关键词:this,调用形式,原型链,class,bind
所属:frontend/js / B-进阶 | 上一篇:B01-作用域与闭包 | 下一篇:B03-Promise与async异步
1 引言
把对象方法直接传给 setTimeout 后,方法里的 this 变成 undefined;把方法赋给另一个变量再调用,结果相同。原因是 this 不随函数定义固定,而由调用表达式决定。
本文回答三个问题:this 在四种调用形式下是什么、如何固定、原型链上属性如何查找。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
this |
四种调用形式、箭头函数、bind/call |
严格模式细节 |
| 原型 | 原型链、class 语法、继承 |
属性描述符与 Proxy(C02) |
| 判别 | instanceof、isPrototypeOf |
类型标注(C05) |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | Node.js 20+ 或浏览器控制台 | 能执行多行脚本 |
| 前置 | B01 | 理解词法作用域 |
| 文件 | js-practice/this-prototype.js |
编码 UTF-8 |
3 原理与实现
3.1 四种调用形式
this 的取值按调用表达式的形态判断,优先级由高到低如图 3-1 所示。
flowchart TD
A["函数被调用"] --> B{"是否箭头函数"}
B -->|是| C["继承定义处外层 this"]
B -->|否| D{"是否 new 调用"}
D -->|是| E["新建的空对象"]
D -->|否| F{"是否 bind 过"}
F -->|是| G["bind 指定的对象"]
F -->|否| H{"是否 obj.fn 形式"}
H -->|是| I["obj"]
H -->|否| J["严格模式 undefined,非严格模式全局对象"]
图 3-1 this 取值按调用形式判定,箭头函数最先排除
图上可见:
obj.fn()的this是obj左侧那个对象;把fn单独取出调用,左侧不再是obj,this随之变化。
3.2 完整实现
// js-practice/this-prototype.js;运行环境:Node.js 20+
'use strict';
const user = {
name: 'Lin',
greet() {
return `hi ${this.name}`;
},
};
console.log(user.greet()); // hi Lin
const detached = user.greet;
try {
console.log(detached()); // 抛 TypeError:this 为 undefined
} catch (err) {
console.log(err.name); // TypeError
}
const bound = user.greet.bind(user);
console.log(bound()); // hi Lin
// 箭头函数继承外层 this,不能用作方法
const obj = {
name: 'Chen',
arrow: () => (typeof this === 'object' ? 'module this' : 'other'),
normal() {
return this.name;
},
};
console.log(obj.normal()); // Chen
// 原型链:实例 → 构造原型 → Object.prototype → null
class Shape {
constructor(name) {
this.name = name;
}
describe() {
return `shape:${this.name}`;
}
}
class Circle extends Shape {
constructor(radius) {
super('circle');
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
const c = new Circle(2);
console.log(c.describe()); // shape:circle
console.log(Object.hasOwn(c, 'describe')); // false,方法在原型上
console.log(Object.hasOwn(c, 'radius')); // true
console.log(c instanceof Shape); // true
console.log(Object.getPrototypeOf(c) === Circle.prototype); // true
this 的四种形式与固定方式见表 3-1。
| 调用形式 | 写法 | this 取值 |
|---|---|---|
| 方法调用 | obj.fn() |
obj |
| 独立调用 | fn() |
严格模式 undefined |
| 构造调用 | new Fn() |
新建实例 |
| 显式指定 | fn.call(x) / fn.apply(x) / fn.bind(x)() |
x |
| 箭头函数 | () => this |
定义处外层 this,不可改 |
原型链查找规则见表 3-2。
| 操作 | 查找顺序 | 结果判定 |
|---|---|---|
| 读属性 | 实例 → 原型 → …→ Object.prototype |
链上首个同名属性 |
| 写属性 | 只在实例自身创建或修改 | 不修改原型上的同名属性 |
in 运算符 |
整条链 | 链上存在即为 true |
hasOwnProperty |
仅实例自身 | 判断自有属性 |
| 删除 | 仅实例自身 | 原型属性删不掉 |
4 验证与讨论
- 执行脚本,确认
detached()抛出TypeError,bound()输出hi Lin。 - 把
'use strict'删除,再次执行detached(),确认不再抛错而是读到全局的name(undefined)。 - 执行
Object.getPrototypeOf(c)逐层向上打印,确认链条以null结束。 - 给实例赋值
c.describe = () => 'own',确认只有该实例受影响,其它实例仍用原型方法。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 方法丢失 | 独立调用抛 TypeError |
严格模式下 this 为 undefined |
| bind 固定 | 输出 hi Lin |
绑定后不受调用形式影响 |
| 方法共享 | hasOwn(c,'describe') 为 false |
方法在原型上 |
| 继承判定 | c instanceof Shape 为 true |
原型链连通 |
讨论与局限。 bind 只能固定一次,后续 call 无法覆盖;箭头函数没有 prototype,不能用 new 调用。另,instanceof 依赖同一运行时的原型对象,跨 iframe 或跨来源对象会失效。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
回调中 this 为 undefined |
方法被独立调用 | 用 bind 或箭头函数包裹 |
| 箭头函数里拿不到对象属性 | 箭头继承外层 this |
对象方法改用简写方法 |
new 调用箭头函数报错 |
箭头函数不可构造 | 改用 class 或普通函数 |
| 修改原型后所有实例受影响 | 属性写在原型上 | 实例数据放构造函数内 |
instanceof 判断失败 |
对象来自其它 iframe | 用鸭子类型或 Symbol.hasInstance |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | this 由调用形式决定,不由定义位置决定 |
图 3-1 |
| 2 | 箭头函数继承外层 this,不可被改写 |
3.2 节 |
| 3 | 方法放原型上共享,数据放实例上 | 表 3-2 |
| 4 | 需要固定上下文时用 bind |
表 3-1 |
- 上一篇:B01-作用域与闭包
- 下一篇:B03-Promise与async异步
- 返回:B00-总结 | 科目 README
参考文献
[1] MDN Web Docs · this,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this(访问于 2026-10-31)
[2] MDN Web Docs · 继承与原型链,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain(访问于 2026-10-31)

评论 0
还没有评论,快来抢沙发吧~