3. 继承
1.原型链
原文:利用原型让一个引用类型继承另一个引用类型的属性和方法。
根据上篇末尾的总结的构造函数与原型与实例之间的关系,如果A实例不是指向A实例自己的prototype,而是指向了B实例的prototype,也就是B.prototype = A实例,这时候B再创建实例,就会获得A的prototype对象的属性及方法,还有A构造函数本身的属性及方法。
同时,所有函数的默认原型,都是Object的实例, 因此 默认原型都会最终指向Object.prototype
SubType.prototype = new SuperType() // A的实例指向了B的prototype
问题: 1.引用类型值的原型属性会被所有实例共享。2. 创建父类型实例时同时传参,子类型创建的所有实例都会共享父类型的参数
2. 借用构造函数
利用apply() call()这种超类型构造函数
在子类型构造函数的内部调用超类型构造函数
function SuperType() {
this.colors = ['red', 'green']
}
function SubType() {
SuperType.call(this)
}
var instance1 = new SubType()
instance1.colors.push('black') // ['red', 'green', 'black']
var instance2 = new SubType()
instance2.colors // ['red', 'green']
- 这样每个实例都会有自己的colors副本
问题:方法都在各自的构造函数中定义,无法复用
3.组合继承
将原型链和借用构造函数混合使用,原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承
function SuperType(name) {
this.name = name
this.colors = ['red', 'green']
}
SuperType.prototype.sayName = function() {}
function SubType(name) {
SuperType.call(this, name)
}
// 继承方法‘
SubType.prototype = new SuperType() // SubType.prototype.constructor是SuperType
SubType.prototype.constructor = SubType // 手动指回SubType
var instance = new SupType()
// 此时 colors是特有的,而sayName() 是共有的
原型式继承
原始方式是
function object(o) {
function F(){}
F.prototype = o
return new F()
}
使用ES5中的Object.create() , 接收两个参数,一是一个模板对象,二是get、set方法
var person = {
name: 'Nicol';
colors: ['Shely', 'Bob']
}
var person1 = Object.create(person)
person1.name = 'Dada'
person1.colors.push('Hey') // ['Shely', 'Bob', 'Hey']
var person2 = Object.create(person)
person2.colors // ['Shely', 'Bob', 'Hey']
person2.name // 'Nicol'
问题:同样的使用原型模式都是一样的问题,就是共享引用类型值
寄生式继承
即创建一个仅用于封装 继承过程的函数,在内部以寄生的方式来增强对象,最后像是真的是它做了所有工作一样返回对象。
function createAnother(original) {
var clone = object(original) // 通过调用一个返回新对象的函数,创建一个新对象。例如Object.create()
clone.sayHi = function() { // 以某种方式在内部像寄生一样增强这个对象
alert('Hi')
}
return clone;
}
寄生组合式继承
可以看上面组合继承,调用两次SuperType()的构造函数
function SuperType(name) {
this.name = name
this.colors = ['red', 'green']
}
SuperType.prototype.sayName = function() {}
function SubType(name) {
SuperType.call(this, name) // 第二次调用SubType构造函数时
}
// 继承方法‘
// ---------------->
SubType.prototype = new SuperType() // 第一次调用SuperType构造函数时
SubType.prototype.constructor = SubType
// <----------------
var instance = new SupType()
两次调用,就会创建两次 name和colors属性,第二次会覆盖第一次,
实际第一次调用,为了共享原型链上的方法,没必要调用构造函数,
所以可以使用,原型链的继承的混成模式
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype)
prototype.constructor = subType
subType.ptototype = prototype
}
// 再将上面---> <-----中的代码替换成下面
inheritPrototype(SubType, SuperType);
总结
这么多莫名其妙的名称,什么原型式,借用构造函数,寄生式等等,真叫人头疼,不得不让我好好看看他们的区别:
原型链 --------------------> 用prototype继承,缺点:所有属性共享
原型式继承---------------> Object.create(obj)等返回一个对象,缺点:引用属性共享
借用构造函数 -----------> apply() call(),内部独享了。缺点:方法不共享
组合继承------------------> 原型链和借用构造函数两个一起来。 缺点:调用了两次父类构造函数
寄生式继承 --------------> 在内部用各种方法增强一个临时对象,最后返回这个对象。伏笔
寄生组合式继承---------> 用寄生式继承的思想创建一个混成模式,解决组合继承中调用两次父类构造函数的问题。堪称完美了。
网友评论