B02 this 与原型链

2026-10-31 17:180 阅读约 11 分钟JavaScript

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 验证与讨论

  1. 执行脚本,确认 detached() 抛出 TypeError,bound() 输出 hi Lin。
  2. 把 'use strict' 删除,再次执行 detached(),确认不再抛错而是读到全局的 name(undefined)。
  3. 执行 Object.getPrototypeOf(c) 逐层向上打印,确认链条以 null 结束。
  4. 给实例赋值 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

参考文献

[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

0/2000

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