当您要执行下列操作时,可以使用 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="#" onclick="return OnTestNewClick()">Object Creation</a></li>
<li><a href="#" onclick="return OnTestDisposeClick()">Object Dispose</a></li>
<li><a href="#" onclick="return OnTestPrivatePropertyClick()">Public vs. Private Properties</a></li>
<li><a href="#" onclick="return OnTestInstanceMethodClick()">Instance Methods</a></li>
<li><a href="#" onclick="return OnTestOverrideMethodClick()">Overriden Methods</a></li>
<li><a href="#" onclick="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="#" onclick="return OnTestNewClick()">Object Creation</a></li>
<li><a href="#" onclick="return OnTestImplementsClick()">Implements Check</a></li>
<li><a href="#" onclick="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"
onchange="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" onclick="return OnButton1Click()" />
<input id="Button2" value="Check Inheritance"
type="button" onclick="return OnButton2Click()" />
<input id="Button3" value="Check Interface"
type="button" onclick="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 功能。