Javascript 對於做過 Web 程序的人不應該是陌生,初期是用來做一些簡單的 FORM 驗證,基本上是在玩弄一些技巧性的東西。 IE 4.0 引入了 DHTML ,同時爲了對抗 Netscape 的 Javascript, 提出了自己的腳本語言 JScript ,除了遵循 EMAC 的標準之外,同時增加了許多擴展,如下要提到的 OOP 編程就是其中的一個,爲了命且概念,我以下提到的 Javascript 都是 Microsoft Internet Explorer 4.0 以上實現的 JScript, 對於 Netscape ,我沒有做過太多的程序,所以一些的區別也就看出來。 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
Javascript 不是一個支持面向對象的語言,更加算不上一個開發平臺,但是 Javascript 提供了一個非常強大的基於 prototype 的面向對象調用功能,你可以在你自己需要的地方使用他們。因此 , 如何使用對象?本文儘可能從 Javascript 面向對象實現原理出發,解析清楚它的工作模型。在瞭解這些模型之後,你可以在自己的腳本庫中編寫一些實現代碼,然後在其他地方調用。
Javascript 的語法和 C++ 很接近,不過在類實現中沒有使用關鍵字 Class, 實現繼承的時候也沒有采用傳統的 Public 或者 Implement 等等所謂的關鍵字來標示類的實現。這樣的情況下,可能有就有人會問,如何編寫 Javascript 的 Class ,如何實現繼承。我開始也是百思不得其解,後來看了 MSDN, 才知道採用了 prototype 來實現,包括繼承和重載,也可以通過這個關鍵字來實現。
Javascript 的函數很奇怪,每個都是默認實現了 Optional 的,即參數都可以可選的, function a(var1,var2,var3), 在調用的過程中 a(),a(value1),a(value1,value2) 等等的調用都是正確的,至少在即使編譯部分可以完整通過,至於其它,只是和函數的實現邏輯比較相關了。
以下就 JS 對於類的實現、繼承、重載詳細介紹其實現方式。
1 。實現
Js 類的實現就通過函數直接實現的,每個函數可以直接看成 class ,如下代碼
function ClassTest1(){
...//implement code
}
var a=new ClassTest1
function ClassTest2(var1){
...//implement code
}
var b=new ClassTest("value")
對於類的屬性,可以通過兩種方式實現
1 ) this."<Property or Method" 的方式實現,在類聲明函數中直接給出函數的實現,如 this.Add=new function(strUserName,strPassword) 這樣的方式調用,編寫的方式在 Class Function 中調用。
2 )通過 ClassFunction.prototype.[FunctionName]=function(var1,var2...){//todo} 這樣的方式完成調用。
這兩種方式從目標來看是一致的,按照我個人的觀點來看,區別的只是在於實現方式,通過 this.propertyName 的方式來創建, Jscript 自動創建了 property 或者 method 的入口,不過從程序的角度而言,還是使用 prototype 的關鍵字實現比較靈活。
另外 Javascript 也可以和我們 C++ 中那種嵌套聲明的方法來聲明 ,C++ 實現的方法如下
Public Class ClassName:ParentClass{
Public DataType FunctionName(){
}
Public Class ClassName{
Public DataType FunctionName(){
}
}
}
在 Javascript 當中,當然不存在 class 這樣的關鍵字了 , 所以實現起來有點戲劇性,不過仍然爲一個非常巧妙的實現。
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 類中屬性和方法的實現,另外有一點比較困惑,整個 class 中都是 public 的,沒有關鍵字 private 之類的可以控制某些方法是否隱藏,那麼在我們編寫代碼實現的規範中,我看國外一些程序員都是使用 _functionName 這樣子爲函數命的方法來區分,但是在調用過程中實際還可以調用的。
實現了屬性和方法,剩下的就是 Event 的實現了,我查找了許多資料,包括整個 MSDN 關於 JScript 的參考,都沒有看到一個很好的模型關於事件實現的,後來參考了一些站點編寫 HTA(HTML Component, 有空我會寫一些相關的文章)的實現,藉助於比較扭曲(我個人認爲)的方法可以大致的實現基於事件驅動的功能。大致的思路是這樣子的:
1 ) . 將所有的事件定義成屬性,只要簡單的聲明就可以
2 ) . 在需要觸發事件的代碼中判斷事件屬性是否是一個函數,如果是函數,直接執行函數代碼,如果是字符串,那麼執行字符串函數,通過 eval(str) 來執行。
3) . 在類的實例當中註冊事件函數。
爲了簡單說明如上的思路,採用 timer 這樣簡單的例子來表述如上的所提到的內容,如果只是爲了簡單的實現 timer 的功能, Javascript 中 setInterval 函數就可以滿足全部的要求,如下的代碼只是用來說明 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(){
}
實際工作代碼是在 TimerCallBack() 上面實現,事件觸發作爲屬性的方式來實現,在應用實例中,代碼提供了三種方法去調用事件,不過在事件的回調當中,我還沒有想到如何可以帶參數,只有才各自的實現當中訪問各自需要的屬性才能夠實現全部的要求。
2 。繼承。
剛採用了大篇幅的文字去介紹如何實現 Javascript 的各種實現,也就是從邏輯上完成了一個封裝 class 的實現,從某種意義上來說, class 的實現是真正腳本編程中使用最多的部分,不過如果只是要完成如上的功能,使用 VBScript 來編寫更能更加清晰,畢竟 VBscript 提供了 class 關鍵字,同時提供了 public 和 private 這兩個關鍵字,可以清晰的將公共和私有對象分離,至於事件的實現,也可以採用類似 Javascript 實現的思路,只是對於函數的引用需要採用 GetRef 這個函數,具體的用法可以參考 scripting reference,MSDN 裏頭也有詳細的介紹,而 Javascript 強大至於在於如下要說的了,雖然具體的東西可能不多。
如上所言,我們已經完成了一個基本的類實現 Timer ,現在要做的是重新編寫這個類,我們簡單的只是想在這個類之中加入一個方法,提供當前的系統時間,方法的名稱爲 getSystemDate, 顯然如果全部重新編寫,那就失去了我這裏說的意義了。先看看如下的實現。
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() ;
}
上述代碼實現了 NewTimer 類,從 Timer 繼承, Javascript 沒有使用 “ : ” 或者 java 的 public 那樣類似的關鍵字,只是通過 newclassname.prototype=new baseclass 這樣的方法來完成,同時 NewTimer 實現了 getSystemDate 的方法,在 NewTimer 的初始化函數中,我使用了 this.base=Timer ,是爲了引用父類的實現,不過在對於父類其他實現函數的調用,到現在我沒有找到一個確定的方法,是否通過 this.base.start() 那樣來調用還是其他的,如果有誰比較清楚的,麻煩告訴我,另外在 netscape 的站點上,我查到有一個特殊的 "__proto__" 的屬性好像是對於父類的直接引用,不過具體的我也沒有嘗試過,在 msdn 中也沒有看到對於 __proto__ 的支持。
3 。重載
或許這個是 OOP 編程中比較複雜的地方了,在 Javascript 的實現中有點無奈,也就是通過 prototype 的方式來完成的,不過因爲我不清楚如何調用父類的實現函數,那麼在重載中只能夠重新編寫所有的實現了,另外就是在實現中實例化一個父類,然後通過調用它來返回需要的東西。
Javascript 中所有的對象都是從 Object 繼承下來的, object 提供了 toString() 的方法,也就是說如果調用 alert(objInstance) 這樣的過程,實際上是調用了 alert(objInstance.toString()) 的方法,如果沒有編寫實現, object 默認的 toString() 都是 "object object" 這樣子的,在許多地方需要重載這個函數的,比如 Timer, 如果我們希望 var ins=new Timer(5);alert(ins) 調用得到的是 interval 的值 5 ,那麼就需要重新編寫 toString() 方法了
Timer.prototype.toString=function(){ return this.Interval};
以上代碼實現之後 alert(ins) 得到的就是 5 了。