1. 程式人生 > >【JavaScript】面向物件與原型

【JavaScript】面向物件與原型

ECMAScript有兩種開發模式:

    1、函式式(過程化)

    2、面向物件(OOP

但是ECMAScript沒有類的概念,因此與基於類的語言的物件也有所不同。

一、建立物件

         var box=new Object();

         box.name='lee';                          //屬性

        box.run=function(){                  //方法

                  returnthis.name+'執行中。。。';

           }

         alert(box.run());

關於工廠模式方法

因為物件是引用型別值,所以,每次更新這個物件,物件的源值都會改變,要想多整出幾個物件,就要再寫一個新的物件,從而產生2個不同的物件引用值,但是這樣每次建立一個新的物件,要寫很多程式碼,所以用到工廠模式方法。

程式碼:

         function createObject(name,age){

                var obj=new Object();

                obj.name=name;

                obj.age=age;

                obj.run=function(){

                     returnthis.name+this.age+'

執行中。。';

                };

                return obj;

          }

         var box1=createObject('lee',100);  

         var box2=createObject('kee',200);

         alert(box1.run());

        alert(box2.run());

工廠模式解決了重複例項化的問題。

關於建構函式

因為工廠模式解決了重複例項化的問題,但是還有一個問題就是識別問題,因為根本無法搞清楚他們到底是那個物件的例項,所以就採用建構函式方法來建立特定物件。

程式碼:

            function Box(name,age){

                     this.name=name;

                     this.age=age;

                     this.run=function(){

                                return this.name+this.age+'執行中';

                      }

           }

            var box1=newBox('lee'100);         //new Box()即可

            var box2=newBox('kee', 200);

             alert(box1.run());

             alert( box1  instantof Box);   //很清晰的識別他從屬於Box.

採用構造方法就解決了上面兩個問題,第一,重複例項化,第二,物件識別的問題,但是這裡了並沒有new Object(),為什麼可以例項化Box()?

採用構造方法,和使用工廠模式的方法不同之處

       1、建構函式方法沒有顯示的建立物件(new Object());

       2、直接將屬性和方法賦值給this物件。

       3沒有return語句。

建構函式方法有一些規範:

    1、函式名和例項化構造名相同且大寫(PS:非強制,但這麼寫有助於區分建構函式和普通函式)。

    2、通過建構函式建立物件,必須使用new運算子。

上面說了,在建構函式方法裡面沒有new Object(),沒有建立物件,那麼它在什麼地方建立了一個新的物件?

    1、使用了建構函式,並且new 建構函式(),那麼後臺就執行了new Object();

    2、將建構函式的作用域給新物件,(即new Object()創建出來的物件),而函式體內的this就代表newObject()出來的物件。

   3、返回新物件(後臺直接返回)

物件冒充呼叫:

為了改變作用域,就用到了物件冒出呼叫。

      var 0=new Object();

      Box.call(o,'jack',200)

      alert(0.run());

這樣物件0就可以呼叫box的方法了。

二、原型

我們建立的每一個函式都有一個prototype()屬性,這個屬性是一個物件,用途是包含可以由特定型別的所有例項共享的屬性和方法。

原型的建立:

方法一:

          functionBox(){}          //宣告一個建構函式

          Box.prototype.name='lee';

          Box.prototype.age=100;

          Box.prototype.run=function(){

                return.this.name+this.age+'執行中。。';

         }

方法二:

            function Box(){};

           Box.prototype={

                          name:'lee',

                          age:100,

                          run:function(){

                                   return this.name+this.age+'執行中。。';

                           }

                       };

進一步瞭解建構函式的宣告方式和原型模式的宣告方式,下面是圖示;



判斷一個物件是否指向了該建構函式的原型物件,可以使用isPrototypeOf()方法來測試。

     alert(Box.prototype.isPrototypeOf(box));

原型模式的執行流程:

    1、先查詢建構函式例項裡的屬性或方法,如果有,立即返回。

    2、如果建構函式例項裡沒有,就去它的原型物件裡找,如果有,就返回。

物件例項僅可以訪問原型中的值,不可更改。

判斷屬性是在建構函式還是在原型裡?

    alert(box.hasOwnProperty('name')); //在例項裡返回true.

原型物件不僅可以在自定義物件的情況下使用,而ECMAScipt內建的引用型別都可以使用這種方式,並且內建的引用型別本身也使用了原型。

因為原型模式的最大優點:共享,也是它的最大的缺點,所以,當我們要保持資料的特性,而不能共享時,我們就用到下面:

組合建構函式+原型模式:

  function Box(name,age){        //不共享的使用建構函式

         this.name=name;

         this.age=age;

        this.family=['father']

}

  Box.prototype={                  //共享時使用原型模式

            constructor:Box,

            runfunction(){

                     returnthis.name+this.age+this.family;

               }

}

為了封裝性更好,我們把建構函式和原型封裝在一起,叫做:

動態原型模式:

          functionBox(name,age){    //將所有的寫成封裝到函式體內

                  this.name=name;

                   this.age=age;

                 if(typeofthis.run!='function'){      //僅在第一次呼叫的初始化

                      Box.prototype.run=function(){

                           return this.name+this.age+'執行中。。。'

}

}

}

Varbox=new Box('lee',100);

Alert(box.run());

三、繼承

繼承是面向物件中的一個比較核心的概念。其他正統面嚮物件語言都會用兩種方式實現繼承,一個是介面實現,一個是繼承,而ECMAScript只支援繼承,不支援介面實現,而實現繼承的方式依靠原型鏈完成。



JavaScript裡,被繼承的函式稱為超型別(父類,基類),繼承的函式稱為子型別(子類,派生類)。繼承也有之前的問題,比如字面量重寫原型會中斷關係,使用引用型別的原型,子類還無法給超型別傳遞引數。

借用建構函式:

目的:

為了解決引用共享和超型別無法傳參的問題。



組合繼承:

目的:

在上面的基礎上,做到複用。

原型鏈+借用建構函式


組合繼承是JavaScript最常用的繼承模式,但有些小問題,就是超型別在使用過程中會被呼叫2次,一次是建立子型別的時候,另一次是在子型別建構函式的內部。

寄生組合繼承:

解決了組合繼承的2次呼叫的問題:




       最後再來一張圖鎮樓~~