JavaScript - 原型继承
原型继承
经典继承现象涉及创建一个新 class,它实际扩展或重用另一个 class 的属性、函数或方法,这种方式被不同的编程语言(如 C、C++、Java 等)使用。JavaScript 使用原型继承(Prototype Inheritance),而不是经典继承。
原型继承发生在 一个对象通过 prototype 链接访问另一个对象的属性或方法时。所有 JavaScript 对象都从它们的 prototype 派生属性和方法。prototype 是隐藏的对象,从它们的父 class 继承属性和方法。
语法
原型继承语法包含 __proto__ 属性,它允许访问子 prototype。原型继承的语法如下 −
child.__proto__ = parent;
原型继承示例
假设你有一个基本的汽车(一个“prototype”),带有基本部件如轮子和引擎。现在你想创建一个特定的车辆,比如跑车,而不必从零开始。不是创建一辆新车,你使用现有的汽车作为基础(prototype)。跑车继承了基本汽车的特性(轮子和引擎),但也可以升级可选特性,如更大的马力或尾翼。
在 JavaScript 中工作方式相同。一个对象可以从另一个对象(它的 prototype)继承特性和方法;继承的对象仍然可以被扩展。
示例代码
在这种情况下,sportsCar 继承了汽车的特性和方法,同时还包括一个名为 speed 的新特性。
// 基本汽车对象(prototype)
let car = {
wheels: 4,
drive: function() {
console.log("汽车正在行驶");
}
};
// 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);
输出
以上代码的输出结果如下 −
汽车正在行驶 4 200
现在我们可以将 'car' 对象中的任何方法和属性用于 'sportsCar' 对象。本章稍后将讨论对象的特性和方法是如何被继承的。
原型继承的特性
以下列出原型继承的一些特性 −
属性是识别对象的特性,如其状态或特征。
在原型继承的情况下,prototype 的属性由所有从它继承的实例共享。
当在对象上访问属性时,JavaScript 首先在对象中搜索,然后在它的 prototype chain 中搜索。
属性可以包括数据值、数组、对象或其他 JavaScript 数据类型。
属性可以是数据值、数组、对象或任何其他 JavaScript 数据类型。
原型继承的方法
方法是绑定到对象上的函数,可以对其数据执行操作或计算。原型继承允许所有从原型继承的实例共享原型中提供的方法。可以在对象上调用方法,从而访问其属性和其他方法。它可以执行各种任务,例如修改数据、进行计算以及与其他对象或环境通信。
现在让我们在下面的部分讨论一些原型继承的方法 −
Object.create
Object.create 是一个 JavaScript 方法,它从原型及其属性生成一个新对象。它使您能够创建一个从原型继承的对象,而无需构造函数方法。此函数通常用于为对象初始化原型链,因此它允许基于原型的继承。
Object.create 的语法如下所示 −
Object.create(proto, [propertiesObject]);
这里,
- proto 是新创建的对象将继承的原型对象。
- propertiesObject (optional) 是一个定义新创建对象额外属性的对象。这些属性将被添加到新创建的对象中,并替换原型链中同名属性。
示例
以下是一个展示如何使用 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 表示生成对象实例的函数。当使用构造函数函数或 class 创建对象时,constructor 属性会立即设置为原型。
constructor 属性广泛用于验证对象的类型,并通过 JavaScript 的基于原型的继承创建相同类型的新实例。
示例
以下是一个展示如何使用 Object.prototype.constructor 的示例 −
// 用于创建 Animal 对象的构造函数函数
function Animal(species, age) {
this.species = species;
this.age = age;
}
// 创建 Animal 对象的实例
const animal = new Animal('Tiger', 5);
// 访问原型的 constructor 属性
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,
};
// 第 1 级继承
pupil.__proto__ = institution;
// 第 2 级继承
pupil.__proto__.__proto__ = fee;
// 输出 pupil 对象的属性
console.log(pupil.id);
// 输出 institution 对象的属性
console.log(pupil.__proto__.id);
输出
根据层级关系,以下是输出结果 −
1 2
继承方法
原型继承会继承对象的属性及其方法。我们可以在父类中创建一个 function,并在子类中调用它。我们还可以在父类中包含 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__ 属性只能从一个 class 继承。
多重继承只能在不同层级发生。要继承第二个 class,必须使用 __proto__.__proto__ 属性,这会扩展层级并使跟踪更加困难。
原型关系只能使用对象创建。
访问与基类中同名元素的难度较大,即不同 class 中可能存在同名属性,但访问它们很困难。
相同的原型无法继承,因为它们会创建循环。