← [ BACK_TO_NOTES ]

JavaScript 原型链

解释对象属性查找如何沿原型链上升、与 constructor/prototype 的关系,以及 class 语法糖如何落到同一模型。

#concept #growing #dev #javascript #ecmascript

[!info] related notes

JavaScript 原型链

这个概念为什么出现

你在对象上读 sku.toString,自己明明没写这个方法,却能调用。团队若把这理解成“类继承复制成员”,后面在改 prototype、谈性能、写 polyfill 时会全面错位。

原型链回答:属性从哪里来,找不到时下一步去哪找。

[!abstract] 一句话理解 普通对象有一个内部原型链接;读属性时沿这条链向上查找,直到找到或到达 null。Proxy 等特殊对象可改变访问行为。

最小可运行示例

场景:库存领域对象共享格式化方法

// 业务意图:所有 SKU 视图模型共享 formatStock,不在每个实例上复制函数。
// 教学点:实例自身没有的方法,会走到构造函数 prototype。

function SkuView(code, qty) {
  this.code = code
  this.qty = qty
}

SkuView.prototype.formatStock = function formatStock() {
  // 教学点:这里的 this 由调用方式决定,一般是实例
  return this.qty < 5
    ? this.code + ' 低库存(' + this.qty + ')'
    : this.code + ' 正常(' + this.qty + ')'
}

const a = new SkuView('A-01', 3)
const b = new SkuView('B-02', 20)

console.log(a.formatStock())
console.log(Object.hasOwn(a, 'formatStock')) // false:方法不在实例自身
console.log(Object.getPrototypeOf(a) === SkuView.prototype) // true
console.log(a.toString === Object.prototype.toString) // 继续沿链找到 Object.prototype

建议运行:

node sku-proto.js

期望输出:

A-01 低库存(3)
false
true
true

结合场景再看三个关注点

  1. 共享方法放在 Fn.prototype,实例变轻。
  2. 查找失败不是报错在第一层,而是继续 [[Prototype]]。
  3. hasOwn/in 不同:in 会看链,hasOwn 只看自身。

核心概念与准确模型

两条相关但不同的线

概念是什么
实例的 [[Prototype]]对象的原型链接(Object.getPrototypeOf)
构造函数的 .prototype普通 new 创建对象时用作实例原型;不是函数对象自身的原型
obj.__proto__历史访问器,教学可用,生产更推荐标准 API
a ---> SkuView.prototype ---> Object.prototype ---> null

读属性算法(压缩)

  1. 看对象自身
  2. 看 [[Prototype]]
  3. 重复直到 null
  4. 若是方法调用,this 仍由调用方式决定,不由方法所在原型决定

写属性

普通对象赋值通常在接收者自身创建或更新属性;继承的 setter 可以接管赋值,继承的不可写数据属性或无 setter 的访问器也可能阻止赋值(严格模式抛错)。

obj.items.push(x) 是修改查找到的数组,不是给 obj.items 赋值;若数组来自原型,所有读取同一数组的实例都会看到变化。

与 class 的关系

class SkuView {
  constructor(code, qty) {
    this.code = code
    this.qty = qty
  }
  formatStock() {
    /* 方法在原型上 */
  }
}

class 是语法层;运行时仍是原型模型。见 原型不是 class。

设计动机

原型共享让方法与默认值可复用;需要独立的可变数据应由每个实例持有。类语言通常也不会逐实例复制方法,区别应放在对象间的动态委托关系上。

边界与反直觉

  • for...in 遍历自身与链上的可枚举字符串键;Object.keys 只看自身可枚举字符串键,两者都不包含 Symbol 键。\
  • Object.create(null) 创建无原型对象,适合纯字典,但没有继承的 toString 或 proto 访问器。
  • 函数不一定有可用于 new 的 prototype:箭头函数不可构造;普通构造函数的 prototype 若不是对象,new 会回退到相应内建原型。\
  • 改内置 prototype 是高风险全局行为。

常见误区

[!warning] 常见误区:原型链 = 类的父类字段复制 没有在实例上复制方法表;是链接查找。

[!warning] 常见误区:arrow 方法挂原型一个样 类字段箭头函数常装在实例上,不共享;与原型方法不同。

工程实践

  • 优先 class 语法,但用原型心智调试。
  • 字典数据用 Map 或 Object.create(null),避免踩 Object.prototype 键。
  • 不要在热路径上深层原型上挂过多可枚举垃圾属性。

本节总结

原型链是属性查找的链表。会画链,才能理解继承、new、class 与方法共享。

自测题

  1. a.formatStock() 时方法在原型,this 是谁?
  2. 为什么修改 SkuView.prototype.x 会影响已有实例读取?
参考答案
  1. 一般是 a(接收者),由调用形式决定。
  2. 仅当实例未以自有属性遮蔽 x 时,读取才会沿链接受原型修改影响。替换 Fn.prototype 为另一个对象不会重新连接已有实例。

延伸阅读

资料类型支撑内容
ECMA-262规范对象内部方法
MDN Inheritance and prototype chain文档原型链
创建于 2026/3/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本