原型链的继承

Posted 改变需要勇气,习惯需要坚持。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原型链的继承相关的知识,希望对你有一定的参考价值。

原型链的问题

原型链的作用很强大,可以用来实现继承,但是也会有一些问题,最大的问题就是引用类型值得原型属性会被所有实例共享

实例是什么? 实例就是 new 对象名称() ; 例如: var instance=new Person();  instance就是实例;

例如: 

    var SuperType=function(){
      this.colors=["red","black"];
    }
    var SubType=function(){};
    SubType.prototype=new SuperType();//继承

    var subType=new SubType();
    subType.colors.push("green");
    console.log(subType.colors);

    var subType2=new SubType();
    console.log(subType2.colors);

这段代码输出结果都是:"red","black","green"

这是怎么回事了?

这是因为SubType的所有实例都会共享colors属性,而我们对subType.colors的修改,能够通过subtype2.colors反映出来。

 

借用构造函数

在解决原型中引用类型值得问题中,可以通过借用构造函数的技术来实现;即:就是通过子类型的构造函数调用父类的构造函数(可以通过call或者apply调用父类构造函数)

以上面的例子改成如下:

var SuperType=function(){
      this.colors=["red","black"];
    }
    var SubType=function(){
        SuperType.call(this) //调用父类的构造函数
    };
    SubType.prototype=new SuperType();//继承

    var subType=new SubType();
    subType.colors.push("green");
    console.log(subType.colors);

    var subType2=new SubType();
    console.log(subType2.colors);

输出的结果为:"red","black","green"

                    "red","black"

这样我们实际是在(未来将要)新创建的SubType实例环境下调用了SupType构造函数

这样就会在新的SubType对象上执行SuperType函数,而SubType的每个实例都会具有colors属性的副本;

 

最基本的原型继承

       var Parent=function(name){
             this.name=name || "parent";
       }

       Parent.prototype.getName=function(){
             return this.name;
       }

       var Child=function(name,age){
              this.name=name;
              this.age=age;
       }

       Child.prototype=new Parent();//通过此种方法继承

       var parent=new Parent("this is a parent");
       var child=new Child("I‘m a child",22);
       console.log(parent.getName()); //this is a parent
       console.log(child.getName());//I‘m a child
       console.log(child.age);//22

 

这种优点是简单,缺点是如果子类需要做跟父类构造函数中相同的初始化动作,需要在子类的构造函数中再重新做一遍,例如:Child中初始化name

如果初始化工作不断增加,这种方式很不方便;

这时候可以借用构造函数,例如:

        var Parent=function(name){
              this.name=name || "parent";
         }

         Parent.prototype.getName=function(){
              return this.name;
         }

         Parent.prototype.obj={a:1};

        
         var Child=function(name,age){
              //通过apply调用父类构造函数进行初始化工作
              //这样不管父类执行多少初始化工作,子类也可以执行同样的初始化工作
              Parent.apply(this,arguments);
              this.age=age;
         }

         Child.prototype=new Parent();
         //Child.prototype=Parent.prototype;

         var child=new Child("alice",22);
         var parent=new Parent("stone");
         console.log(child.getName());
         console.log(parent.getName());

 

这种方式可以解决初始化的问题,但是父类构造函数会被执行两次,一次在子类构造函数中,一次是在赋值子类原型链中,

可以通过

        Child.prototype=Parent.prototype;//代替 Child.prototype=new Parent();

即子类和父类指向相同的原型链,但是这样会存在一个问题,修改子类的原型链,会影响父类的原型链,例如:

  var Parent=function(name){
              this.name=name || "parent";
         }

         Parent.prototype.getName=function(){
              return this.name;
         }

         Parent.prototype.obj={a:1};

        
         var Child=function(name,age){
              //通过apply调用父类构造函数进行初始化工作
              //这样不管父类执行多少初始化工作,子类也可以执行同样的初始化工作
              Parent.apply(this,arguments);
              this.age=age;
         }

         Child.prototype=new Parent();
         //Child.prototype=Parent.prototype;

         var child=new Child("alice",22);
         var parent=new Parent("stone");
         console.log(child.getName());
         console.log(parent.getName());

         Child.prototype.obj.a=26;
         console.log(child.obj.a);//26
         console.log(parent.obj.a);//26

当我改变 Child.prototype.obj.a时,会同时改变父类的原型链。

 

那又什么好的办法?

 

以上是关于原型链的继承的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 基于原型链的继承

JavaScript原型链的理解

原型链的继承都发生在构造函数上

js原型链和继承的理解

原型继承

前端基本知识:JS的原始链的理解