一、原型链继承
核心:将父类的实例作为子类的原型,主要是利用了Dog.prototype = new Animal(),这样Dog.prototype.proto就指向了Animal.prototype,从而将子类Dog的原型和父类Animal的原型相关联
function Animal() {
this.name = 'animal'
this.arr = []
this.eat = function() {
console.log('animal eat')
}
}
Animal.prototype.age = 18
function Dog() {
this.break = function() {
console.log('dog bark');
}
}
// Dog.prototype.__proto__ = Animal.prototype
// Dog.prototype 指向 Animal的实例
Dog.prototype = new Animal();
// h1.__proto__ = Dog.prototype
let h1 = new Dog();
console.log(h1.arr)
h1.arr.push(1)
let h2 = new Dog();
console.log(h2.arr)
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
缺点:
来自父类引用类型的实例属性是所有实例共享的,即属性没有私有化,原型上属性的改变会作用到所有的实例上
二、构造函数继承
核心:在调用子类构造函数时内部使用call或apply来调用父类的构造函数,从而达到了使用父类的的构造函数来增强子类实例的效果,等于是复制父类的实例属性给子类(没有用到原型)
function Super(){
this.flag = true;
}
function Sub(){
Super.call(this) //如果父类可以需要接收参数,这里也可以直接传递
}
var obj = new Sub();
特点:
1. 实现了属性的私有化
2. 创建子类实例时,可以向父类传递参数
3. 可以实现多继承(call多个父类对象)
缺点:
1. 实例并不是父类的实例,只是子类的实例
2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
三、组合继承
核心:结合了构造函数继承和原型链继承
function Super(){
this.flag = true;
}
Super.prototype.getFlag = function(){
return this.flag; // 继承方法
}
function Sub(){
this.subFlag = flase
Super.call(this) // 继承属性
}
Sub.prototype = new Super();
var obj = new Sub();
// Sub.prototype = new Super(); 会导致Sub.prototype的constructor指向Super;
// 然而constructor的定义是要指向原型属性对应的构造函数的,Sub.prototype是Sub构造函数的原型,
// 所以应该添加一句纠正:Sub.prototype.constructor = Sub;
Sub.prototype.constructor = Sub; // 修复构造函数指向
Super.prototype.getSubFlag = function(){
return this.flag;
}