zoukankan      html  css  js  c++  java
  • 第一篇 对Javascript中原型的深入理解

      理解原型对象
    在Javascript中不管什么时候,仅仅要创建一个新的函数,就会依据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象(这个对象的用途是包括能够有特定类型的全部实例共享的属性和方法)。假设依照字面上的意思来理解。那么原型属性就是通过调用构造函数而创建的那个对象的实例的原型对象。

    默认的情况下,全部的原型对象都会自己主动获得一个constructor(构造函数)属性,这个属性包括一个指向prototype属性所在函数的指针。

    例如以下图


    function Person(){
    }
    


    上图中创建了一个构造函数Person(function Person())。Person函数中有一个prototype的属性指向Person的原型对象,在原型对象中有一个constructor的属性指向了Person函数。全部能够通过new Person()创建对象。

    创建了自己定义的构造函数后。其原型对象默认仅仅会取得constructor属性,至于其它的方法。都是从Object继承而来的。

    当中上图中Person.prototype .constructor指向Person.而通过这个构造函数,我们能够继续为原型对象加入其它的属性和方法。

    例如以下图:

     

    上图的js代码就是

    Person.prototype={
      name:"Nicholas",
      age:23,
      sayName:function(){
        return this.name;
      }
    };
    

    当调用构造函数创建一个新实例后。该实例的内部将包括一个指针(内部属性)。指向构造函数的原型对象。

    ECMA-262第5版中管这个指针叫[[Prototype]],尽管在脚本中没有标准方式訪问,我们能够借由Chrome在每一个对象上都支持一个属性_proto_(私有属性不可见),这个链接的存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。


    var p1=new Person();


    当创建一个新的p2之后依旧会有一个_proto_属性指向Person的原型(Persion.prototype)。换句话说每一个实例与构造函数没有直接的关系。尽管这实例都不包括属性和方法,但我们却能够调用p1.name查找对象属性的个过程来实现。

    此时假设通过p2.name设置属性值后会在对象自己的内存空间中存储name的值,当调用sayName()方法的时候在寻找name时,在自己空间中找到之后就不会去原型对象中查找(注意:原型中的值是不会被替换,不过在查找的时候被覆盖)


    var p1=new Person();
     alert(p1.name);//Nicholas
     var p2=new Person();
    

    原型的检測
    在全部实现中都无法訪问到[[Prototype]],可是我们能够通过isPrototypeOf()方法来确定对象直接是否存在这样的关系。

    从本质上讲,假设[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype)。


    ECMAScript5中添加一个新的方法.叫做Object.getPrototypeOf(),这种方法返回[[Prototype]]的值
    == 能够检測某个对象的constructor
    检測某个属性是否是自己是自己的属性用(不是对象原型中的实现)hasOwnProperty(),能够清楚的知道什么时候訪问的自己的属性。什么时候訪问的是原型属性了。
    delete能够用来删除实例属性(自己的属性)
    in操作:有两种方式操作in操作符。

    单独的使用for-in循环中使用。单独是用时,in
    会通过对象可以訪问给的属性的返回值。不管属性存在于实例中还是原型中

    //检查某个对象是否是某个函数的原型
      alert(Person.prototype.isPrototypeOf(p1));//true
       ECMAScript5中添加一个新的方法.叫做Object.getPrototypeOf(),在全部支持的实现中,这种方法返回[[Prototype]]的值
       alert(Object.getPrototypeOf(p1)==Person.prototype);
       alert(Object.getPrototypeOf(p1).name);
      //alert(p1.constructor==Person);
      //alert(p2.hasOwnProperty("name"))
      delete p2.name;
      //alert("name" in p1)//来自原型
       p1.id=12;
     // alert("id" in p1);//来自实例
      alert(hasPrototypeProperty(p1,"name"))
     /**
      检查某个属性是否在原型中存在
      */
      function hasPrototypeProperty(obj,prop){
        return ((!obj.hasOwnProperty(prop))&&(prop in obj));
      }
    

    在上面给原型加入的属性中存在着已问题constructor属性不指向Person了。上面写法的本质是全然重写了默认的prototype对象。因此constructor属性也变成了新的对象的constructor属性(指向Object构造函数),不在指向Person函数,虽然instanceof操作还是正确返回结果,可是通过constructor已经无法确定对象的类型了。
       为了避免这个问题但是手动指定constructor
    alert(p1 instanceofPersion);alert(p1.constructor==Persion)
    原型重写存在的问题
     因为在原型中查找值得过程是一次搜索。我们对原型对象所做的不论什么改动都可以马上从实例上反映出来。即使先创建实例后改动原型也是可以的

    var p2=new Person();
      Person.prototype.sayHi=function(){
        alert(this.name);
      }
      p2.sayHi();//能够获取值
    

    能够获取值是由于实例与原型之间的松散连接关系,当我们调用p2.saHi()时。首先回去实例中搜索名为sayHi的属性。在没有找到的情况下。会继续搜索原型。由于实例和原型之间仅仅只是是一个指针,而不是副本,因此能够在原型中找到新的sayHi属性并返回保存在哪里的函数。
    假设是重写整个原型就不一样了。我们知道调用构造函数是会为实例加入一个指向最初原型的[[Prototype]]指针。把原型改动为另外一个对象就等于切断了构造函数和最初的原型之间的联系。切记:实例中的指针仅指向原型。

    而不是构造函数。


    function Person(){
    }
    
      var p1=new Person();
      
      //全部的"范围对象"都继承自这个对象  重写原型对象
      Person.prototype.sayHi=function(){
        alert(this.name);
      }
      //全部的"范围对象"都继承自这个对象  重写原型对象
      Person.prototype={
      constructor:Person,//显示的设置构造函数的反向引用
      name:"Nicholas",
      age:23,
      sayName:function(){
        return this.name;
      }
    };
      
      p1.sayHi();//undefined
      alert(p1.sayName());//报错
     var p2=new Person(); 
     alert(p2.sayName());//Nichola
    

    重写之前与重写之后如图:




    原型存在的问题
    原型对象中的属性是被非常多实例共享的,这个共享对于函数是非常合适的。对于原型中基本值得属性是没有问题的可是,假设属性值包括引用类型值就会有一定的问题了。

    function Person(){
    }
    
        Person.prototype={
      constructor:Person,//显示的设置构造函数的反向引用
      name:"Nicholas",
      age:23,
      friends:["Shelby","Cunrt"],
      sayName:function(){
        return this.name;
      }
    };
     var p1=new Person();
     var p2=new Person();
     
     p1.friends.push("Jack");
     alert(p1.friends);//"Shelby","Cunrt","Jack"
     alert(p2.friends);//"Shelby","Cunrt","Jack"
     alert(p1.friends==p2.friends);//true
    

    解决这个为题做好的方法例如以下:

    function Person(name,age,friends){
      this.name=name;
      this.age=age;
      this.friends=friends;
    }
    
        Person.prototype={
      constructor:Person,//显示的设置构造函数的反向引用
      sayName:function(){
        return this.name;
      }
    };
     var p1=new Person("Tom",20,"Shelby");
     var p2=new Person("Alisa",20,["Cunrt","Jack"]);
     alert(p1.friends);//"Shelby"
     alert(p2.friends);//"Cunrt","Jack"
     alert(p1.friends==p2.friends);//false
    



  • 相关阅读:
    Hdu 5396 Expression (区间Dp)
    Lightoj 1174
    codeforces 570 D. Tree Requests (dfs)
    codeforces 570 E. Pig and Palindromes (DP)
    Hdu 5385 The path
    Hdu 5384 Danganronpa (AC自动机模板)
    Hdu 5372 Segment Game (树状数组)
    Hdu 5379 Mahjong tree (dfs + 组合数)
    Hdu 5371 Hotaru's problem (manacher+枚举)
    Face The Right Way---hdu3276(开关问题)
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5369190.html
Copyright © 2011-2022 走看看