Prototypal Inheritance
本文最后更新于 2024年3月6日 晚上
Introduction to JavaScript prototypal inheritance
在 OOP 编程范式中,类是创建对象的蓝图。如果希望新类重用现有类的功能,可以创建一个扩展现有类的新类。这称为经典继承。
JavaScript 不使用经典继承。相反,它使用原型继承。
在原型继承中,一个对象通过原型链接从另一个对象“继承”属性。
JavaScript prototypal inheritance and __proto__
下面定义了一个 person 对象:
1 |
|
在此示例中,person 对象有一个属性和一个方法:
name 是存储人名的属性。
greet 是一个以字符串形式返回问候语的方法。
默认情况下,JavaScript 引擎提供内置的 Object() 函数和可由 Object.prototype 引用的匿名对象:
这意味着 person 对象可以调用 Object.prototype 引用的匿名对象中定义的任何方法。
1 |
|
[object Object] 是对象的默认字符串表示形式。
当通过 person 调用 toString() 方法时,JavaScript 引擎无法在 person 对象上找到它。因此,它沿着原型链寻找 Object.prototype 对象中的方法。
要访问 person 对象的原型,可以使用 __proto__
属性,如下所示
1 |
|
注意,永远不应该在生产代码中使用 proto 属性。
下面显示了 person.__proto__
和 Object.prototype 引用了同一个对象:
下面定义了具有 teach() 方法的 teacher 对象:
1 |
|
与 person 对象一样,teacher.__proto__
引用了 Object.prototype,如下图所示:
如果想让 teacher 对象访问 person 对象的所有方法和属性,可以将 teacher 对象的原型设置为 person 对象,如下所示:
1 |
|
现在,teacher 对象可以通过原型链从 person 对象访问 name 属性和 greet() 方法:
1 |
|
当在 teacher 对象上调用 greet() 方法时,JavaScript 引擎首先在 teacher 对象中找到它。
由于 JavaScript 引擎无法在 teacher 对象中找到该方法,因此它沿着原型链在 person 对象中搜索该方法。因为 JavaScript 引擎可以在 person 对象中找到 greet() 方法,所以它会执行该方法。
在 JavaScript 中,我们说 teacher 对象继承了 person 对象的方法和属性。这种继承称为原型继承(prototypal inheritance)。
A standard way to implement prototypal inheritance in ES5
ES5 通过使用 Object.create() 方法提供了一种处理原型继承的标准方法。
注意,现在应该使用较新的 ES6 类和 extends 关键字来实现继承。简单多了。
Object.create() 方法创建一个新对象并使用现有对象作为新对象的原型:
1 |
|
Object.create() 方法接受两个参数:
- 第一个参数 (proto) 是用作新对象的原型对象。
- 第二个参数 (propertiesObject)(如果提供)是一个可选对象,用于为新对象定义其他属性。
假设有一个 person 对象:
1 |
|
以下代码使用 person 对象作为原型对象创建一个空的 teacher 对象:
1 |
|
之后,可以为 teacher 对象定义属性:
1 |
|
或者可以在一条语句中执行所有这些步骤,如下所示:
1 |
|
ES5 还引入了 Object.getPrototypeOf() 方法,该方法返回对象的原型。
1 |
|