JavaScript 原型继承怎么用?原型链实现方式有哪些?

文章导读
Previous Quiz Next 原型继承 经典继承现象涉及创建一个新 class,它实际扩展或重用另一个 class 的属性、函数或方法,这种方式被不同的编程语言(如 C、C++、Java 等)使用。JavaScript 使用原型继承(Prototype Inheri
📋 目录
  1. 原型继承
  2. 原型继承示例
  3. 原型继承的特性
  4. 原型继承的方法
  5. 原型链
  6. 继承方法
  7. 原型继承的缺点
A A

JavaScript - 原型继承



Previous
Quiz
Next

原型继承

经典继承现象涉及创建一个新 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 中可能存在同名属性,但访问它们很困难。

  • 相同的原型无法继承,因为它们会创建循环。