深入了解js的继承

147 阅读4分钟

image.png 上图是本篇文章主要深入的内容

我们将跟随每种方法的缺点,这条线去深入js的继承

1.原型链继承

原型链可谓是继承的灵魂,所以在了解继承之前一定要对原型链要有个大概的认知。 那么先带大家重温下构造函数、原型和实例的关系吧:每个构造函数都有一个原型对象。原型上面有一个constructor属性指回构造函数,而实例有个__proto__指针指向原型。

    function SuperType () {
            this.property = true
    }
    SuperType.prototype.getSuperValue = function () {
            return this.property
    }
    function SubType () {
            this.subproperty = false
    }
    // **原型链继承的核心代码**
    SubType.prototype = new SuperType()

    SubType.prototype.getSubValue = function () {
            return this.subproperty;
    }
    let instance = new SubType()
    console.log(instance.getSuperValue())

核心就是:SubType通过创建SuperType的实例并将其赋值给自己的原型SubType

原型链继承的缺点:

  1. 原型中包含的引用会在所有实例间共享,一个实例改变引用属性,其余实例的相同引用属性都会被改变
  2. 子类型在实例化时不能给父类型的构造函数传参 所以这两个缺点导致了原型链继承基本不会被单独使用。

举个例子来说明缺点一

function SuperType(){
  this.colors = ["red", "blue", "green"];
}
function SubType(){}

SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"

var instance2 = new SubType(); 
alert(instance2.colors); //"red,blue,green,black"

2.盗用构造函数继承

使用apply()和call()方法以新创建的对象为上下文执行构造函数。

    function  SuperType(){
        this.color=["red","green","blue"];
    }
    function  SubType(){
        //继承自SuperType **核心代码**
        SuperType.call(this);
    }
    var instance1 = new SubType();
    instance1.color.push("black");
    alert(instance1.color);//"red,green,blue,black"

    var instance2 = new SubType();
    alert(instance2.color);//"red,green,blue"

总结:盗用构造函数继承解决了原型继承的两个缺点。解决了引用值导致的继承问题,和支持了传递参数。 但是,盗用构造函数继承也有缺点:

  1. 无法继承父类的原型属性和方法。
  2. 必须在构造函数中定义,函数不能重用。

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;
    } 
    // 继承方法 **核心代码**
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType
    SubType.prototype.sayAge = function() {
            console.log(this.age);
    };
    let instance1 = new SubType("Nicholas", 29);
    instance1.colors.push("black");
    console.log(instance1.colors); // "red,blue,green,black" 
    instance1.sayName(); // "Nicholas"; 
    instance1.sayAge(); // 29 
    let instance2 = new SubType("Greg", 27);
    console.log(instance2.colors); // "red,blue,green"
    instance2.sayName(); // "Greg"; 
    instance2.sayAge(); // 27

缺点:1.效率问题,父类构造函数会被调用两次。2.有可能同样的属性名既会在实例属性上又会在原型属性上。

4.原型式继承

本质函数是:创建一个临时的构造函数,将传入的对象赋值给这个构造函数的原型,然后返回临时对象的实例

    function object(obj){
      function F(){}
      F.prototype = obj;
      return new F();
    }

具体写法:

    var person = {
      name: "Nicholas",
      friends: ["Shelby", "Court", "Van"]
    };

    var anotherPerson = object(person);
    anotherPerson.name = "Greg";
    anotherPerson.friends.push("Rob");

    var yetAnotherPerson = object(person);
    yetAnotherPerson.name = "Linda";
    yetAnotherPerson.friends.push("Barbie");

    console.log(person.friends);   //"Shelby,Court,Van,Rob,Barbie"

Object.create()将原型式继承的概念规范化了。

总结:1.弊端:和原型链是一样的 2.优点:不需要单独创建构造函数

5.寄生式继承

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

总结:1.弊端:和原型链是一样的 2.优点:不需要单独创建构造函数

6.寄生组合式继承

和组合继承的区别就在于 原型链继承的时候,它是先创建父类原型的副本,将副本赋值给了子类型的原型。

    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
     }
     // 继承 **和组合式继承的区别就在这行**
     SubType.prototype = Object.create(SuperType.prototype)
     SubType.prototype.constructor = SubType
     SubType.prototype.sayAge =function () {
             console.log(this.age)
     }
     var instance = new SubType("Nick",29)
     console.log(instance)
     console.log(instance instanceof SubType)
     console.log(instance instanceof SuperType)

优点:解决了组合式继承的两个缺点

组合式继承缺点:1.效率问题,父类构造函数会被调用两次。2.有可能同样的属性名既会在实例属性上又会在原型属性上。

总结:寄生式组合继承可以算是引用类型继承的最佳模式