人們期待已久的asp.net ajax v1.0正式版終于發布了。現在你能用microsoft asp.net ajax的javascript很容易的寫出豐富的、交互式的web應用。尤其值得關注的是microsoft ajax library增加了面向對象的支持,而以前javascript是不支持面向對象開發的。現在icrosoft ajax library能很好的支持類、名字空間、繼承、接口、枚舉、反射等特征。這些新增加的功能類似于.net framework,這使得開發asp.net ajax應用變得容易維護,容易擴充。現在我們看看microsoft ajax library是如何支持以上特征的。
1.類、成員和名字空間
在microsoft ajax library中,所有的javascript類都繼承自object(類似于.net framework庫,都繼承自object),在asp.net ajax應用中你可以運用面向對象的編程模式創建繼承自microsoft ajax基類的對象和組件,類有四種成員:字段、屬性、方法、事件。字段和屬性是名/值對,用于描述一個類的一個實例的特性的。字段是由簡單類型構成且可直接訪問,例如:
myclassinstance.name="fred"。
屬性可以是任何簡單類型或引用類型,通過get和set方法訪問。在asp.net ajax中,get和set是獨立的函數,并規定在函數名中使用前綴"get_" 或 "set_" ,例如要獲取或設置cancel屬性的值時,你可以調用get_cancel或set_cancel方法。
一個方法是完成一個活動的函數而不是返回一個屬性的值。屬性和方法在下面的例子里都有示范。
事件指示特指的動作發生。當一個事件發生時,它可以調用一個或多個函數。事件所有者可以完成等待事件發生的任何任務。
名字空間是對關聯類的邏輯分組。名字空間使你可以對公共功能進行分組。
為了使asp.net web頁面具有asp.net ajax功能,你必須添加<asp:scriptmanager>控件到頁面上,當頁面啟動時,參照asp.net ajax庫的腳本自動產生。
下面的例子顯示了頁面使用了<asp:scriptmanager>控件。
<asp:scriptmanager runat="server" id="scriptmanager" />
下面的例子演示了如何使用type.registernamespace和.registerclass方法來把person類增加到demo名字空間中、創建類然后注冊類。
type.registernamespace("demo");
demo.person = function(firstname, lastname, emailaddress) {
this._firstname = firstname;
this._lastname = lastname;
this._emailaddress = emailaddress;
}
demo.person.prototype = {
getfirstname: function() {
return this._firstname;
},
getlastname: function() {
return this._lastname;
},
getname: function() {
return this._firstname + ' ' + this._lastname;
},
dispose: function() {
alert('bye ' + this.getname());
}
}
demo.person.registerclass('demo.person', null, sys.idisposable);
在腳本文件namespace.js中定義了類person,制定了類的名字空間為"demo"。運行頁面namespace.aspx,點擊按鈕將創建一個demo.person類的實例。
2.訪問修飾
許多面向對象編程語言都有訪問修飾的概念。允許你指定類或成員在某種范圍內有效。例如可在外部執行的程序、具有相同名字空間的內部類或特指的代碼快內的類等。在javascript中沒有訪問修飾,但在asp.net ajax中約定以下劃線字符開頭"_"的被認為是私有的,類的外部不能訪問。
3.繼承
繼承是一個類派生于另一個類的能力。派生類自動繼承基類的所有字段、屬性、方法和事件。派生類可以增加新的成員或者重寫基類已存在的成員來改變成員的行為。
下面的腳本實例有兩個類person和employee,employee從person繼承而來,兩個類示范了私有字段的使用,它們都有公共屬性、方法。另外employee類重寫了person類的tostring實現,并調用了基類的功能。
type.registernamespace("demo");
demo.person = function(firstname, lastname, emailaddress) {
this._firstname = firstname;
this._lastname = lastname;
this._emailaddress = emailaddress;
}
demo.person.prototype = {
getfirstname: function() {
return this._firstname;
},
getlastname: function() {
return this._lastname;
},
getemailaddress: function() {
return this._emailaddress;
},
setemailaddress: function(emailaddress) {
this._emailaddress = emailaddress;
},
getname: function() {
return this._firstname + ' ' + this._lastname;
},
dispose: function() {
alert('bye ' + this.getname());
},
sendmail: function() {
var emailaddress = this.getemailaddress();
if (emailaddress.indexof('@') < 0) {
emailaddress = emailaddress + '@example.com';
}
alert('sending mail to ' + emailaddress + ' ...');
},
tostring: function() {
return this.getname() + ' (' + this.getemailaddress() + ')';
}
}
demo.person.registerclass('demo.person', null, sys.idisposable);
demo.employee = function(firstname, lastname, emailaddress, team, title) {
demo.employee.initializebase(this, [firstname, lastname, emailaddress]);
this._team = team;
this._title = title;
}
demo.employee.prototype = {
getteam: function() {
return this._team;
},
setteam: function(team) {
this._team = team;
},
gettitle: function() {
return this._title;
},
settitle: function(title) {
this._title = title;
},
tostring: function() {
return demo.employee.callbasemethod(this, 'tostring') + '/r/n' + this.gettitle() + '/r/n' + this.getteam();
}
}
demo.employee.registerclass('demo.employee', demo.person);
inheritance.js腳本文件中定義了兩個類:person和employee,employee是從person繼承而來。每個類都有字段、公共屬性和方法。另外,employee類重寫了tostring的實現,并在重寫的代碼中調用了基類的功能。在這個例子中把類person的名字空間設定為"demo"。運行頁面inheritance.aspx,點擊“創建對象”、“對象釋放”、“公共和私有屬性”、“對象方法”、“重寫方法”,“對象類型檢查”體驗一下。
4.接口
接口是類要實現的邏輯協議,是對類進行集成的公共遵守的規范。它能使多個類和同一個接口把實現定義和類的具體實現結合起來。下面的例子定義了一個基類tree和接口ifruittree,apple和banana這兩個類實現了接口ifruittree,但pine類沒有實現接口ifruittree。
type.registernamespace("demo.trees");
demo.trees.ifruittree = function() {}
demo.trees.ifruittree.prototype = {
bearfruit: function(){}
}
demo.trees.ifruittree.registerinterface('demo.trees.ifruittree');
demo.trees.tree = function(name) {
this._name = name;
}
demo.trees.tree.prototype = {
returnname: function() {
return this._name;
},
tostringcustom: function() {
return this.returnname();
},
makeleaves: function() {}
}
demo.trees.tree.registerclass('demo.trees.tree');
demo.trees.fruittree = function(name, description) {
demo.trees.fruittree.initializebase(this, [name]);
this._description = description;
}
demo.trees.fruittree.prototype.bearfruit = function() {
return this._description;
}
demo.trees.fruittree.registerclass('demo.trees.fruittree', demo.trees.tree, demo.trees.ifruittree);
demo.trees.apple = function() {
demo.trees.apple.initializebase(this, ['apple', 'red and crunchy']);
}
demo.trees.apple.prototype = {
makeleaves: function() {
alert('medium-sized and desiduous');
},
tostringcustom: function() {
return 'fruittree ' + demo.trees.apple.callbasemethod(this, 'tostringcustom');
}
}
demo.trees.apple.registerclass('demo.trees.apple', demo.trees.fruittree);
demo.trees.grannysmith = function() {
demo.trees.grannysmith.initializebase(this);
// you must set the _description feild after initializebase
// or you will get the base value.
this._description = 'green and sour';
}
demo.trees.grannysmith.prototype.tostringcustom = function() {
return demo.trees.grannysmith.callbasemethod(this, 'tostringcustom') + ' ... its grannysmith!';
}
demo.trees.grannysmith.registerclass('demo.trees.grannysmith', demo.trees.apple);
demo.trees.banana = function(description) {
demo.trees.banana.initializebase(this, ['banana', 'yellow and squishy']);
}
demo.trees.banana.prototype.makeleaves = function() {
alert('big and green');
}
demo.trees.banana.registerclass('demo.trees.banana', demo.trees.fruittree);
demo.trees.pine = function() {
demo.trees.pine.initializebase(this, ['pine']);
}
demo.trees.pine.prototype.makeleaves = function() {
alert('needles in clusters');
}
demo.trees.pine.registerclass('demo.trees.pine', demo.trees.tree);
interface.js腳本文件中定義了一個tree基類和一個ifruittree接口。apple和banana兩個繼承類實現了ifruittree接口,而pine類沒有實現ifruittree接口。運行interface.aspx,點擊“對象創建”、“接口檢查”、“調用接口方法”體驗一下。
5.枚舉
枚舉是包含一組被命名的正整數常數的類。你可以像訪問屬性一樣訪問它的值。例如:
myobject.color = mycolorenum.red,枚舉提供了一種很容易理解的整數表示。下面的例子定義了一個以十六進制數表示的顏色被命名為有意義的名字的枚舉類型。
type.registernamespace("demo");
// define an enumeration type and register it.
demo.color = function(){};
demo.color.prototype =
{
red: 0xff0000,
blue: 0x0000ff,
green: 0x00ff00,
white: 0xffffff
}
demo.color.registerenum("demo.color");
運行enumeration.aspx,選擇下拉框中的顏色,腳本程序把背景色設為選中的枚舉類型demo.color中的顏色。
6.反射
反射用于檢查一個運行期程序的結構和組成,是通過類type的api來實現反射的,這些方法使你能夠收集一個對象的信息,例如:它是繼承于哪個類、它是否實現類某個特指的接口、它是否是某個類的實例等。
下面的例子用反射的api測試grannysmith類是否實現了前面的接口。運行reflection.aspx,點擊“檢查類型”、“檢查繼承”、“檢查接口”體驗一下。
另外需要說明的一點是,microsoft ajax library是基于開放體系架構而開發的,不僅僅用于asp.net,還能用于其它體系架構中,例如用在java中。