JavaScript 原型继承

摘要: 本教程将教你如何使用 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 来引用。

JavaScript Prototype

请注意,圆圈代表函数,而正方形代表对象。

person 对象与 Object() 函数引用的匿名对象之间存在链接。[[Prototype]] 代表链接

JavaScript prototypal inheritance

这意味着 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 classextends 关键字来实现继承。它要简单得多。

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 使用原型继承。
本教程对您有帮助吗?