一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

悟透JavaScript(結(jié))

 WindySky 2009-07-02

原型擴展

    想必君的悟性極高,可能你會這樣想:如果在JavaScript內(nèi)置的那些如Object和Function等函數(shù)的prototype上添加些新的方法和屬性,是不是就能擴展JavaScript的功能呢?

    那么,恭喜你,你得到了!

    在AJAX技術(shù)迅猛發(fā)展的今天,許多成功的AJAX項目的JavaScript運行庫都大量擴展了內(nèi)置函數(shù)的prototype功能。比如微軟的 ASP.NET AJAX,就給這些內(nèi)置函數(shù)及其prototype添加了大量的新特性,從而增強了JavaScript的功能。

    我們來看一段摘自MicrosoftAjax.debug.js中的代碼:

String.prototype.trim  =   function  String$trim() {
    
if  (arguments.length  !==   0 throw  Error.parameterCount();
    
return   this .replace( / ^\s+|\s+$ / g,  '' );
}


    這段代碼就是給內(nèi)置String函數(shù)的prototype擴展了一個trim方法,于是所有的String類對象都有了trim方法了。有了這個擴展,今后要去除字符串兩段的空白,就不用再分別處理了,因為任何字符串都有了這個擴展功能,只要調(diào)用即可,真的很方便。

    當然,幾乎很少有人去給Object的prototype添加方法,因為那會影響到所有的對象,除非在你的架構(gòu)中這種方法的確是所有對象都需要的。

    前兩年,微軟在設計AJAX類庫的初期,用了一種被稱為“閉包”(closure )的技術(shù)來模擬“類”。其大致模型如下:

     function  Person(firstName, lastName, age)
    {
        
// 私有變量:
         var  _firstName  =  firstName;
        
var  _lastName  =  lastName;

        
// 公共變量:
         this .age  =  age;

        
// 方法:
         this .getName  =   function ()
        {
            
return (firstName  +   "   "   +  lastName);
        };
        
this .SayHello  =   function ()
        {
            alert(
" Hello, I'm  "   +  firstName  +   "   "   +  lastName);
        };
    };
    
    
var  BillGates  =   new  Person( " Bill " " Gates " 53 );
    
var  SteveJobs  =   new  Person( " Steve " " Jobs " 53 );
    
    BillGates.SayHello();
    SteveJobs.SayHello();
    alert(BillGates.getName() 
+   "   "   +  BillGates.age);
    alert(BillGates.firstName);     
// 這里不能訪問到私有變量


    很顯然,這種模型的類描述特別象C#語言的描述形式,在一個構(gòu)造函數(shù)里依次定義了私有成員、公共屬性和可用的方法,顯得非常優(yōu)雅嘛。特別是“閉包”機制可以模擬對私有成員的保護機制,做得非常漂亮。

    所謂的“閉包”,就是在構(gòu)造函數(shù)體內(nèi)定義另外的函數(shù)作為目標對象的方法函數(shù),而這個對象的方法函數(shù)反過來引用外層外層函數(shù)體中的臨時變量。這使得只要目標對象在生存期內(nèi)始終能保持其方法,就能間接保持原構(gòu)造函數(shù)體當時用到的臨時變量值。盡管最開始的構(gòu)造函數(shù)調(diào)用已經(jīng)結(jié)束,臨時變量的名稱也都消失了,但在目標對象的方法內(nèi)卻始終能引用到該變量的值,而且該值只能通這種方法來訪問。即使再次調(diào)用相同的構(gòu)造函數(shù),但只會生成新對象和方法,新的臨時變量只是對應新的值,和上次那次調(diào)用的是各自獨立的。的確很巧妙!

    但是前面我們說過,給每一個對象設置一份方法是一種很大的浪費。還有,“閉包”這種間接保持變量值的機制,往往會給JavaSript的垃圾回收器制造難題。特別是遇到對象間復雜的循環(huán)引用時,垃圾回收的判斷邏輯非常復雜。無獨有偶,IE瀏覽器早期版本確實存在JavaSript垃圾回收方面的內(nèi)存泄漏問題。再加上“閉包”模型在性能測試方面的表現(xiàn)不佳,微軟最終放棄了“閉包”模型,而改用“原型”模型。正所謂“有得必有失”嘛。

    原型模型需要一個構(gòu)造函數(shù)來定義對象的成員,而方法卻依附在該構(gòu)造函數(shù)的原型上。大致寫法如下:

     // 定義構(gòu)造函數(shù)
     function  Person(name)
    {
        
this .name  =  name;    // 在構(gòu)造函數(shù)中定義成員
    };
    
    
// 方法定義到構(gòu)造函數(shù)的prototype上
    Person.prototype.SayHello  =   function ()
    {
        alert(
" Hello, I'm  "   +   this .name);
    };    
    
    
// 子類構(gòu)造函數(shù)
     function  Employee(name, salary)
    {
        Person.call(
this , name);     // 調(diào)用上層構(gòu)造函數(shù)
         this .salary  =  salary;        // 擴展的成員
    };
    
    
// 子類構(gòu)造函數(shù)首先需要用上層構(gòu)造函數(shù)來建立prototype對象,實現(xiàn)繼承的概念
    Employee.prototype  =   new  Person()    // 只需要其prototype的方法,此對象的成員沒有任何意義!
    
    
// 子類方法也定義到構(gòu)造函數(shù)之上
    Employee.prototype.ShowMeTheMoney  =   function ()
    {
        alert(
this .name  +   "  $ "   +   this .salary);
    };
    
    
var  BillGates  =   new  Person( " Bill Gates " );
    BillGates.SayHello();    
    
    
var  SteveJobs  =   new  Employee( " Steve Jobs " 1234 );
    SteveJobs.SayHello();
    SteveJobs.ShowMeTheMoney();

    原型類模型雖然不能模擬真正的私有變量,而且也要分兩部分來定義類,顯得不怎么“優(yōu)雅”。不過,對象間的方法是共享的,不會遇到垃圾回收問題,而且性能優(yōu)于“閉包”模型。正所謂“有失必有得”嘛。

    在原型模型中,為了實現(xiàn)類繼承,必須首先將子類構(gòu)造函數(shù)的prototype設置為一個父類的對象實例。創(chuàng)建這個父類對象實例的目的就是為了構(gòu)成原型鏈,以起到共享上層原型方法作用。但創(chuàng)建這個實例對象時,上層構(gòu)造函數(shù)也會給它設置對象成員,這些對象成員對于繼承來說是沒有意義的。雖然,我們也沒有給構(gòu)造函數(shù)傳遞參數(shù),但確實創(chuàng)建了若干沒有用的成員,盡管其值是undefined,這也是一種浪費啊。

    唉!世界上沒有完美的事情??!

原型真諦

    正當我們感概萬分時,天空中一道紅光閃過,祥云中出現(xiàn)了觀音菩薩。只見她手持玉凈瓶,輕拂翠柳枝,灑下幾滴甘露,頓時讓JavaScript又添新的靈氣。

    觀音灑下的甘露在JavaScript的世界里凝結(jié)成塊,成為了一種稱為“語法甘露”的東西。這種語法甘露可以讓我們編寫的代碼看起來更象對象語言。

    要想知道這“語法甘露”為何物,就請君側(cè)耳細聽。

    在理解這些語法甘露之前,我們需要重新再回顧一下JavaScript構(gòu)造對象的過程。

    我們已經(jīng)知道,用 var anObject = new aFunction() 形式創(chuàng)建對象的過程實際上可以分為三步:第一步是建立一個新對象;第二步將該對象內(nèi)置的原型對象設置為構(gòu)造函數(shù)prototype引用的那個原型對象;第三步就是將該對象作為this參數(shù)調(diào)用構(gòu)造函數(shù),完成成員設置等初始化工作。對象建立之后,對象上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關(guān),與構(gòu)造函數(shù)再扯不上關(guān)系了。換句話說,構(gòu)造函數(shù)只是在創(chuàng)建對象時起到介紹原型對象和初始化對象兩個作用。

    那么,我們能否自己定義一個對象來當作原型,并在這個原型上描述類,然后將這個原型設置給新創(chuàng)建的對象,將其當作對象的類呢?我們又能否將這個原型中的一個方法當作構(gòu)造函數(shù),去初始化新建的對象呢?例如,我們定義這樣一個原型對象:

     var  Person  =    // 定義一個對象來作為原型類
    {
        Create: 
function (name, age)   // 這個當構(gòu)造函數(shù)
        {
            
this .name  =  name;
            
this .age  =  age;
        },
        SayHello: 
function ()   // 定義方法
        {
            alert(
" Hello, I'm  "   +   this .name);
        },
        HowOld: 
function ()   // 定義方法
        {
            alert(
this .name  +   "  is  "   +   this .age  +   "  years old. " );
        }
    };

    這個JSON形式的寫法多么象一個C#的類啊!既有構(gòu)造函數(shù),又有各種方法。如果可以用某種形式來創(chuàng)建對象,并將對象的內(nèi)置的原型設置為上面這個“類”對象,不就相當于創(chuàng)建該類的對象了嗎?

    但遺憾的是,我們幾乎不能訪問到對象內(nèi)置的原型屬性!盡管有些瀏覽器可以訪問到對象的內(nèi)置原型,但這樣做的話就只能限定了用戶必須使用那種瀏覽器。這也幾乎不可行。

    那么,我們可不可以通過一個函數(shù)對象來做媒介,利用該函數(shù)對象的prototype屬性來中轉(zhuǎn)這個原型,并用new操作符傳遞給新建的對象呢?

    其實,象這樣的代碼就可以實現(xiàn)這一目標:

     function  anyfunc(){};            // 定義一個函數(shù)軀殼
    anyfunc.prototype  =  Person;      // 將原型對象放到中轉(zhuǎn)站prototype
     var  BillGates  =   new  anyfunc();   // 新建對象的內(nèi)置原型將是我們期望的原型對象

    不過,這個anyfunc函數(shù)只是一個軀殼,在使用過這個軀殼之后它就成了多余的東西了,而且這和直接使用構(gòu)造函數(shù)來創(chuàng)建對象也沒啥不同,有點不爽。

    可是,如果我們將這些代碼寫成一個通用函數(shù),而那個函數(shù)軀殼也就成了函數(shù)內(nèi)的函數(shù),這個內(nèi)部函數(shù)不就可以在外層函數(shù)退出作用域后自動消亡嗎?而且,我們可以將原型對象作為通用函數(shù)的參數(shù),讓通用函數(shù)返回創(chuàng)建的對象。我們需要的就是下面這個形式:

     function  New(aClass, aParams)     // 通用創(chuàng)建函數(shù)
    {
        
function  new_()      // 定義臨時的中轉(zhuǎn)函數(shù)殼
        {
            aClass.Create.apply(
this , aParams);    // 調(diào)用原型中定義的的構(gòu)造函數(shù),中轉(zhuǎn)構(gòu)造邏輯及構(gòu)造參數(shù)
        };
        new_.prototype 
=  aClass;     // 準備中轉(zhuǎn)原型對象
         return   new  new_();           // 返回建立最終建立的對象
    };
    
    
var  Person  =          // 定義的類
    {
        Create: 
function (name, age)
        {
            
this .name  =  name;
            
this .age  =  age;
        },
        SayHello: 
function ()
        {
            alert(
" Hello, I'm  "   +   this .name);
        },
        HowOld: 
function ()
        {
            alert(
this .name  +   "  is  "   +   this .age  +   "  years old. " );
        }
    };
    
    
var  BillGates  =  New(Person, [ " Bill Gates " 53 ]);   // 調(diào)用通用函數(shù)創(chuàng)建對象,并以數(shù)組形式傳遞構(gòu)造參數(shù)
    BillGates.SayHello();
    BillGates.HowOld();

    alert(BillGates.constructor 
==  Object);      // 輸出:true

    這里的通用函數(shù)New()就是一個“語法甘露”!這個語法甘露不但中轉(zhuǎn)了原型對象,還中轉(zhuǎn)了構(gòu)造函數(shù)邏輯及構(gòu)造參數(shù)。

    有趣的是,每次創(chuàng)建完對象退出New函數(shù)作用域時,臨時的new_函數(shù)對象會被自動釋放。由于new_的prototype屬性被設置為新的原型對象,其原來的原型對象和new_之間就已解開了引用鏈,臨時函數(shù)及其原來的原型對象都會被正確回收了。上面代碼的最后一句證明,新創(chuàng)建的對象的 constructor屬性返回的是Object函數(shù)。其實新建的對象自己及其原型里沒有constructor屬性,那返回的只是最頂層原型對象的構(gòu)造函數(shù),即Object。

    有了New這個語法甘露,類的定義就很像C#那些靜態(tài)對象語言的形式了,這樣的代碼顯得多么文靜而優(yōu)雅?。?br>
    當然,這個代碼僅僅展示了“語法甘露”的概念。我們還需要多一些的語法甘露,才能實現(xiàn)用簡潔而優(yōu)雅的代碼書寫類層次及其繼承關(guān)系。好了,我們再來看一個更豐富的示例吧:

     // 語法甘露:
     var  object  =      // 定義小寫的object基本類,用于實現(xiàn)最基礎(chǔ)的方法等
    {
        isA: 
function (aType)    // 一個判斷類與類之間以及對象與類之間關(guān)系的基礎(chǔ)方法
        {
            
var  self  =   this ;
            
while (self)
            {
                
if  (self  ==  aType)
                  
return   true ;
                self 
=  self.Type;
            };
            
return   false ;
        }
    };
    
    
function  Class(aBaseClass, aClassDefine)     // 創(chuàng)建類的函數(shù),用于聲明類及繼承關(guān)系
    {
        
function  class_()    // 創(chuàng)建類的臨時函數(shù)殼
        {
            
this .Type  =  aBaseClass;     // 我們給每一個類約定一個Type屬性,引用其繼承的類
             for ( var  member  in  aClassDefine)
                
this [member]  =  aClassDefine[member];     // 復制類的全部定義到當前創(chuàng)建的類
        };
        class_.prototype 
=  aBaseClass;
        
return   new  class_();
    };
    
    
function  New(aClass, aParams)    // 創(chuàng)建對象的函數(shù),用于任意類的對象創(chuàng)建
    {
        
function  new_()      // 創(chuàng)建對象的臨時函數(shù)殼
        {
            
this .Type  =  aClass;     // 我們也給每一個對象約定一個Type屬性,據(jù)此可以訪問到對象所屬的類
             if  (aClass.Create)
              aClass.Create.apply(
this , aParams);    // 我們約定所有類的構(gòu)造函數(shù)都叫Create,這和DELPHI比較相似
        };
        new_.prototype 
=  aClass;
        
return   new  new_();
    };

    
// 語法甘露的應用效果:    
     var  Person  =  Class(object,       // 派生至object基本類
    {
        Create: 
function (name, age)
        {
            
this .name  =  name;
            
this .age  =  age;
        },
        SayHello: 
function ()
        {
            alert(
" Hello, I'm  "   +   this .name  +   " "   +   this .age  +   "  years old. " );
        }
    });
    
    
var  Employee  =  Class(Person,     // 派生至Person類,是不是和一般對象語言很相似?
    {
        Create: 
function (name, age, salary)
        {
            Person.Create.call(
this , name, age);   // 調(diào)用基類的構(gòu)造函數(shù)
             this .salary  =  salary;
        },
        ShowMeTheMoney: 
function ()
        {
            alert(
this .name  +   "  $ "   +   this .salary);
        }
    });

    
var  BillGates  =  New(Person, [ " Bill Gates " 53 ]);
    
var  SteveJobs  =  New(Employee, [ " Steve Jobs " 53 1234 ]);
    BillGates.SayHello();
    SteveJobs.SayHello();
    SteveJobs.ShowMeTheMoney();
    
    
var  LittleBill  =  New(BillGates.Type, [ " Little Bill " 6 ]);    // 根據(jù)BillGate的類型創(chuàng)建LittleBill
    LittleBill.SayHello();
    
    alert(BillGates.isA(Person));       
// true
    alert(BillGates.isA(Employee));      // false
    alert(SteveJobs.isA(Person));        // true
    alert(Person.isA(Employee));         // false
    alert(Employee.isA(Person));         // true

    “語法甘露”不用太多,只要那么一點點,就能改觀整個代碼的易讀性和流暢性,從而讓代碼顯得更優(yōu)雅。有了這些語法甘露,JavaScript就很像一般對象語言了,寫起代碼了感覺也就爽多了!

    令人高興的是,受這些甘露滋養(yǎng)的JavaScript程序效率會更高。因為其原型對象里既沒有了毫無用處的那些對象級的成員,而且還不存在 constructor屬性體,少了與構(gòu)造函數(shù)間的牽連,但依舊保持了方法的共享性。這讓JavaScript在追溯原型鏈和搜索屬性及方法時,少費許多工夫啊。

    我們就把這種形式稱為“甘露模型”吧!其實,這種“甘露模型”的原型用法才是符合prototype概念的本意,才是的JavaScript原型的真諦!

    想必微軟那些設計AJAX架構(gòu)的工程師看到這個甘露模型時,肯定后悔沒有早點把AJAX部門從美國搬到咱中國的觀音廟來,錯過了觀音菩薩的點化。當然,我們也只能是在代碼的示例中,把Bill Gates當作對象玩玩,真要讓他放棄上帝轉(zhuǎn)而皈依我佛肯定是不容易的,機緣未到啊!如果哪天你在微軟新出的AJAX類庫中看到這種甘露模型,那才是真正的緣分!

編程的快樂

    在軟件工業(yè)迅猛發(fā)展的今天,各式各樣的編程語言層出不窮,新語言的誕生,舊語言的演化,似乎已經(jīng)讓我們眼花繚亂。為了適應面向?qū)ο缶幊痰某绷鳎琂avaScript語言也在向完全面向?qū)ο蟮姆较虬l(fā)展,新的JavaScript標準已經(jīng)從語義上擴展了許多面向?qū)ο蟮男略亍Ec此相反的是,許多靜態(tài)的對象語言也在向JavaScript的那種簡潔而幽雅的方向發(fā)展。例如,新版本的C#語言就吸收了JSON那樣的簡潔表示法,以及一些其他形式的 JavaScript特性。

    我們應該看到,隨著RIA(強互聯(lián)應用)的發(fā)展和普及,AJAX技術(shù)也將逐漸淡出江湖,JavaScript也將最終消失或演化成其他形式的語言。但不管編程語言如何發(fā)展和演化,編程世界永遠都會在“數(shù)據(jù)”與“代碼”這千絲萬縷的糾纏中保持著無限的生機。只要我們能看透這一點,我們就能很容易地學習和理解軟件世界的各種新事物。不管是已熟悉的過程式編程,還是正在發(fā)展的函數(shù)式編程,以及未來量子糾纏態(tài)的大規(guī)模并行式編程,我們都有足夠的法力來化解一切復雜的難題。

    佛最后淡淡地說:只要我們放下那些表面的“類”,放下那些對象的“自我”,就能達到一種“對象本無根,類型亦無形”的境界,從而將自我融入到整個宇宙的生命輪循環(huán)中。我們將沒有自我,也沒有自私的欲望,你就是我,我就是你,你中有我,我中有你。這時,我們再看這生機勃勃的編程世界時,我們的內(nèi)心將自然生起無限的慈愛之心,這種慈愛之心不是虛偽而是真誠的。關(guān)愛他人就是關(guān)愛自己,就是關(guān)愛這世界中的一切。那么,我們的心是永遠快樂的,我們的程序是永遠快樂的,我們的類是永遠快樂的,我們的對象也是永遠快樂的。這就是編程的極樂!

    說到這里,在座的比丘都猶如醍醐灌頂,心中豁然開朗??纯醋筮呥@位早已喜不自禁,再看看右邊那位也是心花怒放。

    驀然回首時,唯見君拈花微笑...

 

 

原著:李戰(zhàn)(leadzen)   http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    91后入中出内射在线| 2019年国产最新视频| 久久91精品国产亚洲| 日本一区二区三区黄色| 亚洲伦片免费偷拍一区| 欧美一本在线免费观看| 日本不卡在线一区二区三区| 高清亚洲精品中文字幕乱码| 成人欧美一区二区三区视频| 国产精品日韩欧美一区二区| 有坂深雪中文字幕亚洲中文| 国产又粗又深又猛又爽又黄| 风间中文字幕亚洲一区| 99热中文字幕在线精品| 亚洲黄色在线观看免费高清| 欧美六区视频在线观看| 日本特黄特色大片免费观看| 欧美日韩中黄片免费看| 国产又色又粗又黄又爽| 日本三区不卡高清更新二区| 日本加勒比中文在线观看| 欧美同性视频免费观看| 夜夜嗨激情五月天精品| 久久99国产精品果冻传媒| 欧美有码黄片免费在线视频| 亚洲少妇人妻一区二区| 亚洲精品国产主播一区| 亚洲天堂男人在线观看| 日韩美女偷拍视频久久| 五月的丁香婷婷综合网| 中文字幕91在线观看| 久久机热频这里只精品| 少妇高潮呻吟浪语91| 欧美精品激情视频一区| 日本高清二区视频久二区| 99热在线精品视频观看| 日韩精品综合免费视频| 99久久人妻中文字幕| 亚洲一区二区三区国产| 91超精品碰国产在线观看| 欧美一区二区三区播放|