javaScript中prototype的妙用 巧妙运用prototype属性原型链创建对象

来源:互联网 发布:mysql 显示视图前几行 编辑:程序博客网 时间:2024/05/16 11:44

转载请说明出处 :<http://blog.csdn.net/liuqiwen0512/article/details/8089690

                      今天说下javaScript 中  对象的prototype属性的巧妙运用  
                        我之前的一篇 js高级篇1中(
http://blog.csdn.net/liuqiwen0512/article/details/7774850)在下面已经说明了

                       创建对象的所有方式那篇文章是借鉴别人的,那篇文章简要说明了js的很多高级应用,

                       所有的东西也只是大概说明了下,没有详细的例子说明 初次接触还是无法理解。
                       下面我们就用详细的例子说明下。
      jQuery 中就是运用对象的prototype属性巧妙的创建jQuery对象    jQuery集合了prototype属性的所有妙用

 我们最开始创建对象的方式就是:
          var person=new Object();
          person.name="liuqiwen";
          person.outName=function(){
              alert(this.name);
          }
          person.outName();        //输出liuqiwen

          上面代码解释:
           创建了一个person对象,并且给对象添加一个name属性,给属性赋值为 liuqiwen,
            再给对象添加一个outName属性,给属性赋值是一个方法,outName属性其实是一个指针,

            指向了这个方法。
             最后一个调用方法   打印出 liuqiwen  (this永远指向调用它的对象,算是对上篇的巩固和加强)。
       如果我们需要多个person对象的话,那就需要我们手动多次创建,于是引出了下面的解决方案。

工厂模式:
      function persons(na){
          var person=new Object();
          person.name=na;
          person.outName=function(){
              alert(this.name);
          }
           return person;
        }
       var person1=persons("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=persons("liuqiwen2");
        person2.outName(); //输出liuqiwen2

              上面代码解释:
                                    上面代码解决了我们手动创建对象的烦恼,增加了代码的重用性。但是还是有两个问题 :
                                     1、浪费资源,为什么这么说呢?因为每次创建一个对象outName方法就会被创建一次,
每个对象都有自己独立的实例化

                                         的outName方法,按道理讲属性应该是每个对象应该有自己的版本,但是方法属性可以公用,这样就造成了资源的浪费。
                                     2、用instanceof运算符运算出的不是persons对象 ,因为本来在方法中创建的就不是persons对象而是Object对象。
      
  解决方案就是:    在函数外创建方法+构造函数
         function outName(){
              alert(this.name);
          }
          function person(na){
             this.name=na;
             this.outName=outName;
          }
       var person1=new person("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=new person("liuqiwen2");
        person2.outName(); //输出liuqiwen2
                上面代码解释:
                                      这种创建对象的方法解决了上面方法浪费资源和instanceof运算符对象的问题。但是看看代码还是冗余,要现在外面构建方法,

                                     然后在构造函数创建
                                     属性指针指向方法,这么做当方法很多像jQuery这样的类库这么多的方法就会混乱,难以维护。
         
                          
  针对上面方法的综合问题引出了我们今天的重点:巧妙运用prototype属性原型链创建对象
         function person(na){
              this.name=na;
           }
           person.prototype={
                 outName:function(){
                    alert(this.name);
                 },
                 iptName:function(na){
                     this.name=na;
                 }
           }
           //上面代码你也可以这样写,person.prototype.outName=function(){},
           //                person.prototype.iptName=function(){}
           //这种写法一般用在给构造函数添加方法,初始化我们用上面的写法简单明了。
       var person1=new person("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=new person("liuqiwen2");
        person2.outName(); //输出liuqiwen2
        person2.iptName("liuqiwen3");
        person2.outName(); //输出liuqiwen3
       
                上面代码解释:prototype属性还有很多细节的东西今天就不详细说明了  我会在下篇文章单独说明。
                                           上面代码先是一个构造函数,然后初始化构造函数的原型链也就是prototype,最后就是创建了两个实例调用了各自的方法。
                                           每个实例所有的方法都是共有的不会创建多余的对象,每个实例对象都有一个指针指向构造函数的prototype这块空间,
                                           说到这里有的童鞋就要问为什么不把 name属性也放在原型链中,因为name属性是私有属性不是公共的,

                                           放在原型链中就成了公有属性
                                           一个实例对象更改那么所有的实例都会更改,所以私有属性定义在构造函数内,公共方法定义在原型链中。
                                           上面的方法还是有一点缺陷就是  new 凸显出来了,怎么样能做到想jQuery隐藏 new关键字呢!
                

上面的代码 看起来不够面向对象              我们如何才能写成和java中的类一样的写法呢    可以用下面的方式
          function person(na){
              this.name=na;
              if(person.initialize==null){
             person.prototype={
                   outName:function(){
                      alert(this.name);
                   },
                   iptName:function(na){
                       this.name=na;
                   }             
             }
            person.initialize=true;
               }
           }          
       var person1=new person("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=new person("liuqiwen2");
        person2.outName(); //输出liuqiwen2
        person2.iptName("liuqiwen3");
        person2.outName(); //输出liuqiwen3
                上面代码解释:
                                    上面代码用person.initialize为标志  当创建person1对象时判断personin.itialize未定义为undefined时(undefined==null),
                                     说明person的原型链上未赋值所以给原型链上赋值,然后给person.initialize赋值为true,
然后创建person2时personin.itialize的值已经为true所以不会给person的原型链上赋值,省去了重复赋值的过程。
                                     当然这种方式只是看起来写法更像java的类(面向对象的写法),但是没有解决问题。
       

解决方案:工厂模式
            function persons(na){
           function person(na){
              this.name=na;
           }
           person.prototype={
                 outName:function(){
                    alert(this.name);
                 },
                 iptName:function(na){
                     this.name=na;
                 }
           }
           return new  person();                
            }
       var person1=persons("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=persons("liuqiwen2");
        person2.outName(); //输出liuqiwen2
        person2.iptName("liuqiwen3");
        person2.outName(); //输出liuqiwen3
                上面代码解释:
                                        上面代码也是运用刚开始的工厂模式  ,但是它的优点在于解决了上面工厂模式的浪费资源和代码冗余的问题。
                                        但是这个解决方案也面临和上面工厂模式同样的缺陷,(person1  instanceof persons)结果为false,
                                        那么怎么样解决这个问题呢?这就是今天的重中之重
                                     
 
解决方案:
         function person(na){
             if(!(this instanceof person)){
     return new person(na);
    }else{
        this.name=na;
    }
          }
          person.prototype={
                outName:function(){
                   alert(this.name);
                },
                iptName:function(na){
                    this.name=na;
                }
          }
       var person1=person("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=person("liuqiwen2");
        person2.outName(); //输出liuqiwen2
        person2.iptName("liuqiwen3");
        person2.outName(); //输出liuqiwen3
                 上面代码解释 :
                                           上面在代码 是 用(!(this instanceof person))判断当前对象是否是person对象,如果没有这句那么代码就会无限的循环下去,

                                       在创建一个对象时
                                       会执行构造函数中的语句,当var person1=person("liuqiwen1");那么就会执行return new person(na);这句就是创建对象,那么它又会
                                       执行person构造方法中return new person(na);所以就无限循环下去了。所以加了(!(this instanceof person))判断,当执行person方法
                                       时,这时this指向的是Window对象(注:这里要用  this instanceof Window 才会返回true,

                                       小写window只是Window的一个属性用来引用Window对 象)
                                       所以(!(this instanceof person))返回true,执行到return new person(na);这时又会执行构造函数,但是这时的this指针已经指向了

                                      new person 创建出来的对象,执行else 给对象name属性赋值。
              上面的方案已经解决了创建对象的各种问题,但是还是不够完美,要在构造函数中加判断  构造函数每次都执行两次。

               我们看看jQuery中是如何避免  这 些瑕疵的。
             

jQuery中的写法:
          function person(na){
         return new person.prototype.init(na);
           }
           person.prototype={
           init:function(na){
        this.name=na;
        return this;
     },
                 outName:function(){
                    alert(this.name);
                 },
                 iptName:function(na){
                     this.name=na;
                 }
           }
             person.prototype.init.prototype=person.prototype;
       var person1=person("liuqiwen1");
        person1.outName(); //输出liuqiwen1
       var person2=person("liuqiwen2");
        person2.outName(); //输出liuqiwen2
        person2.iptName("liuqiwen3");
        person2.outName(); //输出liuqiwen3          
                上面代码解释:
                                       上面代码中person.prototype.init.prototype=person.prototype;这句的意思就是把person的原型链赋值给
                                    person.prototype.init的原型链 ,这种方式完美的解决了上面需要加判断的问题,同时也对代码性能进行了优化。
                                    并且(person1 instanceof person)为true,至于为什么为true 在下篇我会详解prototype属性那么你就会明白了。

转载请说明文章出:http://blog.csdn.net/liuqiwen0512/article/details/8089690

0 0
原创粉丝点击