Prototypal Inheritance

本文最后更新于 2024年3月6日 晚上

Introduction to JavaScript prototypal inheritance

在 OOP 编程范式中,类是创建对象的蓝图。如果希望新类重用现有类的功能,可以创建一个扩展现有类的新类。这称为经典继承。

JavaScript 不使用经典继承。相反,它使用原型继承。

在原型继承中,一个对象通过原型链接从另一个对象“继承”属性。

JavaScript prototypal inheritance and __proto__

下面定义了一个 person 对象:

1
2
3
4
5
6
let person = {
name: 'John Doe',
greet: function () {
return "Hi, I'm " + this.name
},
}

在此示例中,person 对象有一个属性和一个方法:

  • name 是存储人名的属性。

  • greet 是一个以字符串形式返回问候语的方法。

默认情况下,JavaScript 引擎提供内置的 Object() 函数和可由 Object.prototype 引用的匿名对象:

JavaScript Prototype

这意味着 person 对象可以调用 Object.prototype 引用的匿名对象中定义的任何方法。

1
2
console.log(person.toString());
[object Object]

[object Object] 是对象的默认字符串表示形式。

当通过 person 调用 toString() 方法时,JavaScript 引擎无法在 person 对象上找到它。因此,它沿着原型链寻找 Object.prototype 对象中的方法。

要访问 person 对象的原型,可以使用 __proto__ 属性,如下所示

1
console.log(person.__proto__)

注意,永远不应该在生产代码中使用 proto 属性。

下面显示了 person.__proto__ 和 Object.prototype 引用了同一个对象:

下面定义了具有 teach() 方法的 teacher 对象:

1
2
3
4
5
let teacher = {
teach: function (subject) {
return 'I can teach ' + subject
},
}

与 person 对象一样,teacher.__proto__ 引用了 Object.prototype,如下图所示:

img

如果想让 teacher 对象访问 person 对象的所有方法和属性,可以将 teacher 对象的原型设置为 person 对象,如下所示:

1
teacher.__proto__ = person

img

现在,teacher 对象可以通过原型链从 person 对象访问 name 属性和 greet() 方法:

1
2
console.log(teacher.name) // John Doe
console.log(teacher.greet()) // Hi, I'm John Doe

当在 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])

Object.create() 方法接受两个参数:

  • 第一个参数 (proto) 是用作新对象的原型对象。
  • 第二个参数 (propertiesObject)(如果提供)是一个可选对象,用于为新对象定义其他属性。

假设有一个 person 对象:

1
2
3
4
5
6
let person = {
name: 'John Doe',
greet: function () {
return "Hi, I'm " + this.name
},
}

以下代码使用 person 对象作为原型对象创建一个空的 teacher 对象:

1
let teacher = Object.create(person)

之后,可以为 teacher 对象定义属性:

1
2
3
4
teacher.name = 'Jane Doe'
teacher.teach = function (subject) {
return 'I can teach ' + subject
}

或者可以在一条语句中执行所有这些步骤,如下所示:

1
2
3
4
5
6
7
8
let teacher = Object.create(person, {
name: { value: 'John Doe' },
teach: {
value: function (subject) {
return 'I can teach ' + subject
},
},
})

ES5 还引入了 Object.getPrototypeOf() 方法,该方法返回对象的原型。

1
console.log(Object.getPrototypeOf(teacher) === person) // true

Prototypal Inheritance
https://stein283036.github.io/2024/03/06/Prototypal-Inheritance/
作者
倪京龙
发布于
2024年3月6日
更新于
2024年3月6日
许可协议