摘要: 本教程将教你如何使用 JavaScript 原型继承。
JavaScript 原型继承简介
如果你使用过其他面向对象编程语言,例如 Java 或 C++,你应该已经熟悉继承的概念。
在这个编程范式中,类是创建对象的蓝图。如果你想让一个新类复用现有类的功能,你可以创建一个扩展现有类的类。这被称为经典继承。
JavaScript 不使用经典继承。相反,它使用原型继承。
在原型继承中,一个对象通过原型链接从另一个对象“继承”属性。
JavaScript 原型继承和 __proto__
让我们用一个例子来说明这个概念。
以下定义了一个 person
对象
let person = {
name: "John Doe",
greet: function () {
return "Hi, I'm " + this.name;
}
};
Code language: JavaScript (javascript)
在这个例子中,person
对象有一个属性和一个方法
name
是一个存储人名的属性。greet
是一个返回问候语的方法。
默认情况下,JavaScript 引擎会为你提供一个内置的 Object()
函数和一个匿名对象,可以使用 Object.prototype
来引用。
请注意,圆圈代表函数,而正方形代表对象。
person 对象与 Object()
函数引用的匿名对象之间存在链接。[[Prototype]]
代表链接
这意味着 person
对象可以调用在 Object.prototype
引用的匿名对象中定义的任何方法。
例如,以下是如何通过 person
对象调用 toString()
方法
console.log(person.toString());
Code language: JavaScript (javascript)
输出
[object Object]
Code language: JavaScript (javascript)
[object Object]
是对象的默认字符串表示。
当你通过 person
调用 toString()
方法时,JavaScript 引擎无法在 person
对象上找到它。因此,它会跟随原型链,并在 Object.prototype
对象中搜索该方法。
由于 JavaScript 引擎可以在 Object.prototype
对象中找到 toString()
方法,因此它会执行 toString()
方法。
要访问 person
对象的原型,可以使用 __proto__
属性,如下所示
console.log(person.__proto__);
Code language: JavaScript (javascript)
请注意,你永远不应该在生产代码中使用 __proto__
属性。请仅将其用于演示目的。
以下显示 person.__proto__
和 Object.prototype
引用同一个对象
console.log(person.__proto__ === Object.prototype); // true
Code language: JavaScript (javascript)
以下定义了 teacher
对象,它具有 teach()
方法
let teacher = {
teach: function (subject) {
return "I can teach " + subject;
}
};
Code language: JavaScript (javascript)
与 person
对象一样,teacher.__proto__
引用 Object.prototype
,如以下图片所示
如果你想让 teacher
对象访问 person
对象的所有方法和属性,可以将 teacher
对象的原型设置为 person
对象,如下所示
teacher.__proto__ = person;
Code language: JavaScript (javascript)
现在,teacher
对象可以通过原型链访问 person
对象的 name
属性和 greet()
方法
console.log(teacher.name);
console.log(teacher.greet());
Code language: JavaScript (javascript)
输出
John Doe
Hi, I'm John Doe
Code language: JavaScript (javascript)
当你调用 teacher
对象上的 greet()
方法时,JavaScript 引擎会首先在 teacher
对象中找到它。
由于 JavaScript 引擎无法在 teacher
对象中找到该方法,因此它会跟随原型链并在 person
对象中搜索该方法。由于 JavaScript 引擎可以在 person
对象中找到 greet()
方法,因此它会执行该方法。
在 JavaScript 中,我们说 teacher
对象继承了 person
对象的方法和属性。这种继承被称为原型继承。
在 ES5 中实现原型继承的标准方法
ES5 提供了一种使用 Object.create()
方法来处理原型继承的标准方法。
请注意,现在你应该使用更新的 ES6 class
和 extends
关键字来实现继承。它要简单得多。
Object.create()
方法创建一个新对象,并使用现有对象作为新对象的原型
Object.create(proto, [propertiesObject])
Code language: JavaScript (javascript)
Object.create()
方法接受两个参数
- 第一个参数 (
proto
) 是用作新对象原型的对象。 - 第二个参数 (
propertiesObject
),如果提供,是一个可选对象,它定义了新对象的附加属性。
假设你有一个 person
对象
let person = {
name: "John Doe",
greet: function () {
return "Hi, I'm " + this.name;
}
};
Code language: JavaScript (javascript)
以下创建了一个空的 teacher
对象,其 __proto__
是 person
对象
let teacher = Object.create(person);
Code language: JavaScript (javascript)
之后,你可以为 teacher
对象定义属性
teacher.name = 'Jane Doe';
teacher.teach = function (subject) {
return "I can teach " + subject;
}
Code language: JavaScript (javascript)
或者你可以将所有这些步骤组合在一个语句中,如下所示
let teacher = Object.create(person, {
name: { value: 'John Doe' } ,
teach: { value: function(subject) {
return "I can teach " + subject;
}}
});
Code language: JavaScript (javascript)
ES5 还引入了 Object.getPrototypeOf()
方法,该方法返回对象的原型。例如
console.log(Object.getPrototypeOf(teacher) === person);
Code language: JavaScript (javascript)
输出
true
Code language: JavaScript (javascript)
总结
- 继承允许对象使用另一个对象的属性和方法,而无需重复代码。
- JavaScript 使用原型继承。