JavaScript 的秘密生活:理解原型

发布: (2025年12月11日 GMT+8 12:07)
4 min read
原文: Dev.to

Source: Dev.to

对象是委托的,它们不复制

Timothy 带着笔记本电脑和困惑的表情来到图书馆。他一直在阅读 JavaScript 继承的内容,却发现有些地方说不通。

Timothy: “我学到 JavaScript 使用原型(prototype)实现继承。但我也知道 JavaScript 现在有类(class)了。那么到底是哪一种?JavaScript 用原型还是类?”
Margaret: “两者都有,也都不是。情况比这更微妙。”

什么是继承?

Timothy: “子类从父类继承时,会得到父类的所有属性和方法。”
Margaret: “在 Python、Java 等语言里是这样的。子类会复制或包含父类的行为。而 JavaScript 则使用 委托(delegation)。”

委托 vs. 复制

对象不是把属性复制下来,而是指向其他对象并说:“如果我这里没有你要的,就去那个对象那儿找。”

简单示例

const parent = {
  greet: function() {
    console.log("Hello, " + this.name);
  }
};

const child = Object.create(parent);
child.name = "Alice";

child.greet(); // "Hello, Alice"
  • Object.create(parent) 创建了一个新对象,其隐藏的 [[Prototype]] 链接指向 parent
  • 当调用 child.greet() 时,JavaScript 会先在 child 上查找 greet。未找到 → 再去 child 的原型(parent)上查找。找到后以 childthis 调用它。

多个对象共享同一原型

const parent = {
  greet: function() {
    console.log("Hello, " + this.name);
  }
};

const alice = Object.create(parent);
alice.name = "Alice";

const bob = Object.create(parent);
bob.name = "Bob";

alice.greet(); // "Hello, Alice"
bob.greet();   // "Hello, Bob"

alicebob 都委托给同一个 parent 对象,但 greet 中的 this 指向调用者。

[[Prototype]] vs. .prototype

隐藏的链接 [[Prototype]]

const obj = Object.create(parent);
// obj.[[Prototype]] points to parent (invisible)

函数上的 .prototype 属性

function User(name) {
  this.name = name;
}

// User.prototype is a regular property on the User function
console.log(User.prototype); // { constructor: User }

使用 new

function User(name) {
  this.name = name;
}

User.prototype.greet = function() {
  console.log("Hello, " + this.name);
};

const alice = new User("Alice");
alice.greet(); // "Hello, Alice"

当执行 new User("Alice") 时,JavaScript 会:

  1. 创建一个全新的对象。
  2. 将该对象的 [[Prototype]] 设置为 User.prototype
  3. 用新对象作为 this 调用 User 构造函数。
  4. 返回这个新对象。

你可以验证这层链接:

console.log(Object.getPrototypeOf(alice) === User.prototype); // true

Object.getPrototypeOf()(或旧式的 __proto__)可以显示看不见的原型链。

类:原型的语法糖

前面例子的类写法

class User {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log("Hello, " + this.name);
  }
}

const alice = new User("Alice");
alice.greet(); // "Hello, Alice"
  • 类语法在内部创建了相同的原型链。
  • 在类内部定义的方法(greet)会放到 User.prototype 上。
  • new User() 仍然会创建一个对象,其 [[Prototype]] 指向 User.prototype

为什么叫“语法糖”?

这个术语指的是语法看起来不同且通常更清晰,但底层机制——原型委托——保持不变。类还提供了 extendssuper() 等便利关键字,帮助你处理原型链,但它们并没有引入全新的继承模型。

小结

  • JavaScript 对象通过隐藏的 [[Prototype]] 链接 委托 给其他对象。
  • 构造函数上的 .prototype 属性是为使用 new 创建的对象设置这条隐藏链接的一种方式。
  • Object.getPrototypeOf()(或 __proto__)可以让你检查原型链。
  • ES6 类只是写同样基于原型的模式的更简洁方式;它们并没有取代原型。
Back to Blog

相关文章

阅读更多 »