javascript設計模式交流(二) ——Prototype Pattern

來源:互聯網
上載者:User
Prototype Pattern是一種建立型模式,在GoF Book中它的意圖被描述成用原型執行個體指定建立對象的種類,並通過拷貝這些原型建立新的對象。

Prototype Pattern本身實際上非常簡單,任何一個提供了clone()方法的對象都可以成為原型對象,所有通過它複製的對象都屬於一類對象。在靜態語言中,這一模式被用於運行時指定物件類型,相比原廠模式,這一模式無需建立與類層次平行的工廠類結構,實現上要方便得多。

clone的三種js實現

在js中,clone方法的實現並不困難,對js的6種基本類型來說 string boolean undefined null number皆可直接用=賦值,唯一麻煩的是object。
對object 我們可以用clone其所有成員的方式複製 作為方法的函數可以這樣定義:

[複製]

Code:


        function clone()
        {
                var ret=new Object();
                for(var p in this)
                {
                        ret[p]=this[p];
                }
        } 

但我們顯然面對著一個問題:this[p]也可能是一個object 所以很可能我們需要用遞迴來實現deepClone

[複製]

Code:


        function deepClone()
        {
                var ret=new Object();
                for(var p in this)
                {
                        ret[p]=deepClone.call(this[p]);
                }
        } 

對js來說 實現clone還有另外一種方式,在javascript中,構造器的prototype屬性指明了某一類的原型,當執行個體化時,這一原型被作為對象的原型使用。特別地,這個prototype對象也可能是從某一原型構造出來的,這形成了一個類似繼承的結構,所以javascript的面向原型特性又被稱作原型繼承(儘管我很不贊同這種做法,還是要提一下)。

    回到我們前面的Prototype Pattern,javascript天生的引用型原型繼承為我們提供了另外一種clone的實現方式:

[複製]

Code:


        function prototypeClone()
        {
                var tmp=function(){};
                tmp.prototype=this;
                return new tmp;
        } 

這樣clone出來的對象唯讀地共用一個原型的屬性,它的最大優勢是速度非常快,當我們希望快速複製大型物件時,可以使用這種方式,但是它會造成訪問速度降低,而且它即時反映父節點的變化。

內建對象的clone

    但是,到這裡為止,我們還沒有考慮內建對象,內建對象不能用普通方法clone 我們要考慮的內建對象有這麼幾個:
        Function Array String Boolean Number Date
    RegExp Error和Math沒有需要clone的情境 所以不在我們的考慮之中。

    對Function來說,完全產生一個副本是不可能的,因為我們無法保證構造的函數跟原來的函數在同一範圍,但是不包含範圍的實現是很容易的:
        eval(this);
或者使用Function構造
        return Function(new String("return ")+this)();

Function本身是個Object 因此必須加上Object的clone 實現functionPrototypeClone需要一點小花招

[複製]

Code:


        function functionClone()
        {
                var ret=Function(new String("return ")+this)();
                for(var p in this)
                {
                        ret[p]=this[p];
                }
        }
        function functionDeepClone()
        {
                var ret=Function(new String("return ")+this)()
                for(var p in this)
                {
                        ret[p]=deepClone.call(this[p]);
                }
        }
        function functionPrototypeClone()
        {                
                var tmp=Function.prototype;
                Function.prototype=this;
                var ret=(new Function(new String("return ")+this))();
                Function.prototype=tmp;
                return ret;
        } 

Array只要保證length正確就可以了

[複製]

Code:


        function arrayClone()
        {
                var ret=new Array();
                for(var p in this)
                {
                        ret[p]=this[p];
                }
        }
        function arrayDeepClone()
        {
                var ret=new Array();
                for(var p in this)
                {
                        ret[p]=deepClone.call(this[p]);
                }
        }
        function arrayPrototypeClone()
        {                
                var tmp=Array.prototype;
                Array.prototype=this;
                var ret=new Array();
                Array.prototype=tmp;
                return ret;
        } 

Date對象提供了getTime 所以可以很容易實現

[複製]

Code:


        function arrayClone()
        {
                var ret=new Date();
                ret.setTime(this.getTime());
                for(var p in this)
                {
                        ret[p]=this[p];
                }
        }
        function arrayDeepClone()
        {
                var ret=new Date();
                ret.setTime(this.getTime());

                for(var p in this)
                {
                        ret[p]=deepClone.call(this[p]);
                }
        }
        function arrayPrototypeClone()
        {                
                var tmp=Date.prototype;
                Date.prototype=this;
                var ret=new Date();
                ret.setTime(this.getTime());
                Date.prototype=tmp;
                return ret;
        } 

String Boolean Number都是唯讀對象,所以只要=就可以了。

    前面討論了三種Clone的實現方法,它們各自具有適合的語義環境,比如對一個數組來說 若是把它理解為一個集合Collection 則應該使用淺clone(假如集合A是B的子集,則應保證A.clone()亦是B的子集),若是把它理解為一個向量Vector,則應使用深clone(保證對向量A的分量操作不應影響向量A.clone()的分量)。prototypeClone的一個最常見的應用情境是深度優先搜尋演算法演算法,為了擴充解空間樹,我們通常需要快速的構造一個副本,如果使用clone或者deepClone 這將非常慢,而深度優先搜尋的特點是在位元組點被銷毀之前,父節點不會變化,所以prototypeClone是非常合適的。

附:Prototype-oriented Programming和Prototype Pattern
面向原型的語言思想跟原型模式是完全一致的:從同一原型clone出來的對象就是一類對象。Prototype-oriented的語言對這種模式提供了語言層級的支援,即所有"類"的定義都是通過指定該類的一個原型來實現的(Class-Based Programming是通過類結構聲明來描述一類對象,meta-class則是通過構造一個"類對象"來描述一類對象)。每次執行個體話就clone一次原型,然而這種方式會造成資訊的冗餘:所有對象都持有原型對象的一個clone的副本,而且一旦某一對象被構造,修改原型不會對它造成任何影響,這對於希望在程式中統一改變某一類對象的人來說很不方便。於是,一種變通的方法產生了:引用型原型對象,與之相對,原來的原型對象使用方法被稱為 複製型原型對象。引用型原型對象不再clone原型,而是儲存一個指向原型的指標,當訪問屬性時,首先檢查自己的屬性,當查到不存在時,則通過指標向原型索取相應屬性。而引用型原型就是javascript的面向原型特性的實現方式。

PS.本文徵求過本系列原作者意見了 不是盜版滴 呵呵
 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.