JavaScript的面向?qū)ο缶幊袒A(chǔ)
重新認(rèn)識面向?qū)ο?br /> 為了說明 JavaScript 是一門徹底的面向?qū)ο蟮恼Z言,首先有必要從面向?qū)ο蟮母拍钪?, 探討一下面向?qū)ο笾械膸讉€概念:
- 一切事物皆對象
- 對象具有封裝和繼承特性
- 對象與對象之間使用消息通信,各自存在信息隱藏
以這三點(diǎn)做為依據(jù),C++ 是半面向?qū)ο蟀朊嫦蜻^程語言,因?yàn)?,雖然他實(shí)現(xiàn)了類的封裝、繼承和多態(tài),但存在非對象性質(zhì)的全局函數(shù)和變量。Java、C# 是完全的面向?qū)ο笳Z言,它們通過類的形式組織函數(shù)和變量,使之不能脫離對象存在。但這里函數(shù)本身是一個過程,只是依附在某個類上。
然而,面向?qū)ο髢H僅是一個概念或者編程思想而已,它不應(yīng)該依賴于某個語言存在。比如 Java 采用面向?qū)ο笏枷霕?gòu)造其語言,它實(shí)現(xiàn)了類、繼承、派生、多態(tài)、接口等機(jī)制。但是這些機(jī)制,只是實(shí)現(xiàn)面向?qū)ο缶幊痰囊环N手段,而非必須。換言之,一門語言可以根據(jù)其自身特性選擇合適的方式來實(shí)現(xiàn)面向?qū)ο蟆K?,由于大多?shù)程序員首先學(xué)習(xí)或者使用的是類似 Java、C++ 等高級編譯型語言(Java 雖然是半編譯半解釋,但一般做為編譯型來講解),因而先入為主地接受了“類”這個面向?qū)ο髮?shí)現(xiàn)方式,從而在學(xué)習(xí)腳本語言的時候,習(xí)慣性地用類式面向?qū)ο笳Z言中的概念來判斷該語言是否是面向?qū)ο笳Z言,或者是否具備面向?qū)ο筇匦?。這也是阻礙程序員深入學(xué)習(xí)并掌握 JavaScript 的重要原因之一。
實(shí)際上,JavaScript 語言是通過一種叫做 原型(prototype)的方式來實(shí)現(xiàn)面向?qū)ο缶幊痰摹O旅婢蛠碛懻?基于類的(class-based)面向?qū)ο蠛?基于原型的 (prototype-based) 面向?qū)ο筮@兩種方式在構(gòu)造客觀世界的方式上的差別。
基于類的面向?qū)ο蠛突谠偷拿嫦驅(qū)ο蠓绞奖容^
在基于類的面向?qū)ο蠓绞街?,對象(object)依靠 類(class)來產(chǎn)生。而在基于原型的面向?qū)ο蠓绞街?,對象(object)則是依靠 構(gòu)造器(constructor)利用 原型(prototype)構(gòu)造出來的。舉個客觀世界的例子來說明二種方式認(rèn)知的差異。例如工廠造一輛車,一方面,工人必須參照一張工程圖紙,設(shè)計(jì)規(guī)定這輛車應(yīng)該如何制造。這里的工程圖紙就好比是語言中的 類 (class),而車就是按照這個 類(class)制造出來的;另一方面,工人和機(jī)器 ( 相當(dāng)于 constructor) 利用各種零部件如發(fā)動機(jī),輪胎,方向盤 ( 相當(dāng)于 prototype 的各個屬性 ) 將汽車構(gòu)造出來。
事實(shí)上關(guān)于這兩種方式誰更為徹底地表達(dá)了面向?qū)ο蟮乃枷?,目前尚有爭論。但筆者認(rèn)為原型式面向?qū)ο笫且环N更為徹底的面向?qū)ο蠓绞?,理由如下?br />
首先,客觀世界中的對象的產(chǎn)生都是其它實(shí)物對象構(gòu)造的結(jié)果,而抽象的“圖紙”是不能產(chǎn)生“汽車”的,也就是說,類是一個抽象概念而并非實(shí)體,而對象的產(chǎn)生是一個實(shí)體的產(chǎn)生;
其次,按照一切事物皆對象這個最基本的面向?qū)ο蟮姆▌t來看,類 (class) 本身并不是一個對象,然而原型方式中的構(gòu)造器 (constructor) 和原型 (prototype) 本身也是其他對象通過原型方式構(gòu)造出來的對象。
再次,在類式面向?qū)ο笳Z言中,對象的狀態(tài) (state) 由對象實(shí)例 (instance) 所持有,對象的行為方法 (method) 則由聲明該對象的類所持有,并且只有對象的結(jié)構(gòu)和方法能夠被繼承;而在原型式面向?qū)ο笳Z言中,對象的行為、狀態(tài)都屬于對象本身,并且能夠一起被繼承(參考資源),這也更貼近客觀實(shí)際。
最后,類式面向?qū)ο笳Z言比如 Java,為了彌補(bǔ)無法使用面向過程語言中全局函數(shù)和變量的不便,允許在類中聲明靜態(tài) (static) 屬性和靜態(tài)方法。而實(shí)際上,客觀世界不存在所謂靜態(tài)概念,因?yàn)橐磺惺挛锝詫ο?!而在原型式面向?qū)ο笳Z言中,除內(nèi)建對象 (build-in object) 外,不允許全局對象、方法或者屬性的存在,也沒有靜態(tài)概念。所有語言元素 (primitive) 必須依賴對象存在。但由于函數(shù)式語言的特點(diǎn),語言元素所依賴的對象是隨著運(yùn)行時 (runtime) 上下文 (context) 變化而變化的,具體體現(xiàn)在 this 指針的變化。正是這種特點(diǎn)更貼近 “萬物皆有所屬,宇宙乃萬物生存之根本”的自然觀點(diǎn)。
JavaScript 面向?qū)ο蠡A(chǔ)知識
雖然 JavaScript 本身是沒有類的概念,但它仍然有面向?qū)ο蟮奶匦?,雖然和一般常見的面向?qū)ο笳Z言有所差異。
簡單的創(chuàng)建一個對象的方法如下:
function myObject() {
};
JavaScript 中創(chuàng)建對象的方法一般來說有兩種:函數(shù)構(gòu)造法和字面量法,上面這種屬函數(shù)構(gòu)造法。下面是一個字面量法的例子:
var myObject = {
};
如果僅僅需要一個對象,而不需要對象的其它實(shí)例的情況下,推薦用字面量法。如果需要對象的多個實(shí)例,則推薦函數(shù)構(gòu)造法。
定義屬性和方法
函數(shù)構(gòu)造法:
function myObject() {
this.iAm = 'an object';
this.whatAmI = function() {
console.log('I am ' + this.iAm);
};
};
字面量法:
var myObject = {
iAm : 'an object',
whatAmI : function() {
console.log('I am ' + this.iAm);
}
};
以上兩種方法創(chuàng)建的對象中,都有一個名為 “iAm” 的屬性,還有一個名為 “whatAmI” 的方法。屬性是對象中的變量,方法則是對象中的函數(shù)。
如何獲取屬性及調(diào)用方法:
var w = myObject.iAm; myObject.whatAmI();
調(diào)用方法的時候后面一定要加上括號,如果不加括號,那么它只是返回方法的引用而已。
兩種創(chuàng)建對象方法的區(qū)別
- 函數(shù)構(gòu)造法里面定義屬性和方法的時候,都要用前綴 this,字面量法不需要。
- 函數(shù)構(gòu)造法給屬性和方法賦值的時候用的是 =,字面量法用的是 : 。
- 如果有多個屬性或方法,函數(shù)構(gòu)造法里面用 ; 隔開,字面量法用 , 隔開。
對于字面量法創(chuàng)建的對象,可以直接用對象的引用調(diào)用其屬性或方法:
myObject.whatAmI();
而對于函數(shù)構(gòu)造法而言,需要創(chuàng)建對象的實(shí)例,才能調(diào)用其屬性或方法:
var myNewObject = new myObject(); myNewObject.whatAmI();
使用構(gòu)造函數(shù)
現(xiàn)在再來回歸一下之前的函數(shù)構(gòu)造法:
function myObject() {
this.iAm = 'an object';
this.whatAmI = function() {
console.log('I am ' + this.iAm);
};
};
其實(shí)它看起來就是個函數(shù),既然是函數(shù),能不能給它傳參數(shù)呢?將代碼再稍作修改:
function myObject(what) {
this.iAm = what;
this.whatAmI = function(language) {
console.log('I am ' + this.iAm + ' of the ' + language + ' language');
};
};
再將對象實(shí)例化,并傳入?yún)?shù):
var myNewObject = new myObject('an object');
myNewObject.whatAmI('JavaScript');
程序最終輸出 I am an object of the JavaScript language。
兩種創(chuàng)建對象的方法,我該用哪種?
對于字面量方法而言,因?yàn)樗恍枰獙?shí)例化,所以如果修改了某對象的值,那么這個對象的值就永久地被修改了,其它任何地方再訪問,都是修改后的值。而對于函數(shù)構(gòu)造法而言,修改值的時候是修改其實(shí)例的值,它可以實(shí)例化 N 個對象出來,每個對象都可以擁有自己不同的值,而且互不干擾。比較以下幾段代碼。
先看字面量法:
var myObjectLiteral = {
myProperty : 'this is a property'
};
console.log(myObjectLiteral.myProperty); // log 'this is a property'
myObjectLiteral.myProperty = 'this is a new property';
console.log(myObjectLiteral.myProperty); // log 'this is a new property'
即便創(chuàng)建了一個新的變量指向這個對象,結(jié)果還是一樣的:
var myObjectLiteral = {
myProperty : 'this is a property'
};
console.log(myObjectLiteral.myProperty); // log 'this is a property'
var sameObject = myObjectLiteral;
myObjectLiteral.myProperty = 'this is a new property';
console.log(sameObject.myProperty); // log 'this is a new property'
再看函數(shù)構(gòu)造法:
// 用函數(shù)構(gòu)造法
var myObjectConstructor = function() {
this.myProperty = 'this is a property'
};
// 實(shí)例化一個對象
var constructorOne = new myObjectConstructor();
// 實(shí)例化第二個對象
var constructorTwo = new myObjectConstructor();
// 輸出
console.log(constructorOne.myProperty); // log 'this is a property'
// 輸出
console.log(constructorTwo.myProperty); // log 'this is a property'
和預(yù)期一樣,兩個對象的屬性值是一樣的。如果修個其中一個對象的值呢?
// 用函數(shù)構(gòu)造法
var myObjectConstructor = function() {
this.myProperty = 'this is a property';
};
// 實(shí)例化一個對象
var constructorOne = new myObjectConstructor();
// 修改對象的屬性
constructorOne.myProperty = 'this is a new property';
// 實(shí)例化第二個對象
var constructorTwo = new myObjectConstructor();
// 輸出
alert(constructorOne.myProperty); // log 'this is a new property'
// 輸出
alert(constructorTwo.myProperty); // log 'this is a property'
可以看到,用函數(shù)構(gòu)造法實(shí)例化出來的不同對象,相互是獨(dú)立的,可以各自擁有不同的值。所以說,到底用哪種方法來創(chuàng)建對象,需取決于各自實(shí)際情況。
相關(guān)文章
JavaScript高級程序設(shè)計(jì)(第3版)學(xué)習(xí)筆記10 再訪js對象
在ECMAScript中,兩個核心主題就是對象與函數(shù),而這兩個主題也有些互相纏繞的,在前面幾個博文中大略的過了一遍函數(shù)相關(guān)的基礎(chǔ)知識,這篇文章再回到對象主題上來2012-10-10
js中escape對應(yīng)的C#解碼函數(shù) UrlDecode
js中escape對應(yīng)的C#解碼函數(shù) System.Web.HttpUtility.UrlDecode(s),使用過程中有以下幾點(diǎn)需要注意2012-12-12
在JavaScript中操作時間之getMonth()方法的使用
這篇文章主要介紹了在JavaScript中操作時間之getMonth()方法的使用,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-06-06
基于KMP算法JavaScript的實(shí)現(xiàn)方法分析
本篇文章介紹了,基于KMP算法JavaScript的實(shí)現(xiàn)方法分析。需要的朋友參考下2013-05-05

