javascript实现继承

  • Post author:
  • Post category:java

继上一篇文章说到的原型模式创造对象,主要缺点是存在多个实例共享数组,一般来说不同数据应该有自己的属性副本,这就是开发过程中不单独使用原型模式的原因。

这一篇主要讲讲继承,是学习红宝书继承一章做的笔记。

JS的继承主要是通过原型链实现的。

原型链

通过原型继承多个引用类型的属性和方法。

重温一下构造函数,原型和实例的关系:

每个构造函数(Test)都有一个原型对象(Test.prototype),原型有一个属性指回构造函数(constructor),而实例有一个内部指针(proto)指向原型。如果原型是另一个对象的实例,意味着这个原型本身有个内部指针指向另一个原型,另一个原型也有一个指针指向另一个构造函数。实例与原型之间构造了一条原型链。

SubType.prototype = new SuperType(); //重写原型
  1. 默认原型

    1. 默认所有引用类型都继承自object,也是通过原型链实现
    2. 这就是自定义类型能够继承toString等默认方法的原因
  2. 原型与继承关系

    1. instanceof 操作符
  3. 关于方法

    1. 子类覆盖父类的,需要等到原型赋值后再修改
    2. 以字面量创建原型方法会破坏之前的原型链
    // 继承 SuperType 
    SubType.prototype = new SuperType(); 
    // 通过对象字面量添加新方法,这会导致上一行无效
    SubType.prototype = { 
     getSubValue() { 
     return this.subproperty; 
     }, 
     someOtherMethod() { 
     return false; 
     } 
    };
    
  4. 原型链的问题

    1. 在使用原型实现继承时,原型实际上变成另一个类型的实例,原先的实例属性会变成原型属性。

      function SuperType() { 
       this.colors = ["red", "blue", "green"]; 
      } 
      function SubType() {} 
      // 继承 SuperType 
      SubType.prototype = new SuperType(); 
      let instance1 = new SubType(); 
      instance1.colors.push("black"); 
      console.log(instance1.colors); // "red,blue,green,black" 
      let instance2 = new SubType(); 
      console.log(instance2.colors); // "red,blue,green,black" 
      
    2. 子类型实例化的时候不能传参

    综上:原型链基本不会被单独使用

盗用构造函数

在子类构造函数中调用父类构造函数,可以使用call和apply方法创建对象得上下文执行构造函数。

主要缺点:必须在构造函数中定义方法,函数不能重用。此外子类也不能访问父类得方法。

    function Child() {
    //这里相当于在child对象上运行了parent所有的初始化的代码
        Parent.call(this, "yaya");
    }
    let instance1 = new Parent();
  1. 传递参数
    1. 可以在子类构造函数中向父类构造函数传参
  2. 盗用构造函数问题
    1. 必须在构造函数中定义方法,因此函数不能重用。
    2. 子类也不能访问父类原型上定义的方法。

组合继承

结合构造函数和原型链

基本思路就是使用原型链继承原型上属性和方法,而通过盗用构造函数继承实例属性。

缺点:效率低,父类构造函数始终被调用两次。

function SubType(name, age){ 
 // 继承属性
 SuperType.call(this, name); //第二次调用 
 this.age = age; 
} 
// 继承方法
SubType.prototype = new SuperType(); //第一次调用

寄生式继承

创建一个实现继承函数,以某种方式增强对象,然后返回这个对象。

function createAnother(original){ 
 let clone = Object.create(original); // 通过调用函数创建一个新对象
 clone.sayHi = function() { // 以某种方式增强这个对象
 console.log("hi"); 
 }; 
 return clone; // 返回这个对象
} 
let person = { 
 name: "Nicholas", 
 friends: ["Shelby", "Court", "Van"] 
}; 
let anotherPerson = createAnother(person); 
anotherPerson.sayHi(); // "hi" 

通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。

寄生组合继承

通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路就是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本,相当于寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型

function inheritPrototype(subType, superType) { 
 let prototype = Object.create(superType.prototype); // 创建对象
 prototype.constructor = subType; // 增强对象 
 subType.prototype = prototype; // 赋值对象
} 

接收两个参数,子类构造函数 和父类构造函数,

1 取得父类原型副本

2 给返回的对象设置constructor,解决由于重写原型导致默认constructor丢失的问题

3 返回对象

function SuperType(name) { 
 this.name = name; 
 this.colors = ["red", "blue", "green"]; 
} 
SuperType.prototype.sayName = function() { 
 console.log(this.name); 
}; 
function SubType(name, age) { 
 SuperType.call(this, name); 
  this.age = age; 
} 
inheritPrototype(SubType, SuperType); 
SubType.prototype.sayAge = function() { 
 console.log(this.age); 
};

版权声明:本文为weixin_42439051原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。