TL;DR

本文详细介绍了 JavaScript 中实现继承的多种方式。首先讨论了三种基本的对象创建模式:工厂模式、构造函数模式和原型模式,每种模式都有其特定的优缺点。

在继承实现方面,文章探讨了六种主要的继承方式:原型链继承、盗用构造函数、组合继承、原型式继承、寄生式继承和寄生式组合继承。每种方法都针对特定的使用场景,解决了不同的问题。

其中,寄生式组合继承被认为是引用类型继承的最佳模式,它避免了组合继承中构造函数被调用两次的问题,同时还保持了原型链的完整性。

在 JavaScript 中,如何创建对象

  1. 工厂模式
  2. 构造函数模式
  3. 原型模式

工厂模式

这种工厂模式虽然可以解决创建多个类似对象的问题,但没有解决对象标识问题(即新创建的对象是什么类型)。

function createPerson(name, age, job) {
	 let o = new Object();
	 o.name = name;
	 o.age = age;
	 o.job = job;
	 o.sayName = function() {
		 console.log(this.name);
	 };
	 return o;
}
let person1 = createPerson("Nicholas", 29, "Software Engineer");
let person2 = createPerson("Greg", 27, "Doctor"); 

构造函数模式

function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function() {
 console.log(this.name);
 };
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg 

构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍;

对此做了改进:

function Person(name, age, job){
 // ...
 this.sayName = **sayName**;
}

**function sayName() {
	console.log(this.name);
}**

// ...

自定义类型引用的代码不能很好地聚集一起。这个新问题可以通过原型模式来解决;

原型模式

使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享

function Person() {}
Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = 'software engineer';
Person.prototype.sayName = function() {
  console.log(this.name);
}

let person1 = new Person();
person1.sayName(); // "Nicholas"
let person2 = new Person();
person2.sayName(); // "Nicholas"
console.log(person1.sayName == person2.sayName); // true