當您要執行下列操作時,可以使用 Microsoft AJAX Library 的功能:
·向 JavaScript 代碼中添加面向對象的功能,以提高代碼的重用性、靈活性和可維護性。
·使用反射在運行時檢查客戶端腳本的結構和組件。
·使用枚舉提供不同於整數的另一種易讀的表示形式。
·使用 JavaScript 基類型的擴展縮短常規腳本任務的開發時間。
·使用調試擴展和跟蹤功能,實現比傳統 JavaScript 調試技術更快、信息更豐富的調試。
一、使用類型系統
Microsoft AJAX Library 增加了一個類型系統以及一系列對 JavaScript 對象的擴展,可提供與 .NET Framework 功能類似的面向對象的常用功能。利用這些功能,可按一種結構化方式編寫支持 AJAX 的 ASP.NET 應用程序,這不僅能提高可維護性,還簡化了添加功能以及對功能分層的操作。Microsoft AJAX Library 擴展爲 JavaScript 添加了下列功能:
類、命名空間、繼承、接口、枚舉、反射
該庫還提供了針對字符串和數組的 Helper 函數。
1.1、類、成員和命名空間
Microsoft AJAX Library 包括基類及其派生的對象和組件。通過所有這些類,您可以使用面向對象的編程模型來編寫客戶端腳本。
Type 類爲 JavaScript 編程添加了命名空間、類和繼承等面向對象的功能。任何使用 Type 類註冊的 JavaScript 對象都會自動獲得訪問此功能的權限。下面的示例演示如何使用 Type 類在 JavaScript 文件中創建並註冊一個命名空間和類:
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);
// Notify ScriptManager that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
類有四種成員:字段、屬性、方法和事件。字段和屬性是名稱/值對,用於描述類實例的特徵。字段由基元類型組成,可直接進行訪問,如下面的示例所示:
myClassInstance.name="Fred"
屬性可以表示任何基元類型或引用類型。屬性值需通過 get 和 set 訪問器方法進行訪問。在 Microsoft AJAX Library 中,get 和 set 訪問器都是函數。按照約定,這些函數的名稱中應使用前綴“get_”或“set_”。例如,若要獲取或設置屬性 cancel 的值,需要調用 get_cancel 或 set_cancel 方法。
對於在 AJAX 客戶端應用程序生命週期中發生的操作,Microsoft AJAX Library 將引發相應的事件進行響應。Microsoft AJAX Library 還提供一種爲 AJAX 客戶端組件創建自定義事件的標準方式。
Microsoft AJAX Library 提供一種有助於對常用功能進行分組的命名空間註冊方式。下面的示例演示如何使用 Type.registerNamespace 和 .registerClass 方法向 Demo 命名空間中添加 Person 類。
若要對 ASP.NET 網頁啓用 AJAX 功能,必須向該頁面添加 ScriptManager 控件。呈現該頁面時,將自動生成對 AJAX 客戶端腳本庫的相應腳本引用。下面的示例演示一個包含 ScriptManager 控件的頁面。
<asp:ScriptManager runat="server" ID="scriptManager" />
下面的示例演示如何完成以下過程:註冊命名空間,創建類,然後重新註冊該類。
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);
// Notify ScriptManager that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
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);
// Notify ScriptManager that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
1.2、訪問修飾符
大多數面向對象的編程語言都包括“訪問修飾符”這一概念。通過訪問修飾符,可以指定類或成員可用的上下文,例如是對外部程序可用,還是對同一命名空間中的內部類可用,抑或是僅在特定的代碼塊中可用。JavaScript 中沒有訪問修飾符。但是,Microsoft AJAX Library 遵循以下約定:名稱以下劃線字符(“_”)開頭的成員視爲私有成員,不能從成員所屬類的外部訪問它們。
1.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);
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head>
<title>Inheritance</title>
</head>
<body>
<form id="Main" runat="server">
<asp:ScriptManager runat="server" ID="scriptManager" />
<script type="text/javascript" src="Inheritance.js"></script>
</form>
<h2>Inheritance</h2>
<p />
<div>
This file contains two classes defined in script: Person and Employee, where
Employee derives from Person.
<p />
Each class has private fields, and public properties and methods. In addition,
Employee overrides the toString implementation, and in doing so, it uses the
base class functionality.
<p />
This example puts the Person class in the "Demo" namespace.
<p />
</div>
<div>
<ul>
<li><a href="#" οnclick="return OnTestNewClick()">Object Creation</a></li>
<li><a href="#" οnclick="return OnTestDisposeClick()">Object Dispose</a></li>
<li><a href="#" οnclick="return OnTestPrivatePropertyClick()">Public vs. Private Properties</a></li>
<li><a href="#" οnclick="return OnTestInstanceMethodClick()">Instance Methods</a></li>
<li><a href="#" οnclick="return OnTestOverrideMethodClick()">Overriden Methods</a></li>
<li><a href="#" οnclick="return OnTestInstanceOfClick()">Instance Of Check</a></li>
</ul>
</div>
<script type="text/javascript" language="JavaScript">
function GetTestPerson()
{
return new Demo.Person('Jane', 'Doe', '[email protected]');
}
function GetTestEmployee()
{
return new Demo.Employee('John', 'Doe', '[email protected]', 'Platform', 'Programmer');
}
function OnTestNewClick() {
var aPerson = GetTestPerson();
alert(aPerson.getFirstName());
alert(aPerson);
alert(Object.getType(aPerson).getName());
var testPerson = GetTestPerson();
alert(testPerson.getFirstName());
alert(testPerson);
return false;
}
function OnTestDisposeClick() {
var aPerson = GetTestEmployee();
alert(aPerson.getFirstName());
aPerson.dispose();
}
function OnTestPrivatePropertyClick() {
var aPerson = GetTestEmployee();
alert('aPerson._firstName = ' + aPerson._firstName);
alert('aPersona.getFirstName() = ' + aPerson.getFirstName());
return false;
}
function OnTestInstanceMethodClick() {
var aPerson = GetTestEmployee();
aPerson.sendMail('Hello', 'This is a test mail.');
return false;
}
function OnTestOverrideMethodClick() {
var testPerson = GetTestEmployee();
alert(testPerson);
return false;
}
function OnTestInstanceOfClick() {
var aPerson = GetTestEmployee();
if (Demo.Employee.isInstanceOfType(aPerson)) {
alert(aPerson.getName() + ' is an Employee instance./r/nTitle property: ' + aPerson.getTitle());
}
return false;
}
</script>
</body>
</html>
1.4、接口
接口用於定義實現它的類的輸入和輸出要求。這樣,函數可以和實現同一接口的類進行交互,而不用考慮該類還實現哪些其他功能。
下面的示例定義一個 Tree 基類和一個 IFruitTree 接口。兩個派生類 Apple 和 Banana 可實現 IFruitTree 接口,但 Pine 類不實現該接口。實現 IFruitTree 接口的任何類都可確保 bearFruit 方法是該類的成員。
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.GreenApple = function() {
Demo.Trees.GreenApple.initializeBase(this);
// You must set the _description feild after initializeBase
// or you will get the base value.
this._description = 'green and sour';
}
Demo.Trees.GreenApple.prototype.toStringCustom = function() {
return Demo.Trees.GreenApple.callBaseMethod(this, 'toStringCustom') + ' ... its GreenApple!';
}
Demo.Trees.GreenApple.registerClass('Demo.Trees.GreenApple', 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);
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head>
<title>Interface</title>
</head>
<body>
<form id="Main" runat="server">
<asp:ScriptManager runat="server" ID="scriptManager" />
</form>
<h2>Interface</h2>
<p />
<div>
This file contains a Tree base class, and an IFruitTree interface.
Apple and Banana, two derived classes implement that interface, whereas,
Pine does not implement that interface.
<p />
</div>
<script type="text/javascript" src="Interface.js"></script>
<div>
<ul>
<li><a href="#" οnclick="return OnTestNewClick()">Object Creation</a></li>
<li><a href="#" οnclick="return OnTestImplementsClick()">Implements Check</a></li>
<li><a href="#" οnclick="return OnTestInterfaceMethodClick()">Call interface method</a></li>
</ul>
</div>
<script type="text/javascript" language="JavaScript">
function OnTestNewClick() {
var apple = new Demo.Trees.Apple('Apple');
alert(apple.returnName());
apple.makeLeaves();
return false;
}
function OnTestImplementsClick() {
var apple = new Demo.Trees.Apple();
if (Demo.Trees.IFruitTree.isImplementedBy(apple)) {
alert('Apple implements IFruitTree');
}
else {
alert('Apple does not implement IFruitTree');
}
var pine = new Demo.Trees.Pine();
if (Demo.Trees.IFruitTree.isImplementedBy(pine)) {
alert('Pine implements IFruitTree');
}
else {
alert('Pine does not implement IFruitTree');
}
return false;
}
function OnTestInterfaceMethodClick() {
var apple = new Demo.Trees.Apple();
ProcessTree(apple);
var pine = new Demo.Trees.Pine();
ProcessTree(pine);
var banana = new Demo.Trees.Banana();
ProcessTree(banana);
var g = new Demo.Trees.GreenApple();
ProcessTree(g);
return false;
}
function ProcessTree(tree) {
alert('Current Tree ' + tree.returnName());
alert(tree.toStringCustom());
if (Demo.Trees.IFruitTree.isImplementedBy(tree)) {
alert(tree.returnName() + ' implements IFruitTree; Fruit is ' + tree.bearFruit());
}
}
</script>
</body>
</html>
1.5、枚舉
枚舉是指包含一組命名整數常量的類。您可以像訪問屬性那樣訪問這些值,如下面的示例所示:
myObject.color = myColorEnum.red
枚舉提供不同於整數的另一種易讀的表示形式。有關 Microsoft AJAX Library 中的枚舉的更多信息,請參見 Type.registerEnum 方法 (ASP.NET AJAX)。
下面的示例定義一個命名顏色的枚舉,這些命名顏色用於表示十六進制的值。
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");
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head>
<title>Enumeration</title>
</head>
<body>
<form id="Main" runat="server">
<asp:ScriptManager runat="server" ID="scriptManager" />
</form>
<div>
<p>This example creates an Enumeration of colors
and applies them to page background.</p>
<select id="ColorPicker"
οnchange="ChangeColor(options[selectedIndex].value)">
<option value="Red" label="Red" />
<option value="Blue" label="Blue" />
<option value="Green" label="Green" />
<option value="White" label="White" />
</select>
</div>
<script type="text/javascript" src="Enumeration.js"></script>
<script type="text/javascript" language="JavaScript">
function ChangeColor(value)
{
document.body.bgColor = eval("Demo.Color." + value + ";");
}
</script>
</body>
</html>
1.6、反射
反射是指在運行時檢查程序的結構和組件的能力。實現反射的 API 是對 Type 類的擴展。通過這些方法,可以收集有關對象的信息,例如該對象繼承自誰,它是否實現特定的接口,以及它是否是特定類的實例等。
下面的示例使用反射 API 對前面接口示例中的 GreenApple 類進行測試。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head>
<title>Reflection</title>
</head>
<body>
<form id="Main" runat="server">
<asp:ScriptManager runat="server" ID="scriptManager" />
</form>
<div>
<p>This example tests the Demo.Trees.GreenApple class
against various reflection APIs.</p>
<input id="Button1" value="Check Type"
type="button" οnclick="return OnButton1Click()" />
<input id="Button2" value="Check Inheritance"
type="button" οnclick="return OnButton2Click()" />
<input id="Button3" value="Check Interface"
type="button" οnclick="return OnButton3Click()" />
</div>
<script type="text/javascript" src="Interface.js"></script>
<script type="text/javascript" language="JavaScript">
var g = new Demo.Trees.GreenApple();
var gt = Demo.Trees.GreenApple;
var a = new Array(
Demo.Trees.Apple,
Demo.Trees.Tree,
Demo.Trees.Pine,
Demo.Trees.IFruitTree,
Sys.IContainer);
function OnButton1Click()
{
for (var i = 0; i < a.length; i ++)
{
if (a[i].isInstanceOfType(g))
{
alert(gt.getName() + " is a " + a[i].getName() + ".");
}
else alert(gt.getName() + " is not a " + a[i].getName() + ".");
}
}
function OnButton2Click()
{
for (var i = 0; i < a.length; i ++)
{
if (gt.inheritsFrom(a[i]))
{
alert(gt.getName() + " inherits from " + a[i].getName() + ".");
}
else alert(gt.getName() + " does not inherit from " + a[i].getName() + ".");
}
}
function OnButton3Click()
{
for (var i = 0; i < a.length; i ++)
{
if (Type.isInterface(a[i]))
{
if (gt.implementsInterface(a[i]))
{
alert(gt.getName() + " implements the " + a[i].getName() + " interface.");
}
else alert(gt.getName() + " does not implement the " + a[i].getName() + " interface.");
}
else alert(a[i].getName() + " is not an interface.");
}
}
</script>
</body>
</html>
二、使用 JavaScript 基類型的擴展
JavaScript 基類型的擴展可爲這些類型提供附加功能。
·Array 類型擴展
·Boolean 類型擴展
·Date 類型擴展
·Error 類型擴展
·Number 類型擴展
·Object 類型擴展
·String 類型擴展
Sys.Debug 類可提供豐富的調試功能。
如果基於 Microsoft AJAX Library 創建組件,則可以創建由 ScriptManager 控件自動管理的腳本文件的調試版本和發行版本。通過在腳本文件名中添加“.debug”部分,可以標識腳本文件的調試版本。例如,下面的腳本文件名標識同一文件的零售版本和調試版本:
·MyScript.js(零售版)
·MyScript.debug.js(調試版)
三、將客戶端腳本集成到 ASP.NET Web 應用程序中
任何 ASP.NET 網頁均可以通過在 <script> 塊中引用腳本文件來對其進行訪問,如下面的示例所示:
<script type="text/javascript" src="MyScript.js"></script>
但是,以此方式調用的腳本將不能參與部分頁呈現,或無法訪問 Microsoft AJAX Library 的某些組件。若要使腳本文件可在支持 AJAX 的 ASP.NET Web 應用程序中用於部分頁呈現,必須在該頁面的 ScriptManager 控件中註冊該腳本。若要註冊腳本文件,請創建一個指向相關文件的 ScriptReference 對象,並使之將該文件添加到 Scripts 集合中。下面的示例演示如何在標記中執行此操作:
<asp:ScriptManager ID="SMgr" runat="server">
<Scripts>
<asp:ScriptReference path="MyScript.js" />
</Scripts>
</asp:ScriptManager>
若要使腳本文件得到 ScriptManager 控件的正確處理,每個文件都必須在末尾包含對 Sys.Application.notifyScriptLoaded 方法的調用。此調用可通知應用程序,已完成文件加載。下面的示例演示用於實現此目的的代碼:
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
此外,您還可以將 .js 文件作爲資源嵌入在託管代碼程序集中(如果創建的 ASP.NET 服務器控件在客戶端腳本中實現其 AJAX 功能,則可能需要這樣做)。將腳本嵌入在程序集中時,腳本中便無需包括通知語句。此外,您也不必在腳本引用中指定 path 屬性。但是,必須提供不帶文件擴展名的程序集名稱,如下面的示例所示:
<asp:ScriptManager ID="SMgr" runat="server">
<Scripts>
<asp:ScriptReference
Name="MyScript.js" Assembly="MyScriptAssembly"/>
</Scripts>
</asp:ScriptManager>
此外,通過在代碼中創建腳本引用並將它們添加到 Scripts 集合中,還可以用編程方式註冊腳本。有關更多信息,請參見 動態分配腳本引用。
使用 ScriptManager 控件的註冊方法,可以註冊部分頁更新所需的腳本。您可以按下列方式使用這些方法:
·若要在代碼中生成客戶端腳本,請以字符串形式生成一個腳本塊,然後將其傳遞給 RegisterClientScriptBlock 方法。
·若要添加沒有 Microsoft AJAX Library 依賴項的獨立腳本文件,請使用 RegisterClientScriptInclude 方法。
·若要添加嵌入在程序集中的腳本文件,請使用 RegisterClientScriptInclude 方法。
任何要註冊的腳本塊或內聯腳本都必須位於頁面的 <form> 元素中。否則,該腳本將不能在 ScriptManager 控件中註冊,從而無法訪問 ASP.NET AJAX 功能。