.原型链继承
2.构造函数继承
3.组合继承(原型链继承+构造函数继承)
4.原型式继承
5.寄生继承
6.组合寄生继承
代码示例:
//借助构造函数实现继承:缺点是父构造函数的原型链继承不了,若要全部继承除非将所有属性和方法定义在构造函数中
function Parent1 () {
this.name = 'parent1';
}
function Child1 () {
//这么做的好处是定义在父构造函数中的引用类型的属性,对于子构造函数的每个实例来说是独立的
//并且在子构造函数实例化时,可以给父构造函数传参
Parent.call(this);
this.type = 'child1';
}
//借助原型链实现继承:缺点是继承的引用类型属性是共享的,子构造函数的实例更改会影响其他实例上的这个属性,比如 play 属性
function Parent2 () {
this.name = 'parent2';
this.play = [1, 2, 3];
}
function Child2 () {
this.type = 'Child2';
}
Child2.prototype = new Parent2();
//组合方式:缺点是会执行两次父构造函数
function Child3 () {
//执行第一次
Parent2.call(this);
this.type = 'Child3';
}
Child3.prototype = new Parent2(); //执行第二次
//组合优化1,不需要再将定义在父构造函数中的属性和方法再继承一次,只需要继承原型链上的
Child3.prototype = Parent2.prototype;
//缺点是无法区分一个实例是子函构造函数实例化的还是父构造函数实例化的
let s1 = new Child3();
//s1.constructor 指向了 Parent2,而不是 Child3,因为 Child3 原型对象的属性 constructor 继承了 Parent2 原型对象上的
//如果你强行执行 Child3.prototype.constructor = Child3 的话,也会将 Parent2.prototype.constructor 改成 Child3
//组合优化2,通过 Object.create() 创建一个中间对象,将两个原型对象区别开来,并且继承父构造函数的原型链
Child3.prototype = Object.create(Parent2.prototype);
Child3.prototype.constructor = Child3;
//即 Child3.prototype.__proto__ === Parent2.prototype 为 true
//如此 Child3.prototype 和 Parent2.prototype 被隔离开,是两个对象,不会相互影响
//这种方式为理想继承方式