在面向对象的语言中,类的继承最重要的特征之一。本文通过讨论JS中原型链继承法、借用构造函数继承法和寄生组合式继承法的优缺点,来逐步推导出类继承最为成熟的方案
类(构造函数)
在JavaScript中,若一个函数总是被当做构造函数来使用,那么我们就习惯称该函数为构造函数
有很多从面向对象语言过来的开发者,也习惯称之为类。
如何实现继承
已知类Person和类Student有如下属性和原型方法,如何实现Student类继承Person类
继承方案一:原型链
若要实现Student类继承自Person类,让构造的实例对象stu中也有Person中的属性name、friends等,就要让name,age等在stu的原型链上,即Student类的原型链上,因此有了原型链方式的继承。
注意:Person类本身没有name,age的属性,类构造的实例对象per上才有
代码实现
function Person(name, friends){
this.name = name;
this.friends = friends;
}
Person.prototype.eating = function() {
console.log(this.name + ' is eating')
}
function Student(name, age, id){
this.id = id;
}
// 🔻原型
Student.prototype = new Person()
Student.prototype.studying = function() {
console.log(this.name + ' is studying ')
}
弊端
- 弊端1:打印stu对象,某些属性看不到
- 弊端2: 无法传递参数
- 弊端3:多个stu对象共用一个friends name属性,不合理
继承方案二:原型链+借用构造函数
用原型链的方法的缺点是将父类某些属性挂载到父类的实例上,而不是子类的实例上。如果想让这些属性出现在子类的实例上,需要在子类构造函数内部借用父类的代码
代码实例
function Person(name, friends){
this.name = name;
this.friends = friends;
}
Person.prototype.eating = function() {
console.log(this.name + ' is eating')
}
function Student(name, friends, id){
// 🔻构造函数借用
Person.call(this, name, friends);
this.id = id;
}
Student.prototype = new Person()
Student.prototype.studying = function() {
console.log(this.name + ' is studying ')
}
弊端
- 弊端1:父类构造函数被调用了多次(每次创建对象都要调用一次,最初该原型链也要调用一次
- 弊端2 :stu的原型对象会多出部分属性,没必要存在
继承方案三:寄生组合式👍
寄生组合式在方案二:原型链+借用构造函数的基础上,更改了“原型链”部分,构造一个新对象,该对象的原型指向Person的原型对象,来替换之前new Person创建出来的实例对象
function Person(name, friends){
this.name = name;
this.friends = friends;
}
Person.prototype.eating = function() {
console.log(this.name + ' is eating')
}
function Student(name, friends, id){
// 🔻借用父类构造函数
Person.call(this, name, friends);
this.id = id;
}
// 🔻构造的新对象
Student.prototype = Object.create(Person.prototype)
// 添加constructor属性
Object.defineProperty(Student.prototype, "constructor", {
value: Student,
enumerable: false,
writable: true,
configurable: true,
});
Student.prototype.studying = function() {
console.log(this.name + ' is studying ')
}