??? 面向?qū)ο蟮?/span> JavaScript 編程


??? Javascript
對(duì)于做過(guò) Web 程序的人不應(yīng)該是陌生,初期是用來(lái)做一些簡(jiǎn)單的 FORM 驗(yàn)證,基本上是在玩弄一些技巧性的東西。 IE 4.0 引入了 DHTML ,同時(shí)為了對(duì)抗 Netscape Javascript, 提出了自己的腳本語(yǔ)言 JScript ,除了遵循 EMAC 的標(biāo)準(zhǔn)之外,同時(shí)增加了許多擴(kuò)展,如下要提到的 OOP 編程就是其中的一個(gè),為了命且概念,我以下提到的 Javascript 都是 Microsoft Internet Explorer 4.0 以上實(shí)現(xiàn)的 JScript, 對(duì)于 Netscape ,我沒(méi)有做過(guò)太多的程序,所以一些的區(qū)別也就看出來(lái)。


??? Javascript
不是一個(gè)支持面向?qū)ο蟮恼Z(yǔ)言,更加算不上一個(gè)開(kāi)發(fā)平臺(tái),但是 Javascript 提供了一個(gè)非常強(qiáng)大的基于 prototype 的面向?qū)ο笳{(diào)用功能,你可以在你自己需要的地方使用他們。因此 , 如何使用對(duì)象?本文盡可能從 Javascript 面向?qū)ο髮?shí)現(xiàn)原理出發(fā),解析清楚它的工作模型。在了解這些模型之后,你可以在自己的腳本庫(kù)中編寫(xiě)一些實(shí)現(xiàn)代碼,然后在其他地方調(diào)用。

?

??? Javascript 的語(yǔ)法和 C++ 很接近,不過(guò)在類(lèi)實(shí)現(xiàn)中沒(méi)有使用關(guān)鍵字 Class, 實(shí)現(xiàn)繼承的時(shí)候也沒(méi)有采用傳統(tǒng)的 Public 或者 Implement 等等所謂的關(guān)鍵字來(lái)標(biāo)示類(lèi)的實(shí)現(xiàn)。這樣的情況下,可能有就有人會(huì)問(wèn),如何編寫(xiě) Javascript Class ,如何實(shí)現(xiàn)繼承。我開(kāi)始也是百思不得其解,后來(lái)看了 MSDN, 才知道采用了 prototype 來(lái)實(shí)現(xiàn),包括繼承和重載,也可以通過(guò)這個(gè)關(guān)鍵字來(lái)實(shí)現(xiàn)。

?

??? Javascript 的函數(shù)很奇怪,每個(gè)都是默認(rèn)實(shí)現(xiàn)了 Optional 的,即參數(shù)都可以可選的, function a(var1,var2,var3), 在調(diào)用的過(guò)程中 a(),a(value1),a(value1,value2) 等等的調(diào)用都是正確的,至少在即使編譯部分可以完整通過(guò),至于其它,只是和函數(shù)的實(shí)現(xiàn)邏輯比較相關(guān)了。

??? 以下就 JS 對(duì)于類(lèi)的實(shí)現(xiàn)、繼承、重載詳細(xì)介紹其實(shí)現(xiàn)方式。

??? 1 。實(shí)現(xiàn)

??? Js 類(lèi)的實(shí)現(xiàn)就通過(guò)函數(shù)直接實(shí)現(xiàn)的,每個(gè)函數(shù)可以直接看成 class ,如下代碼

??? function ClassTest1(){

??????? ...//implement code

??? }

??? var a=new ClassTest1

???

??? function ClassTest2(var1){

??? ??? ...//implement code

??? }

??? var b=new ClassTest("value")

??? 對(duì)于類(lèi)的屬性,可以通過(guò)兩種方式實(shí)現(xiàn)

??? 1 this."<Property or Method" 的方式實(shí)現(xiàn),在類(lèi)聲明函數(shù)中直接給出函數(shù)的實(shí)現(xiàn),如 this.Add=new function(strUserName,strPassword) 這樣的方式調(diào)用,編寫(xiě)的方式在 Class Function 中調(diào)用。

??? 2 通過(guò) ClassFunction.prototype.[FunctionName]=function(var1,var2...){//todo} 這樣的方式完成調(diào)用。

??? 這兩種方式從目標(biāo)來(lái)看是一致的,按照我個(gè)人的觀點(diǎn)來(lái)看,區(qū)別的只是在于實(shí)現(xiàn)方式,通過(guò) this.propertyName 的方式來(lái)創(chuàng)建, Jscript 自動(dòng)創(chuàng)建了 property 或者 method 的入口,不過(guò)從程序的角度而言,還是使用 prototype 的關(guān)鍵字實(shí)現(xiàn)比較靈活。

???

??? 另外 Javascript 也可以和我們 C++ 中那種嵌套聲明的方法來(lái)聲明 ,C++ 實(shí)現(xiàn)的方法如下

??? Public Class ClassName:ParentClass{

??? ??? Public DataType?FunctionName(){

?

??? ??? }

??? ??? Public Class?ClassName{

??????????? Public DataType?FunctionName(){

??? ??? ??? }

??? ??? }

??? }

??? Javascript 當(dāng)中,當(dāng)然不存在 class 這樣的關(guān)鍵字了 , 所以實(shí)現(xiàn)起來(lái)有點(diǎn)戲劇性,不過(guò)仍然為一個(gè)非常巧妙的實(shí)現(xiàn)。

??? function className(){

??? ??? //Property Implement

??? ??? this.UserName="blue";

??? ??? //Method Implement

??? ??? this.Add=new function(){

?

??? ??? }

??????? //Sub Class Implement

??? ??? function SubClassName(){

??? ??? ??? this.PropertyName="hi"???????????

??????? }

??? ??? //sub class method implement

??????? SubClassName.prototype.Change=function{

?

??? ??? }

??? }

??? //Main Class Method Implement

????className.prototype.Delete=function(){

?

??? }

??? 如上的代碼大致演示了 Javascript 類(lèi)中屬性和方法的實(shí)現(xiàn),另外有一點(diǎn)比較困惑,整個(gè) class 中都是 public 的,沒(méi)有關(guān)鍵字 private 之類(lèi)的可以控制某些方法是否隱藏,那么在我們編寫(xiě)代碼實(shí)現(xiàn)的規(guī)范中,我看國(guó)外一些程序員都是使用 _functionName 這樣子為函數(shù)命的方法來(lái)區(qū)分,但是在調(diào)用過(guò)程中實(shí)際還可以調(diào)用的。

??? 實(shí)現(xiàn)了屬性和方法,剩下的就是 Event 的實(shí)現(xiàn)了,我查找了許多資料,包括整個(gè) MSDN 關(guān)于 JScript 的參考,都沒(méi)有看到一個(gè)很好的模型關(guān)于事件實(shí)現(xiàn)的,后來(lái)參考了一些站點(diǎn)編寫(xiě) HTA(HTML Component, 有空我會(huì)寫(xiě)一些相關(guān)的文章)的實(shí)現(xiàn),借助于比較扭曲(我個(gè)人認(rèn)為)的方法可以大致的實(shí)現(xiàn)基于事件驅(qū)動(dòng)的功能。大致的思路是這樣子的:

??? 1 . 將所有的事件定義成屬性,只要簡(jiǎn)單的聲明就可以

??? 2 . 在需要觸發(fā)事件的代碼中判斷事件屬性是否是一個(gè)函數(shù),如果是函數(shù),直接執(zhí)行函數(shù)代碼,如果是字符串,那么執(zhí)行字符串函數(shù),通過(guò) eval(str) 來(lái)執(zhí)行。

??? 3) . 在類(lèi)的實(shí)例當(dāng)中注冊(cè)事件函數(shù)。

??? 為了簡(jiǎn)單說(shuō)明如上的思路,采用 timer 這樣簡(jiǎn)單的例子來(lái)表述如上的所提到的內(nèi)容,如果只是為了簡(jiǎn)單的實(shí)現(xiàn) timer 的功能, Javascript setInterval 函數(shù)就可以滿(mǎn)足全部的要求,如下的代碼只是用來(lái)說(shuō)明 Timer 的工作原理。

//Class For Timer
function Timer(iInterval){
?//if not set the timer interval ,then defalut set to 500ms
?this.Interval=iInterval || 500;
?this._handleInterval;
?this.TimerEvent=null
?function Start(){
??if(this.Interval!=0){
???this._handleInterval=setInterval("TimerCallBack()",this.Interval);
??}
?}
?function Start(){
??clearInterval(this._handleInterval);
?}
?function TimerCallBack(){
??if (typeof this.TimerEvent=="function"){
???this.TimerEvent();
??}
??else if(this.TimerEvent!=null && this.TimerEvent.length>0){
???eval(this.TimerEvent);
??}
?}
}?
//Code for Instance
var t=new Timer(3);

//------------------------------------//

//1.
t.TimerEvent=function(){
//todo
}

//2.
t.TimerEvent="alert(\"hello\")";

//3.

t.TimerEvent=tTimerCall;

//----------------------------------//
t.Start();
t.Stop();

function tTimerCall(){

?

}

?

??? 實(shí)際工作代碼是在 TimerCallBack() 上面實(shí)現(xiàn),事件觸發(fā)作為屬性的方式來(lái)實(shí)現(xiàn),在應(yīng)用實(shí)例中,代碼提供了三種方法去調(diào)用事件,不過(guò)在事件的回調(diào)當(dāng)中,我還沒(méi)有想到如何可以帶參數(shù),只有才各自的實(shí)現(xiàn)當(dāng)中訪問(wèn)各自需要的屬性才能夠?qū)崿F(xiàn)全部的要求。

?

??? 2 。繼承。

??? 剛采用了大篇幅的文字去介紹如何實(shí)現(xiàn) Javascript 的各種實(shí)現(xiàn),也就是從邏輯上完成了一個(gè)封裝 class 的實(shí)現(xiàn),從某種意義上來(lái)說(shuō), class 的實(shí)現(xiàn)是真正腳本編程中使用最多的部分,不過(guò)如果只是要完成如上的功能,使用 VBScript 來(lái)編寫(xiě)更能更加清晰,畢竟 VBscript 提供了 class 關(guān)鍵字,同時(shí)提供了 public private 這兩個(gè)關(guān)鍵字,可以清晰的將公共和私有對(duì)象分離,至于事件的實(shí)現(xiàn),也可以采用類(lèi)似 Javascript 實(shí)現(xiàn)的思路,只是對(duì)于函數(shù)的引用需要采用 GetRef 這個(gè)函數(shù),具體的用法可以參考 scripting reference,MSDN 里頭也有詳細(xì)的介紹,而 Javascript 強(qiáng)大至于在于如下要說(shuō)的了,雖然具體的東西可能不多。

??? 如上所言,我們已經(jīng)完成了一個(gè)基本的類(lèi)實(shí)現(xiàn) Timer ,現(xiàn)在要做的是重新編寫(xiě)這個(gè)類(lèi),我們簡(jiǎn)單的只是想在這個(gè)類(lèi)之中加入一個(gè)方法,提供當(dāng)前的系統(tǒng)時(shí)間,方法的名稱(chēng)為 getSystemDate, 顯然如果全部重新編寫(xiě),那就失去了我這里說(shuō)的意義了。先看看如下的實(shí)現(xiàn)。

??? function NewTimer(iInterval){

??? ??? //call super

??? ??? this.base=Timer;

??? ??? this.base iInterval);???????

??? }

??? NewTimer.prototype=new Timer;

??? NewTimer.prototype.getSystemDate=function(){

??? ??? var dt=new Date();

??? ??? return dt.getYear()+"-"+dt.getMonth()+"-"+dt.getDay() ;

??? }

???

??? 上述代碼實(shí)現(xiàn)了 NewTimer 類(lèi),從 Timer 繼承, Javascript 沒(méi)有使用 或者 java public 那樣類(lèi)似的關(guān)鍵字,只是通過(guò) newclassname.prototype=new baseclass 這樣的方法來(lái)完成,同時(shí) NewTimer 實(shí)現(xiàn)了 getSystemDate 的方法,在 NewTimer 的初始化函數(shù)中,我使用了 this.base=Timer ,是為了引用父類(lèi)的實(shí)現(xiàn),不過(guò)在對(duì)于父類(lèi)其他實(shí)現(xiàn)函數(shù)的調(diào)用,到現(xiàn)在我沒(méi)有找到一個(gè)確定的方法,是否通過(guò) this.base.start() 那樣來(lái)調(diào)用還是其他的,如果有誰(shuí)比較清楚的,麻煩告訴我,另外在 netscape 的站點(diǎn)上,我查到有一個(gè)特殊的 "__proto__" 的屬性好像是對(duì)于父類(lèi)的直接引用,不過(guò)具體的我也沒(méi)有嘗試過(guò),在 msdn 中也沒(méi)有看到對(duì)于 __proto__ 的支持。

???

??? 3 。重載

??? 或許這個(gè)是 OOP 編程中比較復(fù)雜的地方了,在 Javascript 的實(shí)現(xiàn)中有點(diǎn)無(wú)奈,也就是通過(guò) prototype 的方式來(lái)完成的,不過(guò)因?yàn)槲也磺宄绾握{(diào)用父類(lèi)的實(shí)現(xiàn)函數(shù),那么在重載中只能夠重新編寫(xiě)所有的實(shí)現(xiàn)了,另外就是在實(shí)現(xiàn)中實(shí)例化一個(gè)父類(lèi),然后通過(guò)調(diào)用它來(lái)返回需要的東西。

??? Javascript 中所有的對(duì)象都是從 Object 繼承下來(lái)的, object 提供了 toString() 的方法,也就是說(shuō)如果調(diào)用 alert(objInstance) 這樣的過(guò)程,實(shí)際上是調(diào)用了 alert(objInstance.toString()) 的方法,如果沒(méi)有編寫(xiě)實(shí)現(xiàn), object 默認(rèn)的 toString() 都是 "object object" 這樣子的,在許多地方需要重載這個(gè)函數(shù)的,比如 Timer, 如果我們希望 var ins=new Timer(5);alert(ins) 調(diào)用得到的是 interval 的值 5 ,那么就需要重新編寫(xiě) toString() 方法了

??? Timer.prototype.toString=function(){ return this.Interval};

??? 以上代碼實(shí)現(xiàn)之后 alert(ins) 得到的就是 5 了。

?

?



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1926