小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

悟透JavaScript(續(xù))

 WindySky 2009-07-02

構(gòu)造對(duì)象
 
    好了,接下我們來(lái)討論一下對(duì)象的另一種創(chuàng)建方法。

    除JSON外,在JavaScript中我們可以使用new操作符結(jié)合一個(gè)函數(shù)的形式來(lái)創(chuàng)建對(duì)象。例如:

     function  MyFunc() {};          // 定義一個(gè)空函數(shù)
     var  anObj  =   new  MyFunc();   // 使用new操作符,借助MyFun函數(shù),就創(chuàng)建了一個(gè)對(duì)象


    JavaScript的這種創(chuàng)建對(duì)象的方式可真有意思,如何去理解這種寫(xiě)法呢?
 
   其實(shí),可以把上面的代碼改寫(xiě)成這種等價(jià)形式:

     function  MyFunc(){};
    
var  anObj  =  {};      // 創(chuàng)建一個(gè)對(duì)象
    MyFunc.call(anObj);  // 將anObj對(duì)象作為this指針調(diào)用MyFunc函數(shù)


    我們就可以這樣理解,JavaScript先用new操作符創(chuàng)建了一個(gè)對(duì)象,緊接著就將這個(gè)對(duì)象作為this參數(shù)調(diào)用了后面的函數(shù)。其實(shí),JavaScript內(nèi)部就是這么做的,而且任何函數(shù)都可以被這樣調(diào)用!但從 “anObj = new MyFunc()” 這種形式,我們又看到一個(gè)熟悉的身影,C++和C#不就是這樣創(chuàng)建對(duì)象的嗎?原來(lái),條條大路通靈山,殊途同歸?。?br>
    君看到此處也許會(huì)想,我們?yōu)槭裁床豢梢园堰@個(gè)MyFunc當(dāng)作構(gòu)造函數(shù)呢?恭喜你,答對(duì)了!JavaScript也是這么想的!請(qǐng)看下面的代碼:

 1       function  Person(name)    // 帶參數(shù)的構(gòu)造函數(shù)
 2      {
 3           this .name  =  name;    // 將參數(shù)值賦給給this對(duì)象的屬性
 4           this .SayHello  =   function () {alert( " Hello, I'm  "   +   this .name);};    // 給this對(duì)象定義一個(gè)SayHello方法。
 5      };
 6  
 7       function  Employee(name, salary)      // 子構(gòu)造函數(shù)
 8      {
 9          Person.call( this , name);         // 將this傳給父構(gòu)造函數(shù)
10           this .salary  =  salary;        // 設(shè)置一個(gè)this的salary屬性
11           this .ShowMeTheMoney  =   function () {alert( this .name  +   "  $ "   +   this .salary);};   // 添加ShowMeTheMoney方法。
12      };
13      
14       var  BillGates  =   new  Person( " Bill Gates " );    // 用Person構(gòu)造函數(shù)創(chuàng)建BillGates對(duì)象
15       var  SteveJobs  =   new  Employee( " Steve Jobs " 1234 );    // 用Empolyee構(gòu)造函數(shù)創(chuàng)建SteveJobs對(duì)象
16  
17      BillGates.SayHello();    // 顯示:I'm Bill Gates
18      SteveJobs.SayHello();    // 顯示:I'm Steve Jobs
19      SteveJobs.ShowMeTheMoney();    // 顯示:Steve Jobs $1234
20  
21      alert(BillGates.constructor  ==  Person);   // 顯示:true
22      alert(SteveJobs.constructor  ==  Employee);   // 顯示:true
23      
24      alert(BillGates.SayHello  ==  SteveJobs.SayHello);  // 顯示:false


    這段代碼表明,函數(shù)不但可以當(dāng)作構(gòu)造函數(shù),而且還可以帶參數(shù),還可以為對(duì)象添加成員和方法。其中的第9行,Employee構(gòu)造函數(shù)又將自己接收的 this作為參數(shù)調(diào)用Person構(gòu)造函數(shù),這就是相當(dāng)于調(diào)用基類的構(gòu)造函數(shù)。第21、22行還表明這樣一個(gè)意思:BillGates是由Person構(gòu)造的,而SteveJobs是由Employee構(gòu)造的。對(duì)象內(nèi)置的constructor屬性還指明了構(gòu)造對(duì)象所用的具體函數(shù)!

    其實(shí),如果你愿意把函數(shù)當(dāng)作“類”的話,她就是“類”,因?yàn)樗緛?lái)就有“類”的那些特征。難道不是嗎?她生出的兒子各個(gè)都有相同的特征,而且構(gòu)造函數(shù)也與類同名嘛!

    但要注意的是,用構(gòu)造函數(shù)操作this對(duì)象創(chuàng)建出來(lái)的每一個(gè)對(duì)象,不但具有各自的成員數(shù)據(jù),而且還具有各自的方法數(shù)據(jù)。換句話說(shuō),方法的代碼體(體現(xiàn)函數(shù)邏輯的數(shù)據(jù))在每一個(gè)對(duì)象中都存在一個(gè)副本。盡管每一個(gè)代碼副本的邏輯是相同的,但對(duì)象們確實(shí)是各自保存了一份代碼體。上例中的最后一句說(shuō)明了這一實(shí)事,這也解釋了JavaScript中的函數(shù)就是對(duì)象的概念。

    同一類的對(duì)象各自有一份方法代碼顯然是一種浪費(fèi)。在傳統(tǒng)的對(duì)象語(yǔ)言中,方法函數(shù)并不象JavaScript那樣是個(gè)對(duì)象概念。即使也有象函數(shù)指針、方法指針或委托那樣的變化形式,但其實(shí)質(zhì)也是對(duì)同一份代碼的引用。一般的對(duì)象語(yǔ)言很難遇到這種情況。

    不過(guò),JavaScript語(yǔ)言有大的靈活性。我們可以先定義一份唯一的方法函數(shù)體,并在構(gòu)造this對(duì)象時(shí)使用這唯一的函數(shù)對(duì)象作為其方法,就能共享方法邏輯。例如:

     function  SayHello()      // 先定義一份SayHello函數(shù)代碼
    {
        alert(
" Hello, I'm  "   +   this .name);
    };
    
    
function  Person(name)    // 帶參數(shù)的構(gòu)造函數(shù)
    {
        
this .name  =  name;    // 將參數(shù)值賦給給this對(duì)象的屬性
         this .SayHello  =  SayHello;    // 給this對(duì)象SayHello方法賦值為前面那份SayHello代碼。
    };

    
var  BillGates  =   new  Person( " Bill Gates " );    // 創(chuàng)建BillGates對(duì)象
     var  SteveJobs  =   new  Person( " Steve Jobs " );    // 創(chuàng)建SteveJobs對(duì)象
    
    alert(BillGates.SayHello 
==  SteveJobs.SayHello);  // 顯示:true


    其中,最后一行的輸出結(jié)果表明兩個(gè)對(duì)象確實(shí)共享了一個(gè)函數(shù)對(duì)象。雖然,這段程序達(dá)到了共享了一份方法代碼的目的,但卻不怎么優(yōu)雅。因?yàn)?,定義 SayHello方法時(shí)反映不出其與Person類的關(guān)系。“優(yōu)雅”這個(gè)詞用來(lái)形容代碼,也不知道是誰(shuí)先提出來(lái)的。不過(guò),這個(gè)詞反映了程序員已經(jīng)從追求代碼的正確、高效、可靠和易讀等基礎(chǔ)上,向著追求代碼的美觀感覺(jué)和藝術(shù)境界的層次發(fā)展,程序人生又多了些浪漫色彩。

   顯然,JavaScript早想到了這一問(wèn)題,她的設(shè)計(jì)者們?yōu)榇颂峁┝艘粋€(gè)有趣的prototype概念。

初看原型

    prototype源自法語(yǔ),軟件界的標(biāo)準(zhǔn)翻譯為“原型”,代表事物的初始形態(tài),也含有模型和樣板的意義。JavaScript中的prototype概念恰如其分地反映了這個(gè)詞的內(nèi)含,我們不能將其理解為C++的prototype那種預(yù)先聲明的概念。

    JavaScript的所有function類型的對(duì)象都有一個(gè)prototype屬性。這個(gè)prototype屬性本身又是一個(gè)object類型的對(duì)象,因此我們也可以給這個(gè)prototype對(duì)象添加任意的屬性和方法。既然prototype是對(duì)象的“原型”,那么由該函數(shù)構(gòu)造出來(lái)的對(duì)象應(yīng)該都會(huì)具有這個(gè)“原型”的特性。事實(shí)上,在構(gòu)造函數(shù)的prototype上定義的所有屬性和方法,都是可以通過(guò)其構(gòu)造的對(duì)象直接訪問(wèn)和調(diào)用的。也可以這么說(shuō),prototype提供了一群同類對(duì)象共享屬性和方法的機(jī)制。

    我們先來(lái)看看下面的代碼:

     function  Person(name)
    {
        
this .name  =  name;    // 設(shè)置對(duì)象屬性,每個(gè)對(duì)象各自一份屬性數(shù)據(jù)
    };
    
    Person.prototype.SayHello 
=   function ()   // 給Person函數(shù)的prototype添加SayHello方法。
    {
        alert(
" Hello, I'm  "   +   this .name);
    }

    
var  BillGates  =   new  Person( " Bill Gates " );    // 創(chuàng)建BillGates對(duì)象
     var  SteveJobs  =   new  Person( " Steve Jobs " );    // 創(chuàng)建SteveJobs對(duì)象

    BillGates.SayHello();   
// 通過(guò)BillGates對(duì)象直接調(diào)用到SayHello方法
    SteveJobs.SayHello();    // 通過(guò)SteveJobs對(duì)象直接調(diào)用到SayHello方法

    alert(BillGates.SayHello 
==  SteveJobs.SayHello);  // 因?yàn)閮蓚€(gè)對(duì)象是共享prototype的SayHello,所以顯示:true


    程序運(yùn)行的結(jié)果表明,構(gòu)造函數(shù)的prototype上定義的方法確實(shí)可以通過(guò)對(duì)象直接調(diào)用到,而且代碼是共享的。顯然,把方法設(shè)置到prototype的寫(xiě)法顯得優(yōu)雅多了,盡管調(diào)用形式?jīng)]有變,但邏輯上卻體現(xiàn)了方法與類的關(guān)系,相對(duì)前面的寫(xiě)法,更容易理解和組織代碼。

    那么,對(duì)于多層次類型的構(gòu)造函數(shù)情況又如何呢?

    我們?cè)賮?lái)看下面的代碼:

 1       function  Person(name)    // 基類構(gòu)造函數(shù)
 2      {
 3           this .name  =  name;
 4      };
 5      
 6      Person.prototype.SayHello  =   function ()   // 給基類構(gòu)造函數(shù)的prototype添加方法
 7      {
 8          alert( " Hello, I'm  "   +   this .name);
 9      };
10      
11       function  Employee(name, salary)  // 子類構(gòu)造函數(shù)
12      {
13          Person.call( this , name);     // 調(diào)用基類構(gòu)造函數(shù)
14           this .salary  =  salary;
15      };
16      
17      Employee.prototype  =   new  Person();   // 建一個(gè)基類的對(duì)象作為子類原型的原型,這里很有意思
18      
19      Employee.prototype.ShowMeTheMoney  =   function ()   // 給子類添構(gòu)造函數(shù)的prototype添加方法
20      {
21          alert( this .name  +   "  $ "   +   this .salary);
22      };
23  
24       var  BillGates  =   new  Person( " Bill Gates " );    // 創(chuàng)建基類Person的BillGates對(duì)象
25       var  SteveJobs  =   new  Employee( " Steve Jobs " 1234 );    // 創(chuàng)建子類Employee的SteveJobs對(duì)象
26  
27      BillGates.SayHello();        // 通過(guò)對(duì)象直接調(diào)用到prototype的方法
28      SteveJobs.SayHello();        // 通過(guò)子類對(duì)象直接調(diào)用基類prototype的方法,關(guān)注!
29      SteveJobs.ShowMeTheMoney();  // 通過(guò)子類對(duì)象直接調(diào)用子類prototype的方法
30  
31      alert(BillGates.SayHello  ==  SteveJobs.SayHello);  // 顯示:true,表明prototype的方法是共享的


    這段代碼的第17行,構(gòu)造了一個(gè)基類的對(duì)象,并將其設(shè)為子類構(gòu)造函數(shù)的prototype,這是很有意思的。這樣做的目的就是為了第28行,通過(guò)子類對(duì)象也可以直接調(diào)用基類prototype的方法。為什么可以這樣呢?

    原來(lái),在JavaScript中,prototype不但能讓對(duì)象共享自己財(cái)富,而且prototype還有尋根問(wèn)祖的天性,從而使得先輩們的遺產(chǎn)可以代代相傳。當(dāng)從一個(gè)對(duì)象那里讀取屬性或調(diào)用方法時(shí),如果該對(duì)象自身不存在這樣的屬性或方法,就會(huì)去自己關(guān)聯(lián)的prototype對(duì)象那里尋找;如果 prototype沒(méi)有,又會(huì)去prototype自己關(guān)聯(lián)的前輩prototype那里尋找,直到找到或追溯過(guò)程結(jié)束為止。

    在JavaScript內(nèi)部,對(duì)象的屬性和方法追溯機(jī)制是通過(guò)所謂的prototype鏈來(lái)實(shí)現(xiàn)的。當(dāng)用new操作符構(gòu)造對(duì)象時(shí),也會(huì)同時(shí)將構(gòu)造函數(shù)的 prototype對(duì)象指派給新創(chuàng)建的對(duì)象,成為該對(duì)象內(nèi)置的原型對(duì)象。對(duì)象內(nèi)置的原型對(duì)象應(yīng)該是對(duì)外不可見(jiàn)的,盡管有些瀏覽器(如Firefox)可以讓我們?cè)L問(wèn)這個(gè)內(nèi)置原型對(duì)象,但并不建議這樣做。內(nèi)置的原型對(duì)象本身也是對(duì)象,也有自己關(guān)聯(lián)的原型對(duì)象,這樣就形成了所謂的原型鏈。

    在原型鏈的最末端,就是Object構(gòu)造函數(shù)prototype屬性指向的那一個(gè)原型對(duì)象。這個(gè)原型對(duì)象是所有對(duì)象的最老祖先,這個(gè)老祖宗實(shí)現(xiàn)了諸如 toString等所有對(duì)象天生就該具有的方法。其他內(nèi)置構(gòu)造函數(shù),如Function, Boolean, String, Date和RegExp等的prototype都是從這個(gè)老祖宗傳承下來(lái)的,但他們各自又定義了自身的屬性和方法,從而他們的子孫就表現(xiàn)出各自宗族的那些特征。

    這不就是“繼承”嗎?是的,這就是“繼承”,是JavaScript特有的“原型繼承”。

    “原型繼承”是慈祥而又嚴(yán)厲的。原形對(duì)象將自己的屬性和方法無(wú)私地貢獻(xiàn)給孩子們使用,也并不強(qiáng)迫孩子們必須遵從,允許一些頑皮孩子按自己的興趣和愛(ài)好獨(dú)立行事。從這點(diǎn)上看,原型對(duì)象是一位慈祥的母親。然而,任何一個(gè)孩子雖然可以我行我素,但卻不能動(dòng)原型對(duì)象既有的財(cái)產(chǎn),因?yàn)槟强赡軙?huì)影響到其他孩子的利益。從這一點(diǎn)上看,原型對(duì)象又象一位嚴(yán)厲的父親。我們來(lái)看看下面的代碼就可以理解這個(gè)意思了:

     function  Person(name)
    {
        
this .name  =  name;
    };
    
    Person.prototype.company 
=   " Microsoft " // 原型的屬性
    
    Person.prototype.SayHello 
=   function ()   // 原型的方法
    {
        alert(
" Hello, I'm  "   +   this .name  +   "  of  "   +   this .company);
    };
    
    
var  BillGates  =   new  Person( " Bill Gates " );
    BillGates.SayHello();   
// 由于繼承了原型的東西,規(guī)規(guī)矩矩輸出:Hello, I'm Bill Gates
    
    
var  SteveJobs  =   new  Person( " Steve Jobs " );
    SteveJobs.company 
=   " Apple " ;     // 設(shè)置自己的company屬性,掩蓋了原型的company屬性
    SteveJobs.SayHello  =   function ()  // 實(shí)現(xiàn)了自己的SayHello方法,掩蓋了原型的SayHello方法
    {
        alert(
" Hi,  "   +   this .name  +   "  like  "   +   this .company  +   " , ha ha ha  " );
    };

    SteveJobs.SayHello();   
// 都是自己覆蓋的屬性和方法,輸出:Hi, Steve Jobs like Apple, ha ha ha 
    
    BillGates.SayHello();   
// SteveJobs的覆蓋沒(méi)有影響原型對(duì)象,BillGates還是按老樣子輸出


    對(duì)象可以掩蓋原型對(duì)象的那些屬性和方法,一個(gè)構(gòu)造函數(shù)原型對(duì)象也可以掩蓋上層構(gòu)造函數(shù)原型對(duì)象既有的屬性和方法。這種掩蓋其實(shí)只是在對(duì)象自己身上創(chuàng)建了新的屬性和方法,只不過(guò)這些屬性和方法與原型對(duì)象的那些同名而已。JavaScript就是用這簡(jiǎn)單的掩蓋機(jī)制實(shí)現(xiàn)了對(duì)象的“多態(tài)”性,與靜態(tài)對(duì)象語(yǔ)言的虛函數(shù)和重載(override)概念不謀而合。

    然而,比靜態(tài)對(duì)象語(yǔ)言更神奇的是,我們可以隨時(shí)給原型對(duì)象動(dòng)態(tài)添加新的屬性和方法,從而動(dòng)態(tài)地?cái)U(kuò)展基類的功能特性。這在靜態(tài)對(duì)象語(yǔ)言中是很難想象的。我們來(lái)看下面的代碼:

     function  Person(name)
    {
        
this .name  =  name;
    };
    
    Person.prototype.SayHello 
=   function ()   // 建立對(duì)象前定義的方法
    {
        alert(
" Hello, I'm  "   +   this .name);
    };
    
    
var  BillGates  =   new  Person( " Bill Gates " );    // 建立對(duì)象
    
    BillGates.SayHello();
    
    Person.prototype.Retire 
=   function ()     // 建立對(duì)象后再動(dòng)態(tài)擴(kuò)展原型的方法
    {
        alert(
" Poor  "   +   this .name  +   " , bye bye! " );
    };
    
    BillGates.Retire(); 
// 動(dòng)態(tài)擴(kuò)展的方法即可被先前建立的對(duì)象立即調(diào)用


    阿彌佗佛,原型繼承竟然可以玩出有這樣的法術(shù)!

 

 

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

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多