J2EE社區

          茍有恒,何必三更起五更眠;
          最無益,只怕一日曝十日寒.
          posts - 241, comments - 318, trackbacks - 0, articles - 16

          第6章 面向對象程序設計

          Posted on 2011-07-20 11:46 xcp 閱讀(472) 評論(0)  編輯  收藏 所屬分類: JavaScript高級程序設計(第2版)
          1、自定義對象
              a. 工廠模式
          function Person(name,age){
              
          var person = new Object();
              person.name 
          = name;
              person.age
          =age;
              person.say
          =function(){
                  alert(person.name
          +" "+person.age); 
              }
             return person;
          }

          var person1 = Person("xcp",23);
          var person2 = Person("lxm",24);
          person1.say();
          person2.say();
          //沒有解決對象識別的問題(即怎么知道對象的類型)

           

              b. 構造函數模式

          function Person(name,age){
              
          this.name = name;
              
          this.age = age;
              
          this.say = function(){
                   alert(
          this.name+" "+this.age);
              }
          }
          var person1 = new Person("xcp",23);
          var person2 = new Person("lxm",24);
          person1.say();
          person2.say();
          //直接將屬性附值于this;并且沒有顯示的創建和返回對象;必須用new來創建對象
          alert(person1 constructor Person);//true
          alert(person1 instanceof Person);  //true
              ->創建對象經歷以下步驟:
                 >創建一個新對象
                 >將構造函數的作用賦予新對象(因此this就指向了新對象)
                 >執行構造函數里面的代碼(添加屬性)
                 >返回新對象
              ->構造函數的問題
                 >使用構造函數的主要問題,就是每個方法都要在每個實例上重新創建一遍。例如我們上面的alert(person1.say==person2.say) //false
                   創建兩個同樣功能的Function()是完全沒有必要的;況且有this對象在,根本不用在執行代碼前就把函數綁定到特定對象上面。因些,大可以像以下面的代碼,通過把函數定義轉移到構造函數外部解決這個問題:
                  function Person(name,age){
                      this.name = name;
                      this.age = age;
                      this.say    = sayFunction(this.name,this.age);
                  }
                  function sayFunction(name,age){
                      alert(name+" "+age);
                  }
                  //這樣sayFunction就只會創建一次了 //即:alert(person1.say==person2.say) //true;
                 但問題也來了,如果有很多個方法,就要創建很多方法,這樣無論可讀性,適用性都不是很好

              c. 原型模式
                  >理解:我們創建的每一個對象,都有一個prototype屬性,這個屬性是一個對象,那么這個對象的作用是什么呢?所有prototype屬性都會自動獲得一個constructor(構造函數),這個屬性民包含一個指向prototype屬性在所函數的指針。Person.prototype.constructor指向Person。而通過這個構造函數,我們還可以繼承為原型添加其他屬性和方法。如圖:
              
              
                      .Person對象的prototype屬性指向Person原理對象
                      .Person原理對象的構造方法指向Person對象
                      .Person實例調用屬性和方法的順序:先查找Person對象是否具有此屬性和方法,如果Person對象具有則返回,如果沒有則先查詢Person的原型對像。推出:如果對象本身和原型對象同時具有某個函數和方法,以對象本身的屬性和方法為準。從這個可以體現出多個對象共享原型所保存的屬性和方法,以降低內存開消。但有的時候我們需要用原理對象的屬性屬性和方法去覆蓋原有對象的屬性的方法,這時我們采用刪除原有對象的屬性和方法:delete Person.name(刪除原有對象的name屬性).可以使用hasOwnProperty()和isPrototypeof();
                      
                   
                  
                  >原型與in操作符
                      .有兩種可能使用in操作:單獨使用("name" in person1)和在for..in里面。單獨使用in的時候對象能夠返回給定屬性時返回true(實例和原型)

                  >更加簡單的原型語法
                      function Person(){}
                      Person.prototype={
                          name:'age',
                          age:'23',  
                          say:function(){
                                  alert(this.name+" "+this.age);
                          }
                      }
                  //這樣寫下來就相當于重寫了對象的prototype對象,那原型對象里面的constructor屬性就沒有了.但如果我要得到constructor屬性怎么辦呢?
                      Person.prototype={
                          constructor:Person,//constructor=this,
                          name:'age',
                          age:'23',  
                          say:function(){
                                  alert(this.name+" "+this.age);
                          }   
                      }
           

                  >原型中的動態性           
              .由于原型中查找植的過程是一次性的,因此我們對原型對象所做的操作會立刻反映出來,如
                      function Person(){
                      }
                      Person p = new Person(); 
                      Person.prototype.say=function(){
                          alert("hi");
                      }
                      p.say(); //hi;這樣就可以立刻體現出來
                  
                      //再看看下面的例子
                      function Person(){}
                      Person p = new Person();
                      Person.prototype={
                          say:function(){
                              alert("hi");
                          }
                      }
                      p.say(); //error;
                      //這里為什么就出錯了呢,上一個例子不是好好的嗎?因為第一個例子,只是在prototype原型對象里面添加了一個屬性,我們在調用say的時候他會查找對象本身和原型對象并且是一次查詢(所以的代碼運行完畢),所以可以正常的到原型對象里面取出來;可是后面一個方法呢,聲明對象p他的時候他會找到_proto_指針指向原型對象;而我用Person.prototype也重構這個原型對象,就相當于切斷了constructor與是初始原型之間的連接關系,所有p的任何非本身屬性都會出錯!
           
                  >原生態對象模型
                      .原型類型不僅體現在自定義對象模型,包括原生的引用類型都是采用的這種模式,所以我們可以修改引用類型提供的方法和屬性。
                      String.prototype.trim=function(text){...}  //我們將跟String對象添加了一個trim()方法;

                  >原型對象的問題
                      .原型對象省略了為構造函數傳低參數這一細節,結果所以的實例對象取得的實例值都是一樣的,所以原型對象最大問題就是由本享本質導成的。
                      function Person(){}
                      Person p = new Person();
                      Person.prototype={
                          say:function(){
                              alert("hi");
                          }
                      }
                      p.say(); 

                      Person p1 = new Person();
                      Person p2 = new Person();
                      alert(p1.say==p2.say);//true
                      //所以接下來我們就是來 解決怎么合理共享,即合理使用prototype
           


                  >組合使用構造函數模式和原型模型(最常見的方法)
                      .構造函數模式用于定義:實例屬性(可以傳遞多個參數以確保必要性)
                       原型模式用于定義:共享的屬性和方法(這樣即保證了安全,也保證了最大的節約內存)
                      
                       例:function Person(name,age){
                                 this.name=name;
                                 this.age = age; 
                                 this.friend=["zhangsan","lisi","wangmazi"];
                             }
                             Person.prototype={
                                  constructor:Person,
                                  say:function(){
                                          alert(this.name+" "+this.age);
                                  }
                              }
                              Person p1 = new Person("xcp","23");
                              Person p2 = new Person("lxm","25");
                              alert(p1.friends==p2.friends);//false;
                              alert(p1.say==p2.say);//true;
           

                      .動態原型模型
                             function Person(name,age){
                                 this.name=name;
                                 this.age = age; 
                                 this.friend=["zhangsan","lisi","wangmazi"];
                                  if(typeof this.say !="function"){
                                       Person.prototype.say:function(){
                                           alert(this.name+" "+this.age);
                                       }
                                      //再一次強調這不能寫成
                                          Person.prototype={...}
                                      }
                            }


                  >寄生構造函數模型
                      .在工廠模式上加上參數
                       function Person(name,age){
                          var o = new Object();
                          o.name=name;
                          o.age = age;
                          o.say=function(){
                              alert(this.name+" "+this.age);
                          }
                          return o;
                      }








          2.繼承
              一般所有的OO都支持接口繼承和實現繼承,而ECMAScript函數沒有簽明的說話(空函數標志或者說接口的聲明方法),所以只有實現繼承,而實現繼承主要是通過原型對象來實現了。 
              >原型鏈(重要思想)
                  . 簡單回顧一下構造函數,原型和實例的關系
                      構造函數的prototype屬性(指針) -> 原型地址
                      原型的construtcotr屬性(指針)     ->   構造函數地址
                      實例的_proto_屬性(內部指針)           ->   原型對象 
                  .原型鏈的思想
                      我們將一個原型對象的指針指向另一個構造函數。如此層層遞進,就實現了實例和原型的鏈條
                      function SuperType(){
                              this.property = true;
                      }
                      SuperType.prototype.getSuperValue=function(){
                              return this.property;
                      }
                      function Subtype(){
                              this.subproperty = false;
                      }
                      SubType.prototype = new SuperType();  //子類的原型對象指向 生成的超類的子類(所以有一個_proto_指針指向超類的面型對象)
                      SubType.prototype.getSubValue=function(){
                              return this.subproperty;
                      }
                      Subtype instance = new Subtype();
                      instance.getSuperValue();
                       分析:

                           
                      .如果我們調用subType.getSuperValue()經過的步驟有:1)搜索實例本身 2)搜索SubType.prototype  3)搜索SuperType.prototype  4)所有的類都是實現Object,所以如果前面沒有找到的話,就還要搜索Object.prototype;所以真正的原型鏈應該是:
                      
                      //再到這來這來強調一下,不是原型對象的constructor被重寫了,而且_proto_指針被重寫了。
                  
                     . 兩種方法來確定實現跟原理之間的關系
                          //是否是指定類型的實現
                           alert(instance instanceof Object)//true;
                          alert(instance instanceof SuperType);//true;
                          alert(instance instanceof SubType);//true;
                          alert(instance instanceof Data);  //false;
                          
                          //指定類型的原型對象是否派生了此實例
                          alert(Object.prototype.isPrototype(instance));//true;
                          alert(SuperType.prototype.isPrototype(instance));//true;
                          alert(SubType.prototype.isProtoType(instance));//true
                  
                      .重寫超類方法
                          在SubType.prototype = new SuperType();  后面添加如下:
                          //重寫超類方法
                          SubType.prototype.getSuperValue=function(){
                              return false;
                          }

                          //不能寫成這樣
                          SubType.prototype=new Supertype();
                          SubType.prototype={
                               getSuperValue=function(){
                                  return false;
                              }
                          } //當一使用這一句話時,上一句話就立刻實效了,為什么呢?前面已經說過一這樣就切斷了SubType與SuperType.prototype的鏈條
           
                      .原型鏈的問題
                          最主要的問題就是包含引用類型類型的值,換一句就是訪問安全性問題;怎么說來著?前面介紹包含引用類型值的原型屬性民會被所有實例共享;而這也正是為什么要在構造函數中,而不是在原型對象中定義屬性的原因。但我們通過原型來實現繼承時,原型實際上會變成另一個類型的實例,于是,原先的實例屬性和原型對象里面所有的屬性和方法順利成章地變成了現在的原型屬性了,從而可以將屬性工享這就破壞了原有的安全性了。


                  >借用構造函數(也叫偽造對象和經典繼承->常用方式)
                      .無參數版
                          function SuperType(){
                              colors:["red","blue","green"];
                          }
                          function SubType(){
                              //實現繼承SuperType,為什么呢,因為在子類構造函數調用父類構造函數里面的call方法
                              SuperType.call(this);
                          }
                          var instance1 = new SubType();
                          instance1.colors.push("block");
                          alert(instance1.colors); //"red","blue","green","block";
                      
                          var instance2 = new SubType();
                          alert(instance2.colors);   //"red","blue","green"                                   
                      
                      .有參數版
                          function  SuperType(name){
                                  this.name = name;
                          }
                          function SubType(){
                              //繼承了SuperType,同時傳遞了參數    
                              SuperType.call(this,"xcp"); 
                              //實例屬性
                              this.age = 23;     
                          }
                          var instance = new SubType();
                          alert(instance.name); //xcp
                          alert(instance.age); //23
                        
                      .借用構造函數的問題
                          如果僅僅是借用構造函數,那么也將無法避免構造函數模式存在的問題-方法都在構造函數中定義,這樣些函數復用就無從談起了。

                  >組合繼承(也稱偽經典繼承),指的是將原型鏈和借用構造函數的技術結合在一起來,從而發揮者這宅的一種繼承模式
                      .實例(精典實例)
                          function SuperType(name){
                              this.name  = name;
                              this.colors = ["red","blue","green"];
                          }
                          SuperType.prototype.sayName=function(){
                              alret(this.name);
                          }
                          function SubType(name,age){
                              //繼承屬性
                              SuperType.call(this,name);
                                  
                              //添加新屬性
                              this.age = age;
                          }
                          
                          //繼承原型里面的屬性和方法
                          SubType.prototype = new SuperType();
                          //添加新方法
                          SubType.prototype.sayAge = function(){
                                  alert(this.age);
                          }

                          var instance1 = new SubType("xcp",23);
                          instance1.colors.push("black");
                          alert(instance1.colors); //red,blue,green,black
                          instance1.sayName();//xcp;
                          instance1.sayAge();//23;
                  
                          var instance2 = new SubType("lxm",25);
                          alert(instance2.colors); //red,blue,green
                          instance2.sayName();//lxm
                          instance2.sayAge();  //25

                          //組合繼承就避免了前面的原型鏈(父類所有的屬性和方法均作用子類的原型對象的屬性和方法,所以就存在數據安全問題了)和借用構造函數的缺陷(無共享屬性和方法機制);成為Javascript中最常用的繼承模式。而且,instanceof和isPrototypeOf()也能夠用于識別基于組合繼承創建的對象。
                         
                   >當然還包括:原型式繼承、寄生式繼承等






          3.總結
              >創建javascript對象的方式
                  .方法:
                      構造方法式<接收參數傳值實例屬性>+原型式<抽取公共共享的方式>
                  .實例:
                      function Person(name,age){
                          this.name=name;
                          this.age = age; 
                          this.friend=["zhangsan","lisi","wangmazi"];
                      }
                      Person.prototype={  //使用最簡對象完成
                           constructor:Person,
                           say:function(){
                               alert(this.name+" "+this.age);
                           }
                      }
                      Person p1 = new Person("xcp","23");
                      Person p2 = new Person("lxm","25");
                      alert(p1.friends==p2.friends);//false;
                      alert(p1.say==p2.say);//true;
              
              >繼承超類的方法
                  .方法
                      借用構造函數式<復用超類的實例變量,表示不同實例得到不變量不同>+原型鏈<復用超類的原型對象,意思就是子類的_proto_指針指同超類的原型對象>
                  .實例
                      function SuperType(name){
                              this.name  = name;
                              this.colors = ["red","blue","green"];
                          }
                          SuperType.prototype.sayName=function(){
                              alret(this.name);
                          }
                          function SubType(name,age){
                              //繼承屬性
                              SuperType.call(this,name);
                                  
                              //添加新屬性
                              this.age = age;
                          }
                          
                          //繼承原型里面的屬性和方法
                          SubType.prototype = new SuperType();
                          //添加新方法
                          SubType.prototype.sayAge = function(){
                                  alert(this.age);
                          }

                          var instance1 = new SubType("xcp",23);
                          instance1.colors.push("black");
                          alert(instance1.colors); //red,blue,green,black
                          instance1.sayName();//xcp;
                          instance1.sayAge();//23;
                  
                          var instance2 = new SubType("lxm",25);
                          alert(instance2.colors); //red,blue,green
                          instance2.sayName();//lxm
                          instance2.sayAge();  //25



          名稱: ?4C.ESL | .↗Evon
          口號: 遇到新問題?先要尋找一個方案乄而不是創造一個方案こ
          mail: 聯系我


          主站蜘蛛池模板: 靖江市| 福泉市| 长沙县| 正蓝旗| 松溪县| 南郑县| 镇康县| 柘荣县| 体育| 罗江县| 青川县| 出国| 平武县| 久治县| 余江县| 建水县| 股票| 宜章县| 建湖县| 胶南市| 洪江市| 余庆县| 涪陵区| 琼海市| 昌江| 陵川县| 陈巴尔虎旗| 巴林右旗| 佳木斯市| 磐安县| 大余县| 承德县| 台中县| 建水县| 庆城县| 汉川市| 平度市| 娱乐| 巨野县| 黑河市| 延长县|