原型和原型链

对象

在 JavaScript 中,对象是无序属性的集合,其属性可以包含基本值、对象或者函数(方法)。你可以将对象简单地看作是一个包含键值对(Key-Value pairs)的容器。

const user = {
    name: "Alice",
    age: 25,
    greet: function() {
        console.log("Hello, I'm " + this.name);
    }
};
// 访问对象属性
console.log(user.name); // "Alice"
user.greet(); // "Hello, I'm Alice"

原型

每个 JavaScript 对象(除了 null)在创建时,都会与另一个对象自动关联。这个被关联的对象就被称为原型。对象可以从它的原型上“继承”属性和方法。

原型涉及两个容易混淆的概念:

  • __proto__ (隐式原型):这是每一个对象都有的内部属性(在规范中称为 [[Prototype]]),它指向该对象的原型。
  • prototype (显式原型):这是只有函数才拥有的属性。当你使用 new 关键字通过构造函数创建实例时,实例对象的 __proto__ 会自动指向构造函数的 prototype
// 构造函数
function Person(name) {
    this.name = name; // 定义在实例上的属性
}

// 在构造函数的 prototype 对象上添加方法
Person.prototype.sayHello = function() {
    console.log("Hi!");
};

const alice = new Person("Alice");

// 实例的隐式原型指向构造函数的显式原型
console.log(alice.__proto__ === Person.prototype); // true

原型链

原型链是 JavaScript 实现继承的底层机制。

当你在一个对象上访问某个属性或方法时,JavaScript 引擎会执行以下查找过程:

  1. 首先,在对象自身的属性中查找。如果找到,就直接使用。
  2. 如果没有找到,引擎会顺着对象的 __proto__ 属性,去它的原型对象中查找。
  3. 如果原型对象中依然没有,就继续去原型的原型中查找。
  4. 以此类推,顺藤摸瓜,直到找到该属性,或者到达原型链的顶端(Object.prototype.__proto__,它的值是 null)。
  5. 如果到了 null 还没找到,就返回 undefined

这种由 __proto__ 属性将各个对象串联起来的链式结构,就是原型链