[关闭]
@15152278073 2018-06-05T09:04:07.000000Z 字数 2786 阅读 590

JS的继承(上)

JS


继承是OO语言的一个重要概念。许多OO语言支持两种方式继承:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。ECMAScript函数没有签名,所以无法实现接口继承,只支持实现继承,实现继承主要依靠原型链来实现的。

原型链

原型链作为实现继承的主要方法,其基本思想是利用原型让每个引用类型继承另一个引用类型的属性和方法。

实现原型链有一种基本模式,其代码如下:

  1. //super
  2. function SuperType() {
  3. this.property = true;
  4. }
  5. SuperType.prototype.getSuperValue = function () {
  6. return this.property;
  7. };
  8. //sub
  9. function SubType() {
  10. this.subProperty = false;
  11. }
  12. //继承SuperType
  13. SubType.prototype = new SuperType();
  14. SubType.prototype.getSubValue = function () {
  15. return this.subProperty;
  16. };
  17. let instance = new SubType();
  18. console.log("property : " + instance.getSuperValue()) //true

在上面的例子中,我们没有使用SubType的原型,而是给了它SuperType的实例。于是,新原型有了SuperType的所有属性和方法。通过实现原型链,实际上是扩展了原型搜索机制。

1. 别忘记默认的原型

前面的例子少了一环。SuperType其实还包含了一个内部指针,指向Object.prototype。一句话,SubType继承了SuperTypeSuperType继承了Object

2. 确定实例和原型的关系

可以通过两种方式确定原型和实例的关系。第一种是通过instaceof操作符。另一种是isPropertyOf()方法。

  1. console.log(instance1 instanceof Object) //true
  2. console.log(instance1 instanceof SuperType) //true
  3. console.log(instance1 instanceof SubType) //true
  4. console.log(Object.prototype.isPrototypeOf(instance1)) //true
  5. console.log(SuperType.prototype.isPrototypeOf(instance1)) //true
  6. console.log(SubType.prototype.isPrototypeOf(instance1)) //true

3. 谨慎地定义方法

子类型有时候需要的重写超类型的某个方法,或者需要添加不存在的方法。添加方法的代码一定要放在替换原型之后!

  1. //super
  2. function SuperType() {
  3. this.property = true;
  4. }
  5. SuperType.prototype.getSuperValue = function () {
  6. return this.property;
  7. };
  8. //sub
  9. function SubType() {
  10. this.subProperty = false;
  11. }
  12. //继承SuperType
  13. SubType.prototype = new SuperType();
  14. SubType.prototype.getSubValue = function () {
  15. return this.subProperty;
  16. };
  17. let instance = new SubType();
  18. console.log("property : " + instance.getSuperValue()) //true
  19. SubType.prototype.getSuperValue = function () {
  20. return false;
  21. };
  22. instance = new SubType();
  23. console.log("property : " + instance.getSuperValue()) //false

上述代码重写了SubTypegetSuperValue()方法,当SubType实例调用getSuperValue()方法时,调用的是新的方法;但通过SuperType的实例调用getSuperValue()时,会继续调用原来的方法。

必须在实例替换原型之后,再定义方法。
通过原型链实现继承时,不能使用字面量创建原型方法,因为这样会重写原型链。

  1. //super
  2. function SuperType() {
  3. this.property = true;
  4. }
  5. SuperType.prototype.getSuperValue = function () {
  6. return this.property;
  7. };
  8. //sub
  9. function SubType() {
  10. this.subProperty = false;
  11. }
  12. //继承SuperType
  13. SubType.prototype = new SuperType();
  14. SubType.prototype.getSubValue = function () {
  15. return this.subProperty;
  16. };
  17. SubType.prototype = {
  18. //变成了Obejct实例
  19. getSubValue: function () {
  20. return this.subProperty;
  21. },
  22. someMethod: function () {
  23. return false;
  24. }
  25. };
  26. let instance = new SubType();
  27. console.log("property : " + instance.getSuperValue()) //error

4. 原型链的问题

原型链虽然很强大,可以用来实现继承,但也存在一些问题。最主要的问题是来自包含引用类型值的原型。引用类型的原型属性会被所有实例共享;这也是为什么要在构造函数而不是原型对象中定义属性的原因。

  1. function SuperType(){
  2. this.colors = ["red","blue","green"];
  3. }
  4. function SubType(){
  5. }
  6. //继承
  7. SubType.prototype = new SuperType();
  8. let ins1 = new SubType()
  9. ins1.colors.push("black")
  10. console.log(ins1.colors) //red , blue ,green ,black
  11. let ins2 = new SubType()
  12. console.log(ins2.colors)//red , blue ,green ,black

原型链的另一个问题是:创建子类型的实例时,无法向超类型的构造函数中传递参数。实际上是无法在不影响所有实例对象的情况下,给超类型传递参数。所以实践中很少单独使用原型链继承模式。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注