隨著AJAX和RIA技術的發展,JavaScript被廣泛的使用,並在開發中發揮著越來越重要的作用。JavaScript提供瞭特有的類機制,但是在語法習慣上與傳統面向對象的語言有很大的不同,這使得不少的JavaScript開發人員感到比較迷惑,而dojo作為功能強大的JavaScript類庫,有功能完整的類機制實現。本文將通過實例介紹dojo的類機制,這是dojo提供的一種強大和靈活的功能,其dijit UI組件框架也是以此為基礎實現的。
1. 使用dojo定義類
聲明dojo類是通過dojo.declare()方法來實現的,如我們想要定義一個名為com.levinzhang.Person的類,該類有name、age屬性和getName、getAge方法:
Js代碼
dojo.declare("com.levinzhang.Person", null,{
name:null,
age:null,
constructor: function(name,age){
this.name = name;
this.age = age;
},
getName:function(){
return this.name;
},
getAge:function(){
return this.age;
}
});
除瞭前面提到的屬性的和方法以外,在代碼中我們還定義瞭一個名為constructor的方法,這個方法在dojo的類機制中至關重要,當我們實例化該類得到對象的時候,該方法將會被調用,從而完成初始化的操作。
dojo的declare接受三個參數,分別為類的名稱、要繼承的父類以及該類的屬性和方法。實例化類的語法也很簡潔,與實例化普通的JavaScript類並無分別:
Js代碼
var person = new com.levinzhang.Person("levinzhang",30);
alert(person.getName());//將會提示出levinzhang
2. 實現靜態變量
在常見的面向對象語言中,經常會使用到類層次的靜態變量,而通過dojo定義的類也能實現靜態變量的需求,不過靜態變量僅限於數組和對象類型。
Js代碼
staticInfo:{count:0},
constructor: function(name,age){
this.name = name;
this.age = age;
++this.staticInfo.count;
}
如上所示,如果定義瞭數組和對象,而沒有在構造方法中進行修改的話,這個對象將會成為該類的靜態屬性,測試代碼如下:
Js代碼
var person = new com.levinzhang.Person("levinzhang",30);
alert(person.staticInfo.count);//此時將會提示出1
var person2 = new com.levinzhang.Person("levin",30);
alert(person2.staticInfo.count);//此時將會提示出2
需要註意的兩點是:1)對於原始類型的變量如數字、佈爾值和字符串,dojo的類機制並沒有提供實現靜態屬性的功能;2)如果定義的數組或對象屬性在constructor方法中被重新賦值,那麼該屬性將不再是靜態屬性,而是每個實例化對象都持有一份屬於自己的備份瞭。
3. 使用dojo實現繼承
在JavaScript中沒有直接實現繼承的關鍵字,因此關於繼承有多種的實現方式,代表性的是類式繼承和原型式繼承,但是不管哪種繼承方式都需要開發人員對JavaScript語言本身有著很深厚的瞭解。dojo對JavaScript的繼承機制進行瞭很好的封裝,可以實現功能強大的類定義,我們將對一些常見的功能進行介紹。
dojo.declare方法中的第二個參數,是指明要繼承的父類的,該參數可以為null(要定義的類沒有父類)、單個類(要定義的類繼承自一個父類)或數組(要定義的類繼承自多個父類)。
1) 單繼承
我們要定義一個名為com.levinzhang.Employee 的類,繼承自com.levinzhang.Person,並要添加名為workExperience的屬性、重寫getName方法等功能:
Js代碼
dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{
workExperience:null,
constructor: function(name,age,workExperience){
this.workExperience = workExperience;
},
getWorkExperience:function(){
return this.workExperience;
},
getName:function(){
return "Employee:"+this.name;
},
getInput:function(){
return 5000;
}
});
在以上的代碼中,我們定義的com.levinzhang.Employee繼承瞭com.levinzhang.Person並添加瞭自定義的方法。測試代碼如下:
Js代碼
var employee = new com.levinzhang.Employee("levin",30,4);
alert(employee.getName());//將提示出Employee:levin
alert(employee.getWorkExperience());//將提示出4
alert(employee.getAge());//將提示出30
可以看到在Employee的實例中,我們能夠調用父類中定義的方法。而在類的constructor初始化方法中,我們並沒有調用父類相關的方法,但是我們成功初始化瞭name和age兩個屬性,這是因為dojo會自動調用父類的初始化方法,完成繼承要求的相關初始化工作。
2) 多繼承
dojo支持多繼承的功能, dojo實現瞭被Python和很多支持多繼承語言使用的C3算法。使用dojo的多繼承功能時,需要註意的是:隻有數組中的第一個元素會作為真正的父類,而其它的元素則是通過mixin的方式進行屬性添加以構建原型鏈的。
如我們需要定義一個類來表示公司中的股票持有者(com.levinzhang.Shareholder),而公司中的員工可能也會持有股票,於是我們定義一個名為com.levinzhang.ShareholderEmployee的類繼承自com.levinzhang.Shareholder和com.levinzhang.Employee。
Js代碼
dojo.declare("com.levinzhang.Shareholder", com.levinzhang.Person,{
share:null,
constructor: function(args){
this.share = args.share;
},
getShare:function(){
return this.share;
}
});
dojo.declare("com.levinzhang.ShareholderEmployee", [com.levinzhang.Employee,com.levinzhang.Shareholder],{
getInfo:function(){
alert("I'm an Employee with stock.My share is "+this.getShare()+"."+"My name is "+this.getName()+".");
}
});
在以上的代碼中,我們調整瞭原有的初始化傳入參數的格式,由傳入多個參數改為傳入一個簡單JavaScript字面量對象的方式(原有的代碼也要稍作調整),並通過多繼承的方式實現瞭一個類用來描述持有股票的員工。測試代碼如下:
Js代碼
var shareholderEmployee = new com.levinzhang.ShareholderEmployee({name:"levin",age:30,workExperience:4,share:300});
shareholderEmployee.getInfo();
//將會提示出“I'm an Employee with stock.My share is 300. My name is Employee:levin.”
關於dojo多繼承的更多話題,請參考dojo的文檔資料。
3) 調用父類的方法
在編程中,我們經常會遇到在子類的某個方法中需要調用父類的方法來協作完成功能。如我們定義名為com.levinzhang.Manager的類,該類繼承自com.levinzhang.Employee類,並重寫getInput方法,Manager的收入分為兩部分,一部分是與com.levinzhang.Employee相同的固定收入,另一部分是與管理經驗相關的其它收入,這樣在定義Manager的時候,就需要調用父類的方法,實現方式如下:
Js代碼
dojo.declare("com.levinzhang.Manager", com.levinzhang.Employee,{
manageExperience:null,
constructor: function(args){
this.manageExperience = args.manageExperience;
},
getInput:function(){
var fromBase = this.inherited(arguments);
return fromBase+1000*this.manageExperience;
}
});
從以上代碼可以看到,通過inherited方法的使用,使得Manager可以調用父類的方法。測試代碼如下:
Js代碼
var manager = new com.levinzhang.Manager({name:"levin",age:30,workExperience:4,manageExperience:2});
alert(manager.getInput());//7000
在以上的測試代碼中,getInput的返回值為7000,說明該值為子類和父類方法共同確定的。
除瞭使用inherited來調用父類中的方法以外,從dojo的1.4版本開始提供瞭鏈式調用父類方法的功能,能夠通過設置自動調用父類的方法,並且支持類似於AOP的before或after配置(dojo正在開發中的1.7版本,提供瞭更為豐富的AOP功能,我們將會持續關註)。
4. Dojo類機制的其它功能
除瞭以上介紹的類定義相關功能以外,dojo還提供瞭許多的便利的工具類供使用。
dojo類所生成對象具有一些特有的屬性和方法,常見的如isInstanceOf方法和declaredClass屬性,isInstanceOf方法判定對象是否為某個類的實例,而declaredClass屬性則能夠表明該對象的聲明類是什麼。如:
Js代碼
var manager = new com.levinzhang.Manager({name:"levin",age:30,workExperience:4,manageExperience:2});
alert(manager.isInstanceOf(com.levinzhang.Employee));//提示為true
alert(manager.isInstanceOf(com.levinzhang.Person));//提示為true
alert(manager.declaredClass);//提示為“com.levinzhang.Manager”
類機制還涉及到的包管理等功能,限於篇幅,不再展開敘述,感興趣的讀者可以參考dojo的在線文檔或圖書。
5. 小結
JavaScript本身的類機制比較復雜,對開發人員有著較高的要求,而dojo提供瞭功能強大的類功能,有些降低瞭開發的難度。本文簡單介紹瞭dojo類機制的基本功能,包括類定義、繼承、靜態屬性等,這是dojo最基礎也是最核心的內容之一,dojo的許多高級功能都是基於此來實現的,因此瞭解這部分功能的使用方式甚至源碼實現對於整體把握dojo框架都大有裨益。
作者“山東響馬”