https://www.cnblogs.com/humin/p/4556820.html

1、原型链继承(类式继承)

将父类的实例作为子类的原型,他的特点是实例是子类的实例也是父类的实例,父类新增的原型方法/属性,子类都能够访问,并且原型链继承简单易于实现,缺点是来自原型对象的所有属性被所有实例共享(父类中的共有属性如果是引用类型会共有影响其他子类),无法实现多继承,无法向父类构造函数传参。

//声明父类并且添加共有方法
function SuperClass(){ this.superValue = true; }
SuperClass.prototype.getSuperValue = function(){ return this.superValue; }
//声明子类、继承父类、为子类添加共有方法
function SubClass(){ this.subValue = false; }
SubClass.prototype = new SuperClass();
SubClass.prototype.getSubValue = function(){ return this.subValue; }

//实例化一个子类
var instance = new SubClass();
//子类可以继承父类的方法也可以使用子类新增的方法
console.log(instance.getSuperValue()) //true
console.log(instance.getSubValue()) // false
//实例是子类的实例也是父类的实例
console.log(instance instanceof SuperClass) //true
console.log(instance instanceof SubClass) //true
//子类的原型是一个父类的实例
console.log(SubClass instanceof SuperClass) //false
console.log(SubClass.prototype instanceof SuperClass) //true

//缺点:原型对象所有属性被实例共享
function SuperClass(){ this.books = ['js','css','html'];}
function SubClass(){}
SubClass.prototype = new SuperClass();
var instance1 = new SubClass();
var instance2 = new SubClass();
console.log(instance2.books); //['js','css','html']
instance1.books.push('es6');
console.log(instance2.books); //['js','css','html','es6']

2、构造继承

使用父类的构造函数来增强子类实例,即复制父类的实例属性给子类(没用到原型),
构造继承可以向父类传递参数,可以实现多继承,通过call多个父类对象。但是构造继承只能继承父类的实例属性和方法,不能继承原型属性和方法,无法实现函数复用,每个子类都有父类实例函数的副本,影响性能,实例并不是父类的实例,只是子类的实例。
构造函数继承实际上就是用call将子类的变量在父类中执行一遍,使得子类继承父类的共有属性,但是这其中没有涉及到原型,所以父类的原型方法不会被子类继承,如果想要被继承,就需要将方法放在构造函数中去,这样创建的每个实例都会单独拥有一个方法而不是共有,这样就无法实现代码复用。

//声明父类并且添加共有属性,声明原型方法
function SuperClass(id){ this.books = ['js','css','html']; this.id = id }
SuperClass.prototype.showBooks = function(){ console.log(this.books) }
//声明子类、继承父类
function SubClass(id){ SuperClass.call(this,id); }
//创建子类的实例
var instance1 = new SubClass(10);
var instance2 = new SubClass(11);
instance1.books.push('es6')
// 没有原型链继承里面存在的实例共享属性的问题
console.log(instance1.books); // ['js','css','html','es6']
console.log(instance1.id); //10
console.log(instance2.books); // ['js','css','html']
console.log(instance2.id); //11
//不能继承原型属性和方法
instance1.showBooks(); // TypeError
//实例并不是父类的实例,只是子类的实例
console.log(instance1 instanceof SubClass) //true
console.log(instance1 instanceof SuperClass) //false

3、组合继承

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
将原型链继承(类式继承)和构造函数继承组合在一起将其优点结合在一起构成组合继承,并且过滤掉了两者的缺点!
不完美之处在于构造函数继承和类式继承两次调用了父类的构造函数。

function SuperClass(name){
    //值类型共有属性
    this.name = name;
    //引用类型共有属性
    this.books = ['js','css','html'];
}
//父类原型共有方法
SuperClass.prototype.getName = function(){
    console.log(this.name)
}
//声明子类
function SubClass(name,time){
    //构造函数式继承父类name属性
    SuperClass.call(this,name);
    this.time = time;
}
//类式继承 子类原型继承父类
SubClass.prototype = new SuperClass();
SubClass.prototype.getTime = function (){
    console.log(this.time);
}

var instance1 = new SubClass('前端',2015)
instance1.books.push('es6');
console.log(instance1.books); //['js','css','html','es6']
instance1.getName(); //前端
instance1.getTime(); //2015

var instance2 = new SubClass('网络',2013)
console.log(instance2.books); //['js','css','html']
instance2.getName(); //网络
instance2.getTime(); //2013

4、实例继承

为父类实例添加新特性,作为子类实例返回,实例继承的特点是不限制调用方法,不管是new 子类()还是子类()返回的对象具有相同的效果,缺点是实例是父类的实例,不是子类的实例,不支持多继承

5.原型式继承+改造后的寄生继承

function inheritObject(o){
    //声明一个过度函数对象
    function F(){}
    //过渡对象的原型继承父对象
    F.prototype = o;
    //返回过渡对象的一个实例,该实例的原型继承了父对象
    return new F();
}
//实际上是对类式继承的一个封装,缺点依然存在
var book = {
    name:'js book',
    alikeBook:['css book','html book']
};
var newBook = inheritObject(book)
newBook.name = 'ajax book';
newBook.alikeBook.push('xml book')

var otherBook = inheritObject(book)
otherBook.name = 'flash book';
otherBook.alikeBook.push('as book')

console.log(newBook.name); //ajax book
console.log(newBook.alikeBook); //['css book','html book','xml book']
console.log(otherBook.name); //flash book
console.log(otherBook.alikeBook); //['css book','html book','xml book','as book']
console.log(book.name); //js book
console.log(book.alikeBook); //['css book','html book','xml book','as book']
//跟类式继承一样,父类对象book中的值类型的属性被复制,引用类型的属性被公用

寄生式继承

//声明基对象
var book = {
    name:'js book',
    alikeBook:['css book','html book']
};
function createBook(obj){
    //通过原型继承方式创建新对象
    var o = new inheritObject(obj);
    //拓展新对象
    o.getName = function(){
        console.log(name);
    };
    return o;
}
//寄生式继承是对原型继承的二次封装,在过程中对继承的对象进行了新的拓展,
//新创建的对象不仅有父类的属性和方法而且还添加了新的属性和方法,像寄生虫寄托于某个对象内部

6、寄生组合继承:

通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点, 特点堪称完美但是实现复杂,不是很推荐。

//相比组合继承就是在子类原型继承父类原型这一个地方
//取代 SubClass.prototype = new SuperClass();
inheritPrototype(SubClass,SuperClass);

function inheritPrototype(subClass,superClass){
    //复制一份父类的原型副本保存在变量中
    var p =inheritObject(superClass.prototype);
    //修正因为重写子类原型导致子类的constructor属性被修改
    p.constructor = subClass;
    //设置子类的原型
    subClass.prototype = p;
}

图片说明

7、拷贝继承:

特点:支持多继承,缺点:效率较低,内存占用高(因为要拷贝父类的属性)无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

多继承的实现

js只有一条原型链,理论上无法实现一个子类继承多个父类,但是可以利用复制拷贝的方法对多个对象属性进行继承

//将实现多继承的方法绑定在原生对象Object上,所有对象拥有这个方法
Object.prototype.mix = function(){
    var len = arguments.length;
    var arg;
    for(var i=0;i<len;i++){
        //缓存当前对象
        arg = arguments[i];
        //遍历被继承对象中的属性
        for(var property in arg){
            //将被继承对象中的属性复制到目标对象中
            this[property] = arg[property]
        }
    }
}