JavaScript 原型链

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

#type / concept #status / growing #tech / dev #resource / javascript #resource / ecmascript

[!info] 关联笔记

JavaScript 原型链

这个概念为什么出现

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

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

[!abstract] 一句话理解 每个对象有一个内部原型链接;读属性时沿这条链向上查找,直到找到或到达 null

最小可运行示例

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

// 业务意图:所有 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 在链上)。这就是“改实例不会改共享原型方法”的基础。

与 class 的关系

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

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

设计动机

原型共享让方法与默认值可复用,又保持实例数据独立。这是 JS 对象系统的历史与性能折中,而不是 Java 的拷贝式继承。

边界与反直觉

  • for...in 会遍历可枚举的链上属性;Object.keys 只看自身。
  • Object.create(null) 创建无原型对象,适合纯字典,但没有 toString
  • 改内置 prototype 是高风险全局行为。

常见误区

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

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

工程实践

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

本节总结

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

自测题

  1. a.formatStock() 时方法在原型,this 是谁?
  2. 为什么修改 SkuView.prototype.x 会影响已有实例读取?
参考答案
  1. 一般是 a(接收者),由调用形式决定。
  2. 实例通过链接读到同一原型对象;原型被改,查找结果变。

延伸阅读

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