JavaScript的继承
1.原型链
将原型链作为继承的主要实现方式,利用原型让一个引用类型继承另一个引用类型的属性和方法。
前景回顾构造函数、原型和实例之间的关系:
每个构造函数都有个属性
prototype
指向原型,原型有个constructor
属性指向构造函数,每个实例都有个内部指针指向原型
所以,只要让一个原型对象等于另一个类型的实例,就可以继承另一个类型的所有属性和方法
实现的本质是:重写了原型对象,以一个新类型的实例代替。所以,原来存在于SuperType
中的所有属性和方法,现在都存在了SubType.prototype
中了。
总结下:
- 1.
instance
里有实例属性subproperty:false
- 2.
SubType.prototype
里有实例属性property:true
,原型方法getSubValue
- 3.
SuperType.prototype
里有原型方法getSuperValue
注意:
1.给原型添加方法的代码一定要放在替换原型的语句之后。很重要!!!
2.不能使用对象字面量创建原型方法。因为这样会重写原型链。
缺点:
- 1.由于通过原型来实现继承时,原型实际上会变成另一个类型的实例。则原先的实例属性会变成现在的原型属性了。所以所有的实例都会共享这个原型属性。当这个原型属性是引用类型时就会出现问题。
- 2.在创建子类型的实例时,不能向超类型的构造函数传递参数
为了解决1,产生了
2.借用构造函数
实现方法:在子类型构造函数内部调用超类型的构造函数
通过使用call
或者apply
方法,在未来新创建的SubType
实例的环境下调用了SuperType
构造函数。这样就会在新SubType
对象上执行SuperType()
函数中定义的所有对象初始化代码
为了解决2,则
在SubType
函数内部调用SuperType
构造函数时,实际上是为SubType
的实例设置了name
属性。
缺点:
- 1.方法都在构造函数中定义,无法很好的复用
- 2.在超类型的原型中定义的方法,对子类型来说也是不可见的
3.组合继承
将原型链和借用构造函数的技术结合在一起。思路为:使用原型链实现对原型属性和方法的继承,使用构造函数来实现对实例属性的继承。
通过组合继承。使SubType
拥有实例属性:name,colors,age
,拥有原型方法:sayName(),sayAge().
4.原型式继承
原理:借助原型可以基于原有的对象创建对象,具体如下:
分析:
object
函数里创建了一个构造函数F
,把已有对象o
赋值给F
的原型对象,然后返回一个F
的实例。则这个实例共享对象o
的所有属性和方法,实际上就是完成了第对象o
的浅复制。
ECMAScript5
新增Object.create()
方法规范了原型式继承,接受两个参数:一个用作新对象的原型和一个为新对象定义额外属性的对象,这两个参数都是可选的。
在只传入一个参数的情况下,Object.create()与object()
方法的行为相同。
缺点:包含引用值的属性会被所有的实例所共享
5.寄生式继承
分析:基于
person
创建了一个新对象anotherPerson
,并且拥有person
的属性和方法,还拥有自己的sayHi()
方法
缺点:每个对象都拥有一个不同的sayHi()
方法,不能做到函数复用。
6.寄生组合式继承
实现原理:使用寄生式继承来继承超类型的原型,然后再降结果指定给子类型的原型
分析:
在
inheritPrototype
函数中。通过object()
函数,把superType.prototype赋值给prototype
,使得prototype
就是superType.prototype
的一个副本,导致subType.prototype
(即子类型的原型)拥有父类型原型的所有属性和方法。
优点:最多只调用了一次SuperType
构造函数,并且因此避免了在SubType.prototype
上创建不必要的属性