原文地址:http://www.cnblogs.com/Truly/archive/2007/07/24/830013.html
前言
OO(面向?qū)ο?概念的提出是軟件開發(fā)工程發(fā)展的一次革命,多年來(lái)我們借助它使得很多大型應(yīng)用程序得以順利實(shí)現(xiàn)。如果您還沒(méi)有掌握并使用OO進(jìn)行程序設(shè)計(jì)和開發(fā),那么您無(wú)疑還停留在軟件開發(fā)的石器時(shí)代。大多數(shù)編程語(yǔ)言,尤其是近年問(wèn)世的一些語(yǔ)言,都很好的支持了面向?qū)ο?,您可能?duì)此了如執(zhí)掌,但是一些語(yǔ)言在OO方面卻無(wú)法與其它高級(jí)語(yǔ)言相比,在這些語(yǔ)言上進(jìn)行面向?qū)ο蟪绦蛟O(shè)計(jì)和開發(fā)會(huì)有些困難,例如本文要討論的JavaScript。JavaScript是一門古老的語(yǔ)言,但是隨著近期Web2.0 技術(shù)的熱捧,這門語(yǔ)言又重新煥發(fā)出青春的光輝,借助于JavaScript客戶端技術(shù),我們的Web體驗(yàn)變得豐富而又精彩,為了設(shè)計(jì)和開發(fā)更加完善、復(fù)雜的客戶端應(yīng)用,我們必須掌握J(rèn)avaScript上的OO方法,這正是本文要討論的。 前幾天閱讀了MSDN的《使用面向?qū)ο蟮募夹g(shù)創(chuàng)建高級(jí) Web 應(yīng)用程序》一文,覺(jué)得還有些東西有必要繼續(xù)探討補(bǔ)充一下,就有了本文。
目錄
開始
對(duì)象的聲明
成員的聲明
全局變量和局部變量
命名空間
開始
JavaScript是一門相當(dāng)靈活的語(yǔ)言,語(yǔ)法也相當(dāng)寬松,并且入門門檻很低,您可以不費(fèi)什么力氣就編寫出一大堆可以運(yùn)行的代碼,但是根據(jù)我在實(shí)際工作中的經(jīng)驗(yàn),多數(shù)人還是對(duì)之核心技術(shù)知之甚少。同樣一個(gè)功能,簡(jiǎn)簡(jiǎn)單單幾行代碼,就可看出一個(gè)人的技術(shù)功底。正如天龍八部中的蕭峰使用的一招“太祖長(zhǎng)拳”,這是一種武術(shù)中的入門的招法,雖然它看上去很簡(jiǎn)單,但是在高手的使用下,卻是威力無(wú)窮。其實(shí)越是簡(jiǎn)單的東西,要把它變得完美就越是困難。所以作為能工巧匠的您怎能錯(cuò)過(guò)這篇文章?切聽我一一道來(lái)。
對(duì)象的聲明
在JavaScript我們可以使用下面幾種代碼進(jìn)行對(duì)象聲明:
var MyObject = {};
function MyObject(){blabalbla...}
var MyObject = function(){balbalba...};
對(duì)于后兩種方法,我們還可以增加參數(shù),這樣就類似于一個(gè)帶參數(shù)的構(gòu)造器了,例如:
function MyObject(msg){alert(msg);}var o = new MyObject("Hello world");var MyObject = function(msg){alert(msg + " again");};var o = new MyObject("Hello world!");
甚至我們可以使用字符串來(lái)生聲明函數(shù),這使得我們的程序更加靈活:
/** Function可以有多個(gè)入口參數(shù),最后一個(gè)參數(shù)最為方法體。*/var MyObject = new Function("msg","alert(msg);");var o = new MyObject("Hello world!");
成員的聲明
在JavaScript中,要聲明一個(gè)對(duì)象的成員也非常簡(jiǎn)單,但是跟其它的高級(jí)程序仍然略有不同,請(qǐng)看下面的示例:
var MyObject = {FirstName:"Mary",LastName:"Cook",Age:18,ShowFullName : function(){alert(this.FirstName + ' ' + this.LastName);}}MyObject.ShowFullName();
或者使用字符串來(lái)聲明:
var MyObject = {"FirstName":"Mary","LastName":"Cook","Age":18,"ShowFullName" : function(){alert(this.FirstName + ' ' + this.LastName);}}MyObject.ShowFullName();
用字符串的聲明方式有諸多好處,這也是JavaScript中表示對(duì)象的一種特殊方式,像近年JSON概念的提出,將這種特殊方式提示到了一個(gè)新的高度,更多JSON的介紹請(qǐng)參加我以前的大作《深入淺出JSON》。
而在實(shí)際的程序設(shè)計(jì)中,這種方式在JavaScript的面向?qū)ο蟪绦蛟O(shè)計(jì)中我們通常用來(lái)映射數(shù)據(jù)類型,定義類似高級(jí)語(yǔ)言中的結(jié)構(gòu),集合,實(shí)體等,還常常用作定義靜態(tài)幫助器類,無(wú)需構(gòu)造而可以直接訪問(wèn)成員方法。例如上面代碼中的MyObject.ShowFullName();
前面我們介紹了成員的定義,在JavaScript中另一個(gè)面向?qū)ο筇攸c(diǎn)是我們可以像高級(jí)編程語(yǔ)言一樣使用.和[]引用成員,如:
var DateTime = { Now : new Date(), "Show" : function() { alert(new Date());} };alert(DateTime.Now);// 等價(jià)于:alert(DateTime["Now"]);DateTime.Show()// 等價(jià)于:DateTime["Show"]();
提到方法調(diào)用,這里有一些知識(shí)需要知道,在JavaScript中,所有的對(duì)象的基類是Object,基類通過(guò)prototype定義了很多成員和方法,例如toString, toLocaleString, valueOf等
這里我以toString()來(lái)做一介紹,請(qǐng)看下面示例:
var obj = { "toString" : function() { return "This is an object."; } };alert(obj);
我們注意到當(dāng)alert的時(shí)候,toString()方法被調(diào)用了,事實(shí)上,當(dāng)javascript需要將一個(gè)對(duì)象轉(zhuǎn)換成字符串時(shí)就隱式調(diào)用這個(gè)對(duì)象的toString()方法,例如alert或者document.write或者字符串需要進(jìn)行+運(yùn)算等等。參加下面示例代碼:
Date.prototype.toString = function(){ alert('This is called');}var dt= new Date(new Date());Date.prototype.toString = function(){ alert('This is called');}var dt= new Date() + 1;
通過(guò)這個(gè)例子我們驗(yàn)證了這一點(diǎn),即使一個(gè)對(duì)象作為入口參數(shù)也可能會(huì)調(diào)用其toString方法。除了這一點(diǎn)外,該示例同時(shí)演示了如何覆蓋基類中定義的方法。
全局變量和局部變量
在JavaScript中,在全局上下文中聲明的變量作為全局變量,而在對(duì)象或方法內(nèi)部聲明的對(duì)象則作為本地變量。請(qǐng)參見下面的代碼:
var global = 1;function mm(){var global = 2; // 聲明本地變量alert(this.global); // 等價(jià)于alert(global);}mm();alert(this.global); // 等價(jià)于alert(global);
上面例子我們可以看出本地變量和全局變量即使同名也不會(huì)出現(xiàn)沖突。
另外Javascript有一個(gè)特性就是變量不用聲明就可以使用,在第一次使用一個(gè)未聲明的變量時(shí),系統(tǒng)會(huì)自動(dòng)聲明該變量,并將其作為全局變量。但是在構(gòu)建大型應(yīng)用程序的時(shí)候,這一點(diǎn)是非常具有破壞性的,如果該變量名在多個(gè)腳本塊中出現(xiàn),引起變量名沖突,導(dǎo)致嚴(yán)重的程序錯(cuò)誤。因此,我們應(yīng)該盡量避免使用全局變量,并且保持先聲明后使用的良好習(xí)慣。
在JavaScript中this關(guān)鍵字是比較重要的一個(gè)特點(diǎn),它會(huì)隨調(diào)用對(duì)象而發(fā)生改變,始終與當(dāng)前對(duì)象的上下文保持一致,這里一個(gè)例子讓我們演示this并且同時(shí)繼續(xù)深入研究toString,首先我們使用構(gòu)造器方式創(chuàng)建一個(gè)對(duì)象,代碼如下:
function obj(params){toString = function() { return 'This is an object.'; }}alert(new obj());
你會(huì)發(fā)現(xiàn)當(dāng)運(yùn)行這段代碼的時(shí)候,瀏覽器將會(huì)拋出一個(gè)錯(cuò)誤。
下面我們?cè)倏戳硗鈨啥未a:
function obj(params){aMethod = function() { return 'This is global method.'; }}alert(new obj()); // 正常執(zhí)行function obj(params){this.toString = function() { return 'This is local method.'; }}alert(new obj()); // 正常執(zhí)行
第一個(gè)函數(shù)聲明雖沒(méi)有使用this關(guān)鍵字,這時(shí)如果初始化對(duì)象那么將聲明一個(gè)全局方法aMethod。第二個(gè)函數(shù)聲明則為對(duì)象定義了一個(gè)自己的toString()方法。
當(dāng)分析這兩個(gè)函數(shù)的時(shí)候,你會(huì)注意到JavaScript的另一個(gè)特性,解釋執(zhí)行,所以
function obj(params){aMethod = function(){return 'This is global method.';}}alert(aMethod()); // 次句會(huì)報(bào)錯(cuò)function obj(params){aMethod = function(){return 'This is global method.';}}new obj(); // 實(shí)例化的時(shí)候,聲明了全局變量阿Method()方法alert(aMethod()); // 正常執(zhí)行
通過(guò)上面的例子我們知道關(guān)鍵字this非常重要,如果使用不當(dāng),可能會(huì)造成全局函數(shù)的改變。有一點(diǎn)需要記住,絕不要調(diào)用包含“this”(卻沒(méi)有所屬對(duì)象)的函數(shù)。否則,將違反全局命名空間,因?yàn)樵谡{(diào)用這樣的函數(shù)時(shí),“this”將引用全局對(duì)象,而這必然會(huì)給您的應(yīng)用程序帶來(lái)災(zāi)難。
如下面的例子,當(dāng)對(duì)象沒(méi)有定義this指定的函數(shù)(isNaN)時(shí),那么可能覆蓋全局的同名函數(shù),看一些代碼示例:
正確使用this的例子:
alert(isNaN(1)); // 全局函數(shù)isNaNfunction obj(params){this.toString = function(){return 'This is an object.';};this.isNaN = function(){return 'not anymore';};}var o = new obj(); // 正確使用方式,調(diào)用構(gòu)造函數(shù)alert(o.isNaN(1)); // 此時(shí)obj定義中的this指向o這個(gè)實(shí)例而不是全局上下文alert(isNaN(1)); // 全局函數(shù)未發(fā)生變化
錯(cuò)誤的例子:
alert(isNaN(1)); // 全局函數(shù)isNaNfunction obj(params){isNaN = function(){return 'not anymore';}}obj(); // 錯(cuò)誤的使用方式,this指向全局上下文,全局函數(shù)isNaN被覆蓋alert(isNaN(1)); // 全局函數(shù)發(fā)生改變
同時(shí)我們還注意到有一些全局函數(shù)則無(wú)法覆蓋,例如toString()
下面我們看JavaScript的一個(gè)很好用的方法:call
關(guān)于call的解釋:
call方法可以用來(lái)代替另一個(gè)對(duì)象調(diào)用一個(gè)方法。call方法可以將一個(gè)函數(shù)的對(duì)象上下文從初始化的上下文改變?yōu)橛蓆hisObj指定的新對(duì)象。
可以這樣來(lái)理解:
我們定義了一個(gè)函數(shù)abc:
function abc(){alert(this.member1);}var obj = {member1:"Hello world!",show:abc};var obj2 = {member1:"Hello world again!",show:abc};obj.show();// 也可以使用abc.call(obj);abc.call(obj2);
修改后的另一個(gè)版本:
member1 = 'test';function abc(){alert(this.member1);}var obj = { member1:"Hello world", show:abc};var obj2 = { member1:"Hello world again", show:abc};obj.show();// 也可以使用abc.call(obj);abc.call(obj2);abc(); // 此時(shí)abc中的this指向了當(dāng)前上下文
每個(gè)函數(shù)都有call方法,上面的過(guò)程中我們看到用另一個(gè)對(duì)象代替調(diào)用顯示方法,并且注意到this在對(duì)象上下文中的改變。
通過(guò)上面基礎(chǔ)知識(shí)的研究,讓我們?cè)傧蚯翱绯鲆徊?,使?strong>call的特性來(lái)實(shí)現(xiàn)類的繼承,參見下面的示例:
// 統(tǒng)一類構(gòu)造器function MyClassInitor(){this.base();if(!this.mm){alert('未定義成員函數(shù):mm()');}return this;}// 定義一個(gè)基類function baseClass(){if(!this.tt) // 判斷該成員是否被繼承類覆蓋{this.tt = '基類成員';}}// 從基類繼承var obj = { member1:"Hello world", base:baseClass, gg:function(){ alert('I am an GG');}};var obj2 = { member1:"Hello world again", base:baseClass,mm:function(name){alert('I am MM '+name + '.');}, tt:"覆蓋基類的tt成員"};var o = MyClassInitor.call(obj);var o2 = MyClassInitor.call(obj2);alert(o.tt);alert(o2.tt);o2.mm('Mary');
雖然跟高級(jí)編程語(yǔ)言的語(yǔ)法有點(diǎn)不同,但是你必須了解JavaScript的語(yǔ)法特點(diǎn)。通過(guò)這個(gè)例子,我們什么分析了this和call的配合,但是實(shí)際進(jìn)行類繼承設(shè)計(jì)時(shí)往往不會(huì)采用此方法進(jìn)行實(shí)現(xiàn),后面我們介紹Prototype時(shí)再做詳細(xì)介紹。
命名空間
前面我們了解完類、對(duì)象的聲明,下面看一下Javascript中命名空間的處理,大家知道,在高級(jí)編程語(yǔ)言中我們非常熟練的使用命名空間來(lái)避免變量或方法名的沖突,那么同樣我們也可以在JavaScript中使用命名空間來(lái)為我們的類和方法進(jìn)行界定。在JavaScript中命名空間的聲明與其它高級(jí)語(yǔ)言略有不同,下面是一個(gè)命名空間聲明的示例:
var System = {};var System.Web = {};
通過(guò)這兩行代碼我們就有了System和System.Web兩個(gè)命名空間,回想一下前面我們介紹的知識(shí),你很快可以發(fā)現(xiàn),這是兩個(gè)對(duì)象聲明語(yǔ)句。在JavaScript中,我們正是使用對(duì)象來(lái)表示命名空間的。但是你必須清楚一點(diǎn),由于JavaScript的特性,在實(shí)際應(yīng)用中,我們不能這么簡(jiǎn)單的來(lái)處理命名空間,因?yàn)槁暶髡Z(yǔ)句可能同時(shí)出現(xiàn)在多個(gè)地方或者多個(gè)js文件中,我們知道,在JavaScript中,最后聲明的變量會(huì)覆蓋前面同名的變量,因此通常我們要加一些判斷代碼來(lái)防止重復(fù)聲明,例如:
if(typeof System == 'undefined') var System = {};
這樣即使這段代碼在程序中重復(fù)出現(xiàn)多次我們也可以保證System對(duì)象只聲明一次。關(guān)于這一點(diǎn),大家如果深入研究過(guò)AjaxPro和其它很多大型JavaScript框架,會(huì)發(fā)現(xiàn)當(dāng)配合后端應(yīng)用程序的時(shí)候,它是多么的有用。例如AjaxPro的類型注冊(cè),關(guān)于AjaxPro可參見我另一篇文章《AjaxPro框架剖析》。
聯(lián)系客服