新聞中心
Javascript本身是從Perl語言的語法演變而來的,本質(zhì)上是腳本語言,隨著版本的更新逐漸加入的對面向?qū)ο蟮哪M。我一直很難理解Javascript語言的繼承機制。

目前累計服務(wù)客戶成百上千家,積累了豐富的產(chǎn)品開發(fā)及服務(wù)經(jīng)驗。以網(wǎng)站設(shè)計水平和技術(shù)實力,樹立企業(yè)形象,為客戶提供成都網(wǎng)站建設(shè)、成都做網(wǎng)站、網(wǎng)站策劃、網(wǎng)頁設(shè)計、網(wǎng)絡(luò)營銷、VI設(shè)計、網(wǎng)站改版、漏洞修補等服務(wù)。成都創(chuàng)新互聯(lián)始終以務(wù)實、誠信為根本,不斷創(chuàng)新和提高建站品質(zhì),通過對領(lǐng)先技術(shù)的掌握、對創(chuàng)意設(shè)計的研究、對客戶形象的視覺傳遞、對應(yīng)用系統(tǒng)的結(jié)合,為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進步。
它沒有"子類"和"父類"的概念,也沒有"類"(class)和"實例"(instance)的區(qū)分,全靠一種很奇特的"原型鏈"(prototype chain)模式,來實現(xiàn)繼承。
我花了很多時間,學(xué)習這個部分,還做了很多筆記。但是都屬于強行記憶,無法從根本上理解。
直到昨天,我讀到法國程序員Vjeux的解釋,才恍然大悟,完全明白了Javascript為什么這樣設(shè)計。
下面,我嘗試用自己的語言,來解釋它的設(shè)計思想。徹底說明白prototype對象到底是怎么回事。其實根本就沒那么復(fù)雜,真相非常簡單。
一、從古代說起
要理解Javascript的設(shè)計思想,必須從它的誕生說起。
1994年,網(wǎng)景公司(Netscape)發(fā)布了Navigator瀏覽器0.9版。這是歷史上***個比較成熟的網(wǎng)絡(luò)瀏覽器,轟動一時。但是,這個版本的瀏覽器只能用來瀏覽,不具備與訪問者互動的能力。
比如,如果網(wǎng)頁上有一欄"用戶名"要求填寫,瀏覽器就無法判斷訪問者是否真的填寫了,只有讓服務(wù)器端判斷。如果沒有填寫,服務(wù)器端就返回錯誤,要求用戶重新填寫,這太浪費時間和服務(wù)器資源了。
因此,網(wǎng)景公司急需一種網(wǎng)頁腳本語言,使得瀏覽器可以與網(wǎng)頁互動。工程師Brendan Eich負責開發(fā)這種新語言。他覺得,沒必要設(shè)計得很復(fù)雜,這種語言只要能夠完成一些簡單操作就夠了,比如判斷用戶有沒有填寫表單。
1994年正是面向?qū)ο缶幊蹋╫bject-oriented programming)最興盛的時期,C++是當時***的語言,而Java語言的1.0版即將于第二年推出,Sun公司正在大肆造勢。
Brendan Eich無疑受到了影響,Javascript里面所有的數(shù)據(jù)類型都是對象(object),這一點與Java非常相似。但是,他隨即就遇到了一個難題,到底要不要設(shè)計"繼承"機制呢?
二、Brendan Eich的選擇
如果真的是一種簡易的腳本語言,其實不需要有"繼承"機制。但是,Javascript里面都是對象,必須有一種機制,將所有對象聯(lián)系起來。所以,Brendan Eich***還是設(shè)計了"繼承"。
但是,他不打算引入"類"(class)的概念,因為一旦有了"類",Javascript就是一種完整的面向?qū)ο缶幊陶Z言了,這好像有點太正式了,而且增加了初學(xué)者的入門難度。
他考慮到,C++和Java語言都使用new命令,生成實例。
C++的寫法是:
- ClassName *object = new ClassName(param);
Java的寫法是:
- Foo foo = new Foo();
因此,他就把new命令引入了Javascript,用來從原型對象生成一個實例對象。但是,Javascript沒有"類",怎么來表示原型對象呢?
這時,他想到C++和Java使用new命令時,都會調(diào)用"類"的構(gòu)造函數(shù)(constructor)。他就做了一個簡化的設(shè)計,在Javascript語言中,new命令后面跟的不是類,而是構(gòu)造函數(shù)。
舉例來說,現(xiàn)在有一個叫做DOG的構(gòu)造函數(shù),表示狗對象的原型。
- function DOG(name)
- {
- this.name = name;
- }
對這個構(gòu)造函數(shù)使用new,就會生成一個狗對象的實例。
- var dogA = new DOG('大毛');
- alert(dogA.name); // 大毛
注意構(gòu)造函數(shù)中的this關(guān)鍵字,它就代表了新創(chuàng)建的實例對象。
三、new運算符的缺點
用構(gòu)造函數(shù)生成實例對象,有一個缺點,那就是無法共享屬性和方法。
比如,在DOG對象的構(gòu)造函數(shù)中,設(shè)置一個實例對象的共有屬性species。
- function DOG(name)
- {
- this.name = name;
- this.species = '犬科';
- }
然后,生成兩個實例對象:
- var dogA = new DOG('大毛');
- var dogB = new DOG('二毛');
這兩個對象的species屬性是獨立的,修改其中一個,不會影響到另一個。
- dogA.species = '貓科';
- alert(dogB.species); // 顯示"犬科",不受dogA的影響
每一個實例對象,都有自己的屬性和方法的副本。這不僅無法做到數(shù)據(jù)共享,也是極大的資源浪費。
四、prototype屬性的引入
考慮到這一點,Brendan Eich決定為構(gòu)造函數(shù)設(shè)置一個prototype屬性。
這個屬性包含一個對象(以下簡稱"prototype對象"),所有實例對象需要共享的屬性和方法,都放在這個對象里面;那些不需要共享的屬性和方法,就放在構(gòu)造函數(shù)里面。
實例對象一旦創(chuàng)建,將自動引用prototype對象的屬性和方法。也就是說,實例對象的屬性和方法,分成兩種,一種是本地的,另一種是引用的。
還是以DOG構(gòu)造函數(shù)為例,現(xiàn)在用prototype屬性進行改寫:
- function DOG(name){
- this.name = name;
- }
- DOG.prototype = { species : '犬科' };
- var dogA = new DOG('大毛');
- var dogB = new DOG('二毛');
- alert(dogA.species); // 犬科
- alert(dogB.species); // 犬科
現(xiàn)在,species屬性放在prototype對象里,是兩個實例對象共享的。只要修改了prototype對象,就會同時影響到兩個實例對象。
- DOG.prototype.species = '貓科';
- alert(dogA.species); // 貓科
- alert(dogB.species); // 貓科
五、總結(jié)
由于所有的實例對象共享同一個prototype對象,那么從外界看起來,prototype對象就好像是實例對象的原型,而實例對象則好像"繼承"了prototype對象一樣。
這就是Javascript繼承機制的設(shè)計思想。不知道說清楚了沒有,繼承機制的具體應(yīng)用方法,可以參考有關(guān)的系列文章:
- Javascript面向?qū)ο缶幊蹋ㄒ唬?封裝
- Javascript面向?qū)ο缶幊蹋ǘ├^承
- Javascript面向?qū)ο缶幊蹋ㄈ┓呛瘮?shù)對象的繼承
原文地址:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_
inheritance_mechanism_in_javascript.html
【編輯推薦】
- Javascript中閉包的作用域鏈
- Javascript的興起是否意味著LAMP的終結(jié)?
- Web移動應(yīng)用 HTML5 CSS和JavaScript
- Javascript閉包(closure) 深入淺出
- 如何判斷Javascript對象是否存在
分享名稱:Javascript繼承機制的設(shè)計思想
標題URL:http://fisionsoft.com.cn/article/dpsjcsh.html


咨詢
建站咨詢
