1、原型链继承,将父类的实例作为子类的原型,他的特点是实例是子类的实例也是父类的实例,父类新增的原型方法/属性,子类都能够访问,并且原型链继承简单易于实现,缺点是来自原型对象的所有属性被所有实例共享,无法实现多继承,无法向父类构造函数传参。
2、构造继承,使用父类的构造函数来增强子类实例,即复制父类的实例属性给子类,
构造继承可以向父类传递参数,可以实现多继承,通过call多个父类对象。但是构造继承只能继承父类的实例属性和方法,不能继承原型属性和方法,无法实现函数服用,每个子类都有父类实例函数的副本,影响性能
3、实例继承,为父类实例添加新特性,作为子类实例返回,实例继承的特点是不限制调用方法,不管是new 子类()还是子类()返回的对象具有相同的效果,缺点是实例是父类的实例,不是子类的实例,不支持多继承
4、拷贝继承:特点:支持多继承,缺点:效率较低,内存占用高(因为要拷贝父类的属性)无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
5、组合继承:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
6、寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
7、ES6中的Class继承
一、原型链继承
原型链继承的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
父类的实例作为子类的原型 所有子类共享父类的的属性(飞机大战)
// 原型链继承 父类的实例作为子类的原型
function SuperType(){
this.name='jack';
this.colors=['pink','blue','green']
}
SuperType.prototype.getName=function (){
return this.name;
}
function SubType(){
this.age=22;
}
SubType.prototype=new SuperType() //父类的实例作为子类的原型
SubType.prototype.getAge=function (){
return this.age;
}
console.log(SubType.prototype.constructor==SuperType) //true 构造函数的原型对象的constructor指向构造函数
//实例1
let instansel =new SubType(); //instansel是SubType的实例
instansel.colors.push('yellow');
console.log(instansel.getName()); //jack
console.log(instansel.colors); //["pink", "blue", "green", "yellow"]
console.log(instansel.getAge()); //22
//实例2
let instance2=new SubType();
console.log(instance2.colors) //(4) ["pink", "blue", "green", "yellow"]
二、借用构造函数
在子类型的构造函数中调用父类型构造函数。
function SuperType(name){
this.name=name;
this.colors=['pink','blue','green'];
}
function SubType(name){
SuperType.call(this,name)
}
let instancel=new SubType("jack")
instancel.colors.push('yellow');
console.log(instancel.name);
console.log(instancel.colors);
let instancel2=new SubType("jack");
console.log(instancel2.name);
console.log(instancel2.colors)
优点:
- 可以向超类传递参数
- 解决了原型中包含引用类型值被所有实例共享的问题
缺点:
- 方法都在构造函数中定义,函数复用无从谈起,另外超类型原型中定义的方法对于子类型而言都是不可见的。
三、组合继承(原型链 + 借用构造函数)
组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥二者之长的一种继承模式。基本思路:
使用原型链实现对(父类)原型属性和方法的继承,通过借用构造函数来实现对实例(父类)属性的继承,既通过在原型上定义方法来实现了函数复用,又保证了每个实例都有自己的属性。
function SuperType(name){
this.name=name;
this.colors=['pink','blue','green'];
}
SuperType.prototype.sayName=function(){
console.log(this.name)
}
function SuberType(name,age){
//继承属性
SuperType.call(this,name); //在子类型的构造函数中调用超类型构造函数。
this.age=age;
}
//继承方法
SuberType.prototype=new SuperType();
SuberType.prototype.constructor=SuberType;
SuberType.prototype.sayAge=function (){
console.log(this.age)
}
let instancel=new SuberType('jack',20);
instancel.colors.push('yellow');
console.log(instancel.colors); //["pink", "blue", "green", "yellow"]
instancel.sayName(); //jack
instancel.sayAge(); //20
let instancel2=new SuberType('alice',22);
console.log(instancel2.colors); // ["pink", "blue", "green"]
instancel2.sayName() //alice
instancel2.sayAge(); //22
四、原型式继承
原型继承的基本思想:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(o){
function F(){} //临时空的构造函数
F.prototype=o; //传入的参数对象最为构造函数的原型
return new F(); //返回临时类型的一个新实例
}
在 object()
函数内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例,从本质上讲,object()
对传入的对象执行了一次浅拷贝。
缺点:
同原型链实现继承一样,包含引用类型值的属性会被所有实例共享。
五、Class继承
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
class
实现继承的核心在于使用 extends
表明继承自哪个父类,并且在子类构造函数中必须调用 super
,因为这段代码可以看成 Parent.call(this, value)
。