js5种继承方式
- 原型链
- 构造函数
- 组合式
- 组合寄生式
- ES6提供的class extents
从前到后,缺点越来越少,越来越优化
一、原型链继承
实现方式
将子类原型设置为父类对象的实例
优点:
- 简单、易实现
缺点:
- 全为静态属性,所有父类相关属性都在原型上,某个实例对象无法创建在父类中定义的自己专属的属性
- 创建子类对象时,无法向父类构造函数传参,因为一开始就确定了原型
- 一旦某个子类实例修改了静态属性,则所有实例均被修改,因为共享同一个原型
总结
js继承中最简单的一种实现方式,但是有缺陷。
function Person(name){
this.name = name;
}
Person.prototype.hasname=function(){
console.log("My name is ", this.name)
}
function Teacher(){};
Teacher.prototype=new Person()
Teacher.prototype.constructor = Teacher;
二、构造函数继承
实现方式:
在子类构造函数中调用一次父类构造函数
优点:
- 可向父类传参
- 某个实例修改属性不会影响其他实例
缺点:
- 无静态属性,父类构造函数原型上的内容无法继承
- 属性或方法,想要被继承,只能在父类构造函数中定义
- 每创建一个新实例对象,对应继承属性都会被创建一次
总结
解决了原型链继承存在的问题,但带来了新的问题。
function Person(name){
this.name = name;
}
function Teacher(book, name){
Person.call(this, Array.prototype.slice.call(arguments, 1))
this.book=book
};
三、组合继承
实现方式
将构造函数继承和原型链继承组合起来
优点
既解决了原型链继承全为静态属性的问题,又解决了构造函数继承无静态属性的问题
缺点
产生了副作用,各个实例对象上的属性虽然相互独立,但其原型链上还有额外同名属性
总结
组合继承的本质上是组合使用构造函数继承和原型链继承,将原型链继承中的静态属性或方法通过构造函数继承重写一遍,因此会造成对象属性和原型链上静态属性重复
function Person(name){
this.name = name;
}
Person.prototype.hasname=function(){
console.log("My name is ", this.name)
}
function Teacher(book,name){
Person.call(this, Array.prototype.slice.call(arguments, 1))
this.book=book
};
Teacher.prototype=new Person()
Teacher.prototype.constructor = Teacher;
四、组合寄生式继承
实现方式
在组合继承基础上,将子类原型指向由原本的父类实例,变为了父类原型的复制对象,采用复制对象是为了避免父类实例操作影响子类实例
优点
解决了上述所有问题
缺点
书写复杂
总结
组合寄生式继承解决了前三种继承方式的问题,是ES5及以前旧版标准下较为完美的继承方案
function Person(name){
this.name = name;
}
Person.prototype.hasname=function(){
console.log("My name is ", this.name)
}
function Teacher(book,name){
Person.call(this, Array.prototype.slice.call(arguments, 1))
this.book=book
};
Teacher.prototype = Object.creat(Person.prototype)
Teacher.prototype.constructor = Teacher;
五、ES6 class继承方案
实现方式
class是ES6提供的语法糖,本质上就是组合寄生式继承。相比于组合寄生式继承,多了对键的处理,详见后续代码。是ES6+下的更完美的继承方案
优点
- 拥有组合寄生式继承继承的优点
- 书写清晰易懂
class Person{
constructor (name){
this.name = name;
}
function hasname(){
console.log("My name is ", this.name)
}
}
class Teacher extends Person{
constructor (book,name){
super(name)
this.book = book;
}
}
// 内部实现相比组合寄生式继承额外多做了如下操作
// for(let k in Person){
// if(Person.hasOwnProperty(k) && !(k in Teacher)){
// Teacher[k] = Person[k]
// }
// }