摘要: 本教程将教你如何使用 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); // trueCode 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 DoeCode 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)输出
trueCode language: JavaScript (javascript)总结
- 继承允许对象使用另一个对象的属性和方法,而无需重复代码。
- JavaScript 使用原型继承。