一、原型链继承
重点:利用原型让一个引用类型继承另外一个引用类型的属性和方法。构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
1 | function SuperType(){ |
使用原型创建对象会存在多个实例对引用类型的操作会被篡改的问题,在上面同样存在这个问题,如下:
1 | function SuperType(){ |
两个实例对象example1和example2的colors属性指向相同,改变一个会影响另一个实例的属性。
缺点:
①原型链继承多个实例的引用类型属性指向相同,一个实例修改了原型属性,另一个实例的原型属性也会被修改;
②不能传递参数;
③继承单一。
二、借用构造函数继承
重点:使用.call()和.apply()将父类构造函数引入子类函数,使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类。
1 | function SuperType(name){ |
借用构造函数继承的重点就在于SuperType.call(this, name),调用了SuperType构造函数,这样,SubType的每个实例都会将SuperType中的属性复制一份。
缺点:
①只能继承父类的实例属性和方法,不能继承原型属性/方法;
②无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿。
三、组合继承
重点:将原型链继承和构造函数继承这两种模式的优点组合在一起,通过调用父类构造,继承父类的属性并保留传参,然后通过将父类实例作为子类原型,实现函数复用。
其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承,这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性。
1 | function SuperType(name){ |
缺陷:
父类中的实例属性和方法既存在于子类的实例中,又存在于子类的原型中,不过仅是内存占用,因此,在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。——-这个方法是javascript中最常用的继承模式。
四、 原型式继承
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理,直接将某个对象直接赋值给构造函数的原型。
1 | function object(obj){ |
ECMAScript5通过新增Object.create()方法规范化了原型式继承,这个方法接收两个参数:一个用作新对象原型的对象和一个作为新对象定义额外属性的对象。
1 | var person = { |
缺点:
①原型链继承多个实例的引用类型属性指向相同(所有实例都会继承原型上的属性),存在篡改的可能;
②无法传递参数,无法实现复用。(新实例属性都是后面添加的)。
五、寄生式继承
重点:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回构造函数。(就像给原型式继承外面套了个壳子,然后return出来)
1 | function createAnother(original){ |
函数的主要作用是为构造函数新增属性和方法,以增强函数。
1 | var person = { |
缺点:
①原型链继承多个实例的引用类型属性指向相同,存在篡改的可能;
②无法传递参数,没用到原型,无法复用。
六、寄生组合式继承
重点:通过借用构造函数传递参数和寄生模式实现继承属性,通过原型链的混成形式来继承方法,在函数中用apply或者call引入另一个构造函数,可传参。
1 | function inheritPrototype(subType, superType){ |
寄生组合继承集合了前面几种继承优点,几乎避免了上面继承方式的所有缺陷,是执行效率最高也是应用面最广的。
缺点:
实现的过程相对繁琐。
时间有点匆忙,没有加上ES6的extends,有空再补上。