JavaScript - 原型继承
原型继承
经典继承现象涉及创建一个新类,该新类实际上扩展或重用了其他编程语言(如 C、C++、Java 等)所使用的另一个类的属性、函数或方法。JavaScript 使用原型继承,而非经典继承。
当一个对象使用原型链接访问另一个对象的属性或方法时,就会发生原型继承。所有 JavaScript 对象都从其原型派生属性和方法。原型是从其父类继承属性和方法的隐藏对象。
语法
原型继承语法包含 __proto__ 属性,该属性允许访问子原型。原型继承的语法如下:
child.__proto__ = parent;
原型继承示例
假设您有一辆基础车型("原型"),其中包含车轮和发动机等基本部件。现在,您想创建一辆特定的车辆,例如跑车,但又不想从头开始。您无需创建新车辆,而是以现有车辆为基础(原型)。跑车继承了基础车型的功能(车轮和发动机),但也可以升级为可选功能,例如更大的马力或扰流板。
在 JavaScript 中也是如此。一个对象可以从另一个对象(其原型)继承特性和方法;继承的对象仍然可以扩展。
示例代码
在本例中,sportsCar 继承了汽车的特性和方法,同时还包含一个名为 speed 的新功能。
// 基本汽车对象(原型)
let car = {
wheels: 4,
drive: function() {
console.log("The car is driving");
}
};
// SportsCar 对象
let sportsCar = {
speed: 200,
};
// SportsCar 对象继承自 car 对象
sportsCar.__proto__ = car;
console.log(sportsCar);
// 使用 sportsCar 调用 car 对象的方法
sportsCar.drive();
// 继承自 car
console.log(sportsCar.wheels);
// sportsCar 中的新属性
console.log(sportsCar.speed);
输出
以上代码的结果如下 -
The car is driving 4 200
我们现在可以使用从"car"对象到"sportsCar"对象的任何方法和属性。在本章后面,我们将讨论对象的特性和方法是如何被继承的。
原型继承的属性
以下列出了原型继承的一些属性 -
属性是标识对象的特征,例如其状态或特性。
在原型继承的情况下,原型的属性由所有继承自它的实例共享。
当访问对象的属性时,JavaScript 首先在对象中搜索该属性,然后在其原型链中搜索。
属性可以包括数据值、数组、对象或其他 JavaScript 数据类型。
属性可以是数据值、数组、对象或任何其他 JavaScript 数据类型。
原型继承的方法
方法是绑定到对象并可对其数据执行操作或计算的函数。原型继承允许所有从原型继承的实例共享原型提供的方法。对象上的方法可以被调用,从而可以访问其属性和其他方法。它可以执行各种任务,例如修改数据、进行计算以及与其他对象或环境通信。
现在,我们将在下一节讨论一些原型继承的方法 -
Object.create
Object.create 是一个 JavaScript 方法,它根据原型及其属性生成一个新对象。它使您能够创建从原型继承的对象,而无需构造函数方法。此函数通常用于初始化对象的原型链,因此它允许基于原型的继承。
Object.create 的语法如下 -
Object.create(proto, [propertiesObject]);
这里,
- proto 是新创建的对象将从其继承的原型对象。
- propertiesObject(可选) 是一个为新创建的对象定义附加属性的对象。这些属性将添加到新创建的对象中,并替换原型链中同名的属性。
示例
以下示例演示如何使用 Object.create 方法 -
// 创建原型对象
const animalPrototype = {
describe: function() {
console.log(`This is a ${this.species}, and it is ${this.age} years old.`);
}
};
// 创建一个继承自 animalPrototype 的新对象
const Tiger = Object.create(animalPrototype);
tiger.species = 'Tiger';
tiger.age = 5;
// 调用 Tiger 对象的 describe 方法
tiger.describe();
输出
这将生成以下结果 -
This is a Tiger, and it is 5 years old.
Object.prototype.constructor
Object.prototype.constructor 属性表示生成对象实例的函数。当使用构造函数或类创建对象时,constructor 属性会立即设置为原型。
constructor 属性广泛用于验证对象的类型,并通过 JavaScript 基于原型的继承创建相同类型的新实例。
示例
以下是如何使用 Object.prototype.constructor 的示例 -
// 用于创建 Animal 对象的构造函数
function Animal(species, age) {
this.species = species;
this.age = age;
}
// Creating an instance of the Animal object
const animal = new Animal('Tiger', 5);
// 访问原型的构造函数属性
console.log(animal.constructor);
输出
这将产生以下结果 -
Animal(species, age) { this.species = species; this.age = age; }
hasOwnProperty
在 JavaScript 中,hasOwnProperty 函数用于判断对象是否具有给定属性,而不是从其原型链继承该属性。它返回一个布尔值,指示对象是否包含给定的属性。
hasOwnProperty 函数在 JavaScript 中被广泛使用,用于区分对象自身的属性和从其原型链继承的属性,以确保被检查的属性直接存在于对象上。
语法
hasOwnProperty 的语法如下 -
object.hasOwnProperty(propertyName)
这里,
object 是应用 hasOwnProperty 方法的对象。
PropertyName 是对象中要查找的属性的名称。
示例
以下是使用 hasOwnProperty 方法的示例 -
const animal = {
species: 'Lion',
habitat: 'Grasslands'
};
console.log(animal.hasOwnProperty('species'));
console.log(animal.hasOwnProperty('habitat'));
console.log(animal.hasOwnProperty('diet'));
输出
这将产生以下结果 -
true true false
原型链
原型链用于表示不同层次的多重继承。我们可以使用以下步骤将一个原型连接到另一个原型。
示例
以下是 JavaScript 中原型链的示例 -
let pupil = {
id: 1,
};
let fee = {
id: 2,
};
let institution = {
id: 3,
};
// 一级继承
pupil.__proto__ = Institution;
// 二级继承
pupil.__proto__.__proto__ = Fee;
// 输出 pupil 对象的属性
console.log(pupil.id);
// 输出 Institution 对象的属性
console.log(pupil.__proto__.id);
输出
以下是基于层次结构的输出 -
1 2
继承方法
原型继承会同时继承对象的属性和方法。我们可以在父类中创建一个函数,并在子类中调用它。我们还可以在父类中包含 getter 和 setter 方法,以供子类使用。
示例
以下示例演示了如何继承方法 -
let userBase = {
// 父对象
canRead: true,
profession: "",
showReadPermission: function () {
console.log(this.canRead);
},
// 设置用户职业的 Setter 方法
set info(value) {
this.profession = value;
},
// 获取职业详情的 Getter 方法
get info() {
return `${this.profession}`;
},
};
let writer = {
// 子对象
canWrite: true,
};
writer.__proto__ = userBase;
// 调用父函数
writer.showReadPermission();
// 调用 setter 方法
writer.info = "blogger";
// 调用 getter 方法
console.log(writer.info);
输出
这将产生以下结果 -
true blogger
原型继承的缺点
以下是使用原型继承时应考虑的缺点列表 -
它限制了灵活性,因为单个 __proto__ 属性只能从一个类继承。
多重继承只能发生在不同的级别。要继承第二个类,我们必须使用 __proto__.__proto__ 属性,这会扩展层次结构并使跟踪更加困难。
原型关系只能使用对象创建。
访问与基类中元素同名的元素很困难,也就是说,同名的属性可能存在于不同的类中,但很难访问。
相同的原型无法被继承,因为它们会形成循环。

