再談javascript面向?qū)ο缶幊?/h1>
更新時(shí)間:2012年03月18日 14:46:16 作者:
雖有陳皓《Javascript 面向?qū)ο缶幊獭分橛裨谇?,但是我還是忍不住再畫蛇添足的補(bǔ)上一篇文章,主要是因?yàn)閖avascript這門語言魅力
另外這篇文章是一篇入門文章,我也是才開始學(xué)習(xí)Javascript,有一點(diǎn)心得,才想寫一篇這樣文章,文章中難免有錯(cuò)誤的地方,還請(qǐng)各位不吝吐槽指正
吐槽Javascript
初次接觸Javascript,這門語言的確會(huì)讓很多正規(guī)軍感到諸多的不適,這種不適來自于Javascript的語法的簡(jiǎn)練和不嚴(yán)謹(jǐn),這種不適也來自Javascript這個(gè)悲催的名稱,我在想網(wǎng)景公司的Javascript設(shè)計(jì)者在給他起名稱那天一定是腦殼進(jìn)水了,讓Javascript這么多年來受了這么多不白之冤,人們都認(rèn)為他是Java的附屬物,一個(gè)WEB玩具語言。因此才會(huì)有些人會(huì)對(duì)Javascript不屑,認(rèn)為Javascript不是一門真正的語言,但是這此他們真的錯(cuò)了。Javascript不僅是一門語言,是一門真真正正的語言,而且他還是一門里程碑式的語言,他獨(dú)創(chuàng)多種新的編程模式原型繼承,閉包(作者注:閉包不是JS首創(chuàng),應(yīng)該Scheme首創(chuàng),prototypal inheritance 和 dynamic objects 是self語言首創(chuàng),Javascript的首創(chuàng)并不精彩,謝謝網(wǎng)友的指正。),對(duì)后來的動(dòng)態(tài)語言產(chǎn)生了巨大的影響。做為當(dāng)今最流行的語言(沒有之一),看看git上提交的最多的語言類型就能明白。隨著HTML5的登場(chǎng),瀏覽器將在個(gè)人電腦上將大顯身手,完全有替換OS的趨勢(shì)的時(shí)候,Javascript做為瀏覽器上的一門唯一真真的語言,如同C之于 unix/linux,java之于JVM,Cobol之于MainFrame,我們也需要來重新的認(rèn)真地認(rèn)識(shí)和審視這門語言。另外Javascript的正式名稱是:ECMAScript,這個(gè)名字明顯比Javascript帥太多了!
言歸正傳,我們切入主題——Javascript的面向?qū)ο缶幊獭R凧avascript的面向?qū)ο缶幊蹋覀兊谝徊揭龅氖虑榫褪峭浳覀兯鶎W(xué)的面向?qū)ο缶幊獭鹘y(tǒng)C++或Java的面向?qū)ο笏季S來學(xué)習(xí)Javascript的面向?qū)ο髸?huì)給你帶來不少困惑,讓我們先忘記我們所學(xué)的,從新開始學(xué)習(xí)這門特殊的面向?qū)ο缶幊?。既然是OO編程,要如何來理解OO編程呢,記得以前學(xué)C++,學(xué)了很久都不入門,后來有幸讀了《Inside The C++ Object Model》這本大作,頓時(shí)豁然開朗,因此本文也將以對(duì)象模型的方式來探討的Javascript的OO編程。因?yàn)镴avascript 對(duì)象模型的特殊性,所以使得Javascript的繼承和傳統(tǒng)的繼承非常不一樣,同時(shí)也因?yàn)镴avascript里面沒有類,這意味著Javascript里面沒有extends,implements。那么Javascript到底是如何來實(shí)現(xiàn)OO編程的呢?好吧,讓我們開始吧,一起在Javascript的OO世界里來一次漫游
首先,我們需要先看看Javascript如何定義一個(gè)對(duì)象。下面是我們的一個(gè)對(duì)象定義:
復(fù)制代碼 代碼如下:var o = {};
還可以這樣定義一個(gè)對(duì)象
復(fù)制代碼 代碼如下: function f() { }
對(duì),你們沒有看錯(cuò),在Javascript里面,函數(shù)也是對(duì)象。
當(dāng)然還可以
復(fù)制代碼 代碼如下:var array1= [ 1,2,3];
數(shù)組也是一個(gè)對(duì)象。
其他關(guān)于對(duì)象的基本的概念的描述,還是請(qǐng)各位親們參見陳皓《Javascript 面向?qū)ο缶幊獭肺恼隆?
對(duì)象都有了,唯一沒有的就是class,因?yàn)樵贘avascript里面是沒有class關(guān)鍵字的,算好還有function,function的存在讓我們可以變通的定義類,在擴(kuò)展這個(gè)主題前,我們還需要了解一個(gè)Javascript對(duì)象最重要的屬性,__proto__成員。
__proto__成員
嚴(yán)格的說這個(gè)成員不應(yīng)該叫這個(gè)名字,__proto__是Firefox中的稱呼,__proto__只有在Firefox瀏覽器中才能被訪問到。做為一個(gè)對(duì)象,當(dāng)你訪問其中的一個(gè)成員或方法的時(shí)候,如果這個(gè)對(duì)象中沒有這個(gè)方法或成員,那么Javascript引擎將會(huì)訪問這個(gè)對(duì)象的__proto__成員所指向的另外的一個(gè)對(duì)象,并在那個(gè)對(duì)象中查找指定的方法或成員,如果不能找到,那就會(huì)繼續(xù)通過那個(gè)對(duì)象的__proto__成員指向的對(duì)象進(jìn)行遞歸查找,直到這個(gè)鏈表結(jié)束。
好了,讓我們舉一個(gè)例子。
比如上上面定義的數(shù)組對(duì)象array1。當(dāng)我們創(chuàng)建出array1這個(gè)對(duì)象的時(shí)候,array1實(shí)際在Javascript引擎中的對(duì)象模型如下:

array1對(duì)象具有一個(gè)length屬性值為3,但是我們可以通過如下的方法來為array1增加元素:
復(fù)制代碼 代碼如下:array1.push(4);
push這個(gè)方法來自于array1的__proto__成員指向?qū)ο蟮囊粋€(gè)方法(Array.prototye.push())。正是因?yàn)樗械臄?shù)組對(duì)象(通過[]來創(chuàng)建的)都包含有一個(gè)指向同一個(gè)具有push,reverse等方法對(duì)象(Array.prototype)的__proto__成員,才使得這些數(shù)組對(duì)象可以使用push,reverse等方法。
那么這個(gè)__proto__這個(gè)屬性就相當(dāng)于面向?qū)ο笾械摹県as a”關(guān)系,這樣的的話,只要我們有一個(gè)模板對(duì)象比如Array.prototype這個(gè)對(duì)象,然后把其他的對(duì)象__proto__屬性指向這個(gè)對(duì)象的話就完成了一種繼承的模式。不錯(cuò)!我們完全可以這么干。但是別高興的太早,這個(gè)屬性只在FireFox中有效,其他的瀏覽器雖然也有屬性,但是不能通過__proto__來訪問,只能通過getPrototypeOf方法進(jìn)行訪問,而且這個(gè)屬性是只讀的??磥砦覀円贘avascript實(shí)現(xiàn)繼承并不是很容易的事情啊。
函數(shù)對(duì)象prototype成員
首先我們先來看一段函數(shù)prototype成員的定義,
When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object
當(dāng)一個(gè)函數(shù)對(duì)象被創(chuàng)建時(shí),這個(gè)函數(shù)對(duì)象就具有一個(gè)prototype成員,這個(gè)成員是一個(gè)對(duì)象,這個(gè)對(duì)象包含了一個(gè)構(gòu)造子成員,這個(gè)構(gòu)造子成員會(huì)指向這個(gè)函數(shù)對(duì)象。
例如:
復(fù)制代碼 代碼如下: function Base() {
this.id = "base"
}
Base這個(gè)函數(shù)對(duì)象就具有一個(gè)prototype成員,關(guān)于構(gòu)造子其實(shí)Base函數(shù)對(duì)象自身,為什么我們將這類函數(shù)稱為構(gòu)造子呢?原因是因?yàn)檫@類函數(shù)設(shè)計(jì)來和new 操作符一起使用的。為了和一般的函數(shù)對(duì)象有所區(qū)別,這類函數(shù)的首字母一般都大寫。構(gòu)造子的主要作用就是來創(chuàng)建一類相似的對(duì)象。
上面這段代碼在Javascript引擎的對(duì)象模型是這樣的

new 操作符
在有上面的基礎(chǔ)概念的介紹之后,在加上new操作符,我們就能完成傳統(tǒng)面向?qū)ο蟮腸lass + new的方式創(chuàng)建對(duì)象,在Javascript中,我們將這類方式成為Pseudoclassical。
基于上面的例子,我們執(zhí)行如下代碼
復(fù)制代碼 代碼如下:var obj = new Base();
這樣代碼的結(jié)果是什么,我們?cè)贘avascript引擎中看到的對(duì)象模型是:

new操作符具體干了什么呢?其實(shí)很簡(jiǎn)單,就干了三件事情。
復(fù)制代碼 代碼如下: var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
第一行,我們創(chuàng)建了一個(gè)空對(duì)象obj
第二行,我們將這個(gè)空對(duì)象的__proto__成員指向了Base函數(shù)對(duì)象prototype成員對(duì)象
第三行,我們將Base函數(shù)對(duì)象的this指針替換成obj,然后再調(diào)用Base函數(shù),于是我們就給obj對(duì)象賦值了一個(gè)id成員變量,這個(gè)成員變量的值是”base”,關(guān)于call函數(shù)的用法,請(qǐng)參看陳皓《Javascript 面向?qū)ο缶幊獭肺恼?
如果我們給Base.prototype的對(duì)象添加一些函數(shù)會(huì)有什么效果呢?
例如代碼如下:
復(fù)制代碼 代碼如下:Base.prototype.toString = function() {
return this.id;
}
那么當(dāng)我們使用new創(chuàng)建一個(gè)新對(duì)象的時(shí)候,根據(jù)__proto__的特性,toString這個(gè)方法也可以做新對(duì)象的方法被訪問到。于是我們看到了:
構(gòu)造子中,我們來設(shè)置‘類'的成員變量(例如:例子中的id),構(gòu)造子對(duì)象prototype中我們來設(shè)置‘類'的公共方法。于是通過函數(shù)對(duì)象和Javascript特有的__proto__與prototype成員及new操作符,模擬出類和類實(shí)例化的效果。
Pseudoclassical 繼承
我們模擬類,那么繼承又該怎么做呢?其實(shí)很簡(jiǎn)單,我們只要將構(gòu)造子的prototype指向父類即可。例如我們?cè)O(shè)計(jì)一個(gè)Derive 類。如下
復(fù)制代碼 代碼如下: function Derive(id) {
this.id = id;
}
Derive.prototype = new Base();
Derive.prototype.test = function(id){
return this.id === id;
}
var newObj = new Derive("derive");
這段代碼執(zhí)行后的對(duì)象模型又是怎么樣的呢?根據(jù)之前的推導(dǎo),應(yīng)該是如下的對(duì)象模型

這樣我們的newObj也繼承了基類Base的toString方法,并且具有自身的成員id。關(guān)于這個(gè)對(duì)象模型是如何被推導(dǎo)出來的就留給各位同學(xué)了,參照前面的描述,推導(dǎo)這個(gè)對(duì)象模型應(yīng)該不難。
Pseudoclassical繼承會(huì)讓學(xué)過C++/Java的同學(xué)略微的感受到一點(diǎn)舒服,特別是new關(guān)鍵字,看到都特親切,不過兩者雖然相似,但是機(jī)理完全不同。當(dāng)然不關(guān)什么樣繼承都是不能離不開__proto__成員的。
Prototypal繼承
這是Javascript的另外一種繼承方式,這個(gè)繼承也就是之前陳皓文章《Javascript 面向?qū)ο缶幊獭分衏reate函數(shù),非??上У氖沁@個(gè)是ECMAScript V5的標(biāo)準(zhǔn),支持V5的瀏覽器目前看來也就是IE9,Chrome最新版本和Firefox。雖然看著多,但是做為IE6的重災(zāi)區(qū)的中國,我建議各位還是避免使用create函數(shù)。好在沒有create函數(shù)之前,Javascript的使用者已經(jīng)設(shè)計(jì)出了等同于這個(gè)函數(shù)的。例如:我們看看Douglas Crockford的object函數(shù)。
復(fù)制代碼 代碼如下:function object(old) {
function F() {};
F.prototype = old;
return new F();
}
var newObj = object(oldObject);
例如如下代碼段
復(fù)制代碼 代碼如下:var base ={
id:"base",
toString:function(){
return this.id;
}
};
var derive = object(base);
上面函數(shù)的執(zhí)行后的對(duì)象模型是:

如何形成這樣的對(duì)象模型,原理也很簡(jiǎn)單,只要把object這個(gè)函數(shù)擴(kuò)展一下,就能畫出這個(gè)模型,怎么畫留給讀者自己去畫吧。
這樣的繼承方式被稱為原型繼承。相對(duì)來說要比Pseudoclassical繼承來的簡(jiǎn)單方便。ECMAScript V5正是因?yàn)檫@原因也才增加create函數(shù),讓開發(fā)者可以快速的實(shí)現(xiàn)原型繼承。
上述兩種繼承方式是Javascript中最常用的繼承方式。通過本文的講解,你應(yīng)該對(duì)Javascript的OO編程有了一些‘原理'級(jí)的了解了吧
參考:
《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》
Advance Javascript (Douglas Crockford 大神的視頻,一定要看?。╊}外話:
web2.0后,web應(yīng)用可謂飛速發(fā)展,如今在HTML5發(fā)布之際,瀏覽器的功能被大大強(qiáng)化,我感覺Browser遠(yuǎn)遠(yuǎn)在不是一個(gè)Browser那么簡(jiǎn)單了。記得C++之父曾經(jīng)這樣說過JAVA,JAVA不是跨平臺(tái),JAVA本身就是一個(gè)平臺(tái)。如今的Browser也本身就是一個(gè)平臺(tái)了,好在這個(gè)平臺(tái)是基于標(biāo)準(zhǔn)的。如果Browser是平臺(tái),由于Browser安全沙箱的限制,個(gè)人電腦的資源被使用的很少,感覺Browser就是一個(gè)NC(Network Computer)?我們居然又回到了Sun最初提出的構(gòu)想,Sun是不是太強(qiáng)大了些?
相關(guān)文章
-
JavaScript 原型繼承,學(xué)習(xí)js面向?qū)ο蟮呐笥芽梢钥纯础?/div> 2011-12-12
-
JavaScript 基于原型的對(duì)象(創(chuàng)建、調(diào)用)
在我們寫js代碼的時(shí)候,內(nèi)部對(duì)象是不可避免的要引用,但是光靠這些對(duì)象是不夠的,所以需要我們自己定義對(duì)象,這個(gè)時(shí)候通常用到的對(duì)象是第三種,即基于原型的對(duì)象,下面就如何創(chuàng)建自己的對(duì)象,定義對(duì)象的方法、屬性,調(diào)用對(duì)象給出詳細(xì)的說明。 2009-10-10
-
javascript 設(shè)計(jì)模式之單體模式 面向?qū)ο髮W(xué)習(xí)基礎(chǔ)
單體是在腳本加載時(shí)創(chuàng)建的,能將一系列有關(guān)聯(lián)的變量和方法組織為一個(gè)邏輯單元,邏輯單元里面的內(nèi)容通過單一的變量進(jìn)行訪問,也是筆記基礎(chǔ)與常用的面向?qū)ο蟮亩x方法。 2010-04-04
-
一實(shí)用的實(shí)現(xiàn)table排序的Javascript類庫
一實(shí)用的實(shí)現(xiàn)table排序的Javascript類庫... 2007-09-09
-
JS類定義原型方法的兩種實(shí)現(xiàn)的區(qū)別評(píng)論很多
JS類定義原型方法的兩種實(shí)現(xiàn)的區(qū)別評(píng)論很多... 2007-09-09
-
javascript面向?qū)ο缶幊?一) 實(shí)例代碼
javascript面向?qū)ο缶幊虒?shí)例代碼,代碼也算比較基礎(chǔ)了,不懂得朋友可以參考腳本之家之前發(fā)布的文章。 2010-06-06
-
[推薦]javascript 面向?qū)ο蠹夹g(shù)基礎(chǔ)教程
看了很多介紹javascript面向?qū)ο蠹夹g(shù)的文章,很暈.為什么?不是因?yàn)閷懙貌缓?而是因?yàn)樘願(yuàn)W.
javascript中的對(duì)象還沒解釋清楚怎么回事,一上來就直奔主題,類/繼承/原型/私有變量....
2009-03-03
最新評(píng)論
吐槽Javascript
初次接觸Javascript,這門語言的確會(huì)讓很多正規(guī)軍感到諸多的不適,這種不適來自于Javascript的語法的簡(jiǎn)練和不嚴(yán)謹(jǐn),這種不適也來自Javascript這個(gè)悲催的名稱,我在想網(wǎng)景公司的Javascript設(shè)計(jì)者在給他起名稱那天一定是腦殼進(jìn)水了,讓Javascript這么多年來受了這么多不白之冤,人們都認(rèn)為他是Java的附屬物,一個(gè)WEB玩具語言。因此才會(huì)有些人會(huì)對(duì)Javascript不屑,認(rèn)為Javascript不是一門真正的語言,但是這此他們真的錯(cuò)了。Javascript不僅是一門語言,是一門真真正正的語言,而且他還是一門里程碑式的語言,他獨(dú)創(chuàng)多種新的編程模式原型繼承,閉包(作者注:閉包不是JS首創(chuàng),應(yīng)該Scheme首創(chuàng),prototypal inheritance 和 dynamic objects 是self語言首創(chuàng),Javascript的首創(chuàng)并不精彩,謝謝網(wǎng)友的指正。),對(duì)后來的動(dòng)態(tài)語言產(chǎn)生了巨大的影響。做為當(dāng)今最流行的語言(沒有之一),看看git上提交的最多的語言類型就能明白。隨著HTML5的登場(chǎng),瀏覽器將在個(gè)人電腦上將大顯身手,完全有替換OS的趨勢(shì)的時(shí)候,Javascript做為瀏覽器上的一門唯一真真的語言,如同C之于 unix/linux,java之于JVM,Cobol之于MainFrame,我們也需要來重新的認(rèn)真地認(rèn)識(shí)和審視這門語言。另外Javascript的正式名稱是:ECMAScript,這個(gè)名字明顯比Javascript帥太多了!
言歸正傳,我們切入主題——Javascript的面向?qū)ο缶幊獭R凧avascript的面向?qū)ο缶幊蹋覀兊谝徊揭龅氖虑榫褪峭浳覀兯鶎W(xué)的面向?qū)ο缶幊獭鹘y(tǒng)C++或Java的面向?qū)ο笏季S來學(xué)習(xí)Javascript的面向?qū)ο髸?huì)給你帶來不少困惑,讓我們先忘記我們所學(xué)的,從新開始學(xué)習(xí)這門特殊的面向?qū)ο缶幊?。既然是OO編程,要如何來理解OO編程呢,記得以前學(xué)C++,學(xué)了很久都不入門,后來有幸讀了《Inside The C++ Object Model》這本大作,頓時(shí)豁然開朗,因此本文也將以對(duì)象模型的方式來探討的Javascript的OO編程。因?yàn)镴avascript 對(duì)象模型的特殊性,所以使得Javascript的繼承和傳統(tǒng)的繼承非常不一樣,同時(shí)也因?yàn)镴avascript里面沒有類,這意味著Javascript里面沒有extends,implements。那么Javascript到底是如何來實(shí)現(xiàn)OO編程的呢?好吧,讓我們開始吧,一起在Javascript的OO世界里來一次漫游
首先,我們需要先看看Javascript如何定義一個(gè)對(duì)象。下面是我們的一個(gè)對(duì)象定義:
復(fù)制代碼 代碼如下:
var o = {};
還可以這樣定義一個(gè)對(duì)象
復(fù)制代碼 代碼如下:
function f() { }
對(duì),你們沒有看錯(cuò),在Javascript里面,函數(shù)也是對(duì)象。
當(dāng)然還可以
復(fù)制代碼 代碼如下:
var array1= [ 1,2,3];
數(shù)組也是一個(gè)對(duì)象。
其他關(guān)于對(duì)象的基本的概念的描述,還是請(qǐng)各位親們參見陳皓《Javascript 面向?qū)ο缶幊獭肺恼隆?
對(duì)象都有了,唯一沒有的就是class,因?yàn)樵贘avascript里面是沒有class關(guān)鍵字的,算好還有function,function的存在讓我們可以變通的定義類,在擴(kuò)展這個(gè)主題前,我們還需要了解一個(gè)Javascript對(duì)象最重要的屬性,__proto__成員。
__proto__成員
嚴(yán)格的說這個(gè)成員不應(yīng)該叫這個(gè)名字,__proto__是Firefox中的稱呼,__proto__只有在Firefox瀏覽器中才能被訪問到。做為一個(gè)對(duì)象,當(dāng)你訪問其中的一個(gè)成員或方法的時(shí)候,如果這個(gè)對(duì)象中沒有這個(gè)方法或成員,那么Javascript引擎將會(huì)訪問這個(gè)對(duì)象的__proto__成員所指向的另外的一個(gè)對(duì)象,并在那個(gè)對(duì)象中查找指定的方法或成員,如果不能找到,那就會(huì)繼續(xù)通過那個(gè)對(duì)象的__proto__成員指向的對(duì)象進(jìn)行遞歸查找,直到這個(gè)鏈表結(jié)束。
好了,讓我們舉一個(gè)例子。
比如上上面定義的數(shù)組對(duì)象array1。當(dāng)我們創(chuàng)建出array1這個(gè)對(duì)象的時(shí)候,array1實(shí)際在Javascript引擎中的對(duì)象模型如下:

array1對(duì)象具有一個(gè)length屬性值為3,但是我們可以通過如下的方法來為array1增加元素:
復(fù)制代碼 代碼如下:
array1.push(4);
push這個(gè)方法來自于array1的__proto__成員指向?qū)ο蟮囊粋€(gè)方法(Array.prototye.push())。正是因?yàn)樗械臄?shù)組對(duì)象(通過[]來創(chuàng)建的)都包含有一個(gè)指向同一個(gè)具有push,reverse等方法對(duì)象(Array.prototype)的__proto__成員,才使得這些數(shù)組對(duì)象可以使用push,reverse等方法。
那么這個(gè)__proto__這個(gè)屬性就相當(dāng)于面向?qū)ο笾械摹県as a”關(guān)系,這樣的的話,只要我們有一個(gè)模板對(duì)象比如Array.prototype這個(gè)對(duì)象,然后把其他的對(duì)象__proto__屬性指向這個(gè)對(duì)象的話就完成了一種繼承的模式。不錯(cuò)!我們完全可以這么干。但是別高興的太早,這個(gè)屬性只在FireFox中有效,其他的瀏覽器雖然也有屬性,但是不能通過__proto__來訪問,只能通過getPrototypeOf方法進(jìn)行訪問,而且這個(gè)屬性是只讀的??磥砦覀円贘avascript實(shí)現(xiàn)繼承并不是很容易的事情啊。
函數(shù)對(duì)象prototype成員
首先我們先來看一段函數(shù)prototype成員的定義,
When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object
當(dāng)一個(gè)函數(shù)對(duì)象被創(chuàng)建時(shí),這個(gè)函數(shù)對(duì)象就具有一個(gè)prototype成員,這個(gè)成員是一個(gè)對(duì)象,這個(gè)對(duì)象包含了一個(gè)構(gòu)造子成員,這個(gè)構(gòu)造子成員會(huì)指向這個(gè)函數(shù)對(duì)象。
例如:
復(fù)制代碼 代碼如下:
function Base() {
this.id = "base"
}
this.id = "base"
}
Base這個(gè)函數(shù)對(duì)象就具有一個(gè)prototype成員,關(guān)于構(gòu)造子其實(shí)Base函數(shù)對(duì)象自身,為什么我們將這類函數(shù)稱為構(gòu)造子呢?原因是因?yàn)檫@類函數(shù)設(shè)計(jì)來和new 操作符一起使用的。為了和一般的函數(shù)對(duì)象有所區(qū)別,這類函數(shù)的首字母一般都大寫。構(gòu)造子的主要作用就是來創(chuàng)建一類相似的對(duì)象。
上面這段代碼在Javascript引擎的對(duì)象模型是這樣的

new 操作符
在有上面的基礎(chǔ)概念的介紹之后,在加上new操作符,我們就能完成傳統(tǒng)面向?qū)ο蟮腸lass + new的方式創(chuàng)建對(duì)象,在Javascript中,我們將這類方式成為Pseudoclassical。
基于上面的例子,我們執(zhí)行如下代碼
復(fù)制代碼 代碼如下:
var obj = new Base();
這樣代碼的結(jié)果是什么,我們?cè)贘avascript引擎中看到的對(duì)象模型是:

new操作符具體干了什么呢?其實(shí)很簡(jiǎn)單,就干了三件事情。
復(fù)制代碼 代碼如下:
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
obj.__proto__ = Base.prototype;
Base.call(obj);
第一行,我們創(chuàng)建了一個(gè)空對(duì)象obj
第二行,我們將這個(gè)空對(duì)象的__proto__成員指向了Base函數(shù)對(duì)象prototype成員對(duì)象
第三行,我們將Base函數(shù)對(duì)象的this指針替換成obj,然后再調(diào)用Base函數(shù),于是我們就給obj對(duì)象賦值了一個(gè)id成員變量,這個(gè)成員變量的值是”base”,關(guān)于call函數(shù)的用法,請(qǐng)參看陳皓《Javascript 面向?qū)ο缶幊獭肺恼?
如果我們給Base.prototype的對(duì)象添加一些函數(shù)會(huì)有什么效果呢?
例如代碼如下:
復(fù)制代碼 代碼如下:
Base.prototype.toString = function() {
return this.id;
}
return this.id;
}
那么當(dāng)我們使用new創(chuàng)建一個(gè)新對(duì)象的時(shí)候,根據(jù)__proto__的特性,toString這個(gè)方法也可以做新對(duì)象的方法被訪問到。于是我們看到了:
構(gòu)造子中,我們來設(shè)置‘類'的成員變量(例如:例子中的id),構(gòu)造子對(duì)象prototype中我們來設(shè)置‘類'的公共方法。于是通過函數(shù)對(duì)象和Javascript特有的__proto__與prototype成員及new操作符,模擬出類和類實(shí)例化的效果。
Pseudoclassical 繼承
我們模擬類,那么繼承又該怎么做呢?其實(shí)很簡(jiǎn)單,我們只要將構(gòu)造子的prototype指向父類即可。例如我們?cè)O(shè)計(jì)一個(gè)Derive 類。如下
復(fù)制代碼 代碼如下:
function Derive(id) {
this.id = id;
}
Derive.prototype = new Base();
Derive.prototype.test = function(id){
return this.id === id;
}
var newObj = new Derive("derive");
this.id = id;
}
Derive.prototype = new Base();
Derive.prototype.test = function(id){
return this.id === id;
}
var newObj = new Derive("derive");
這段代碼執(zhí)行后的對(duì)象模型又是怎么樣的呢?根據(jù)之前的推導(dǎo),應(yīng)該是如下的對(duì)象模型

這樣我們的newObj也繼承了基類Base的toString方法,并且具有自身的成員id。關(guān)于這個(gè)對(duì)象模型是如何被推導(dǎo)出來的就留給各位同學(xué)了,參照前面的描述,推導(dǎo)這個(gè)對(duì)象模型應(yīng)該不難。
Pseudoclassical繼承會(huì)讓學(xué)過C++/Java的同學(xué)略微的感受到一點(diǎn)舒服,特別是new關(guān)鍵字,看到都特親切,不過兩者雖然相似,但是機(jī)理完全不同。當(dāng)然不關(guān)什么樣繼承都是不能離不開__proto__成員的。
Prototypal繼承
這是Javascript的另外一種繼承方式,這個(gè)繼承也就是之前陳皓文章《Javascript 面向?qū)ο缶幊獭分衏reate函數(shù),非??上У氖沁@個(gè)是ECMAScript V5的標(biāo)準(zhǔn),支持V5的瀏覽器目前看來也就是IE9,Chrome最新版本和Firefox。雖然看著多,但是做為IE6的重災(zāi)區(qū)的中國,我建議各位還是避免使用create函數(shù)。好在沒有create函數(shù)之前,Javascript的使用者已經(jīng)設(shè)計(jì)出了等同于這個(gè)函數(shù)的。例如:我們看看Douglas Crockford的object函數(shù)。
復(fù)制代碼 代碼如下:
function object(old) {
function F() {};
F.prototype = old;
return new F();
}
var newObj = object(oldObject);
function F() {};
F.prototype = old;
return new F();
}
var newObj = object(oldObject);
例如如下代碼段
復(fù)制代碼 代碼如下:
var base ={
id:"base",
toString:function(){
return this.id;
}
};
var derive = object(base);
id:"base",
toString:function(){
return this.id;
}
};
var derive = object(base);
上面函數(shù)的執(zhí)行后的對(duì)象模型是:

如何形成這樣的對(duì)象模型,原理也很簡(jiǎn)單,只要把object這個(gè)函數(shù)擴(kuò)展一下,就能畫出這個(gè)模型,怎么畫留給讀者自己去畫吧。
這樣的繼承方式被稱為原型繼承。相對(duì)來說要比Pseudoclassical繼承來的簡(jiǎn)單方便。ECMAScript V5正是因?yàn)檫@原因也才增加create函數(shù),讓開發(fā)者可以快速的實(shí)現(xiàn)原型繼承。
上述兩種繼承方式是Javascript中最常用的繼承方式。通過本文的講解,你應(yīng)該對(duì)Javascript的OO編程有了一些‘原理'級(jí)的了解了吧
參考:
《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》
Advance Javascript (Douglas Crockford 大神的視頻,一定要看?。╊}外話:
web2.0后,web應(yīng)用可謂飛速發(fā)展,如今在HTML5發(fā)布之際,瀏覽器的功能被大大強(qiáng)化,我感覺Browser遠(yuǎn)遠(yuǎn)在不是一個(gè)Browser那么簡(jiǎn)單了。記得C++之父曾經(jīng)這樣說過JAVA,JAVA不是跨平臺(tái),JAVA本身就是一個(gè)平臺(tái)。如今的Browser也本身就是一個(gè)平臺(tái)了,好在這個(gè)平臺(tái)是基于標(biāo)準(zhǔn)的。如果Browser是平臺(tái),由于Browser安全沙箱的限制,個(gè)人電腦的資源被使用的很少,感覺Browser就是一個(gè)NC(Network Computer)?我們居然又回到了Sun最初提出的構(gòu)想,Sun是不是太強(qiáng)大了些?
相關(guān)文章
JavaScript 原型繼承,學(xué)習(xí)js面向?qū)ο蟮呐笥芽梢钥纯础?/div> 2011-12-12
JavaScript 基于原型的對(duì)象(創(chuàng)建、調(diào)用)
在我們寫js代碼的時(shí)候,內(nèi)部對(duì)象是不可避免的要引用,但是光靠這些對(duì)象是不夠的,所以需要我們自己定義對(duì)象,這個(gè)時(shí)候通常用到的對(duì)象是第三種,即基于原型的對(duì)象,下面就如何創(chuàng)建自己的對(duì)象,定義對(duì)象的方法、屬性,調(diào)用對(duì)象給出詳細(xì)的說明。2009-10-10
javascript 設(shè)計(jì)模式之單體模式 面向?qū)ο髮W(xué)習(xí)基礎(chǔ)
單體是在腳本加載時(shí)創(chuàng)建的,能將一系列有關(guān)聯(lián)的變量和方法組織為一個(gè)邏輯單元,邏輯單元里面的內(nèi)容通過單一的變量進(jìn)行訪問,也是筆記基礎(chǔ)與常用的面向?qū)ο蟮亩x方法。2010-04-04
一實(shí)用的實(shí)現(xiàn)table排序的Javascript類庫
一實(shí)用的實(shí)現(xiàn)table排序的Javascript類庫...2007-09-09
JS類定義原型方法的兩種實(shí)現(xiàn)的區(qū)別評(píng)論很多
JS類定義原型方法的兩種實(shí)現(xiàn)的區(qū)別評(píng)論很多...2007-09-09
javascript面向?qū)ο缶幊?一) 實(shí)例代碼
javascript面向?qū)ο缶幊虒?shí)例代碼,代碼也算比較基礎(chǔ)了,不懂得朋友可以參考腳本之家之前發(fā)布的文章。2010-06-06
[推薦]javascript 面向?qū)ο蠹夹g(shù)基礎(chǔ)教程
看了很多介紹javascript面向?qū)ο蠹夹g(shù)的文章,很暈.為什么?不是因?yàn)閷懙貌缓?而是因?yàn)樘願(yuàn)W. javascript中的對(duì)象還沒解釋清楚怎么回事,一上來就直奔主題,類/繼承/原型/私有變量....2009-03-03最新評(píng)論

