JS中原型的动态性以及原型链相关问题

653 阅读3分钟

1.对于JS原型中的动态性基本有两个问题:

(1)在通过构造函数实例化对象之后,给它的原型上添加方法,这个时候我们调用这个方法,发现这个新的方法尽管是在实例化对象之后被添加上的但依然可以被调用。

    var a = function(name){
       this.name = name;
               
    }
    
    var item1 = new a("fxn");
    a.prototype.getName = function(){
        return this.name;
    }
    
    item1.getName();//fxn

(2)在通过构造函数实例化对象之后,给它的原型上重写新的属性和方法(并以字面量的形式添加新的方法和属性),这个时候我们调用新增加的方法和属性发现是会报错的。

   var a = function(name){
       this.name = name;
   }
   
       var item1 = new a("fxn");
       a.prototype={
       name:"fxn",
       age:23,
       behavior:function(){
       return this.age;
       }
   }
   item1.behavior(); //Uncaught TypeError: item1.behavior is not a function at<anonymous>: 13: 7
   

由上图可知重写函数实际是因为字面量的形式重新开辟了一块空间,并将这些变量存入新开的空间内,此时构造函数指向新的原型,但是新创造的对象却还是指向老的原型,所以,此时无法调用新的函数,但是可以调用老的函数。

!!!!而第一种追加的方式其实本质是在原来已有的地址上重新开辟了空间,这里类似于C语言的alloc()函数。

2 原型链

(1)原型链继承

    var a = function(name){
    this.name = name;
    this.arr = [];
    };
    
    a.prototype.getName = function(){
    return this.name;
    } 
    var b = function(age){
    this.age = age;
    }
    
    b.prototype = new a("fxn");
    
    var item1 = new b(23);
    item1.name //"fxn"
    item1.arr.push(2);//[2]  item1压栈2
    item1.getName();//"fxn"
    
    var item2 = new b(24);
    item2.arr;//[2]//item2没有压栈,但是也打印出了2。
(1) 由上述代码可知原型链继承的弊端是每个实例都共享了引用对象。这样会导致数组无法私有,显然不能正常使用。
(2) 这样对于传参也不是很方便,不能直接通过子类给父类传参。

2 借用构造函数继承

    var a = function(name){
       this.name = name;
       this.arr = [];
   }
   
   a.prototype.getName = function(){
       return this.name;
   }
   var b = function(name,age){
       a.call(this,name)
       this.age = age;
   }
   
   var item1 = new b("cui",25);
   var item2 = new b("fxn",24);
   
   item1.arr.push(3);
   
   item2.arr.push(4);
   
   item2.arr // 4
   item1.arr // 3
   item1.getName(); //Uncaught TypeError: item1.getName is not a function
   at <anonymous>:20:7
    

这里借用构造函数的方式解决了构造函数内引用共享的问题,就是在一个实例内对数组操作不会影响其它实例,但是却不能解决调用原型链上的函数,此类方法也存在缺陷。

3.组合继承

var a = function (name) {
     this.name = name;
     this.arr = [];
 }
 
 a.prototype.getName = function () {
     return this.name;
 }
 var b = function (name, age) {
     a.call(this, name) //1.这个里面的this实际指的是a对象 2.这里第二次调用构造函数
     this.age = age;
 }
 
 b.prototype = new a(); //这里第一次调用构造函数
 b.prototype.constructor = b;
 var item1 = new b("cui", 25);
 var item2 = new b("fxn", 24);
 
 item1.arr.push(3);//[3]
 
 item2.arr.push(4);//[4]
 
 item1.getName();//cui
 
 item2.getName();//fxn

这里利用组合继承看似解决了构造函数内引用类型共享的问题,同时也解决了原型链上的方法继承的问题,但是 这里的效率不够高,因为兴师动众的进行了两次构造函数创建对象,所以有些浪费空间。

4.寄生组合式继承

       var a = function (name) {
           this.name = name;
           this.arr = [];
       }
       
       a.prototype.getName = function () {
           return this.name;
       }
       var b = function (name, age) {
           a.call(this, name)
           this.age = age;
       }
       
       function parasitism(a,b){
           var prototype = Object(a.prototype);
           prototype.constructor = b;
           b.prototype = prototype;
       }
       
       parasitism(a,b);
       var item1 = new b("cui", 25);
       var item2 = new b("fxn", 24);
       
       item1.arr.push(3);//[3]
       item2.arr.push(4);//[4]
       item1.getName();//cui
       item2.getName();//fxn
       

3 总结

寄生式组合继承基本来说完美的解决了3个问题

(1)实例对象构造函数的引用类型的共享问题
(2)解决原型链上的呃方法不能被继承
(3)解决效率两次构造函数效率低的问题。