继承的七种方式
1. 原型链继承
- 原理:
即将子类构造函数所对应的的原型对象即 prototype 这个对象(即是一个引用)指向
改变,将其指向改为指向父类创建的实例(因为创建的实例中有一个属性[[prototype]]实际上在现代浏览器上已经实现即__proto__)
是指向父类构造函数对应的原型对象。实现原型链的继承。 - 实现代码
SubType.prototype = new super(); SubType.prototype.constructor = SubType;
- 缺点:
实现继承了父类的原型对象上定义的属性和方法,而使用子类创建的实例都共享这一块数据。
2 . 构造函数继承
- 原理:
在子类的构造函数中调用父类的构造函数实现构造函数的继承 - 实现
在子类的构造函数中调用父类构造函数实现继承父类属性SuperType.call(this);
- 缺点
虽然实现了属性的继承,但是方法不是共享的,没创建一个子类实例就单独创建了一个方法,没有实现
方法之间的共享。
3 . 组合继承
- 原理
采用构造函数继承实现构造函数的继承(即实现父类构造函数的继承),另改变原型链的指向使原型执行指向父类的原型对象实现方法即共享属性的继承实现。 - 实现
//首先在子类的构造函数中调用父类的构造函数 SuperType.call(this); //其次在外面实现原型链 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType;
- 要点
最为经典的继承方式实现了完全的继承,但是存在父类的构造函数调用了两次的现象,即一次是在原型指向父类的实例对象时调用了一次父类,另一次是在子类的构造函数中显式调用的父类的构造函数。
4 . 原型式继承
-
原理
借助原型可以基于已有的对象创建新的对象,即一个方法实现创建返回一个子类的的实例,并将创建这个子类的构造函数对应的原型对象改变成传入的对象,实现继承传入的对象的基本原理–在 ES5 时实现了这个方法Object.create(o)
; -
实现
function object(o) { function F() {} F.prototype = o; return new F(); } //实现继承 var person = { name: "mumu", friends: ["my", "andy"] }; var anotherPerson = object(person); anotherPerson.name = "greny"; anotherPerson.friends.push("yxiuchao"); console.log(anotherPerson.__proto__); //{ name: 'mumu', friends: [ 'my', 'andy', 'yxiuchao' ] }
-
要点
实现了原型属性及方法的继承,但是当多个使用此方法创建的对象当操作一个引用类型时,由于操作的引用类型存储的数据指向的地址单元是相同的,故无法进行函数的复用。
5 . 寄生式继承
- 原理
寄生式继承是与原型式继承紧密相关的一种思路,思路是与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数。 - 实现
其中既有基于function createAnothor(original) { var clone = object(original); //通过调用函数创建一个对象 clone.sayHi = function() { //给这个对象创建函数--增强这个对象 console.log("hi"); }; return clone; }
original
的属性和方法又有自己的sayHi
方法 - 要点
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似。
6 . 寄生组合式继承
-
原理
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的思路是:不必为了指定子类型的原型而调用父类的构造函数,我们需要的无非是父类原型的一个副本。 -
实现
function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; // 制定对象 } //在子类的构造函数中调用父类的构造函数继承属性 function SuperType(name) { this.name = name; this.colors = ["red", "blue"]; } SuperType.prototype.sayHi = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); //调用构造函数 this.age = age; } inheritPrototype(SubType, SuperType); //实现原型链的混合继承---继承原型的副本
-
要点
调用父类的构造函数一次,且子类的原型是执行父类的原型上。实现真正的继承
7 . ES6 中的 class 继承
区别:
ES6 中的 class 实现类 extends 继承实际上是和寄生组合式继承一样,同样只是调用了一遍父类的构造函数,唯一区别是,子类创建时 this 的变化,使用 ES6 的继承是先创建父类的 this 在创建子类的 this(在子类继承时必须先调用super()
),而寄生组合式继承是直接创建子类的 this。