ASP.NET 中的 AJAX 功能有助于创建客户端脚本并将其集成到 ASP.NET 应用程序中。 这包括 ECMAScript (JavaScript) 的类型系统以及为现有 ECMAScript (JavaScript) 对象提供丰富的 .NET Framework 类的扩展。 ASP.NET 还包括 ScriptManager 控件,此控件可用于管理这些脚本库以及应用程序中的任何自定义脚本。
方案
当您要执行下列操作时,可以使用 Visual Studio 2010 和 Microsoft Visual Web Developer 速成版中的 Microsoft AJAX Library 的功能:
向 JavaScript 代码中添加面向对象的功能,以提高代码的重用性、灵活性和可维护性。
使用反射在运行时检查客户端脚本的结构和组件。
使用枚举提供不同于整数的另一种易读的表示形式。
使用 JavaScript 基类型的扩展缩短常规脚本任务的开发时间。
使用调试扩展和跟踪功能,实现比传统 JavaScript 调试技术更快、信息更丰富的调试。
使用类型系统
Microsoft AJAX Library 增加了一个类型系统以及一系列对 JavaScript 对象的扩展,可提供与 .NET Framework 功能类似的面向对象的常用功能。 利用这些功能,可按一种结构化方式编写支持 AJAX 的 ASP.NET 应用程序,这不仅能提高可维护性,还简化了添加功能以及对功能分层的操作。Microsoft AJAX Library 扩展为 JavaScript 添加了下列功能:
类
命名空间
继承
接口
枚举
反射
该库还提供了针对字符串和数组的 Helper 函数。
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 客户端组件创建自定义事件的标准方式。 有关更多信息,请参见创建自定义客户端事件和 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();
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Namespace</title> </head> <body> <form id="Main" runat="server"> <asp:ScriptManager runat="server" ID="scriptManager" /> </form> <div> <p>This example creates an instance of the Person class and puts it in the "Demo" namespace.</p> <input id="Button1" value="Create Demo.Person" type="button" onclick="return OnButton1Click()" /> </div> <script type="text/javascript" src="Namespace.js"></script> <script type="text/javascript" language="JavaScript"> function OnButton1Click() { var testPerson = new Demo.Person( 'John', 'Smith', '[email protected]'); alert(testPerson.getFirstName() + " " + testPerson.getLastName() ); return false; } </script> </body> </html>
访问修饰符
大多数面向对象的编程语言都包括“访问修饰符”这一概念。通过访问修饰符,可以指定类或成员可用的上下文,例如是对外部程序可用,还是对同一命名空间中的内部类可用,抑或是仅在特定的代码块中可用。 JavaScript 中没有访问修饰符。 但是,Microsoft AJAX Library 遵循以下约定:名称以下划线字符(“_”)开头的成员视为私有成员,不能从成员所属类的外部访问它们。
继承
继承是指一个类从另一个类派生的能力。 派生类可自动继承基类的所有字段、属性、方法和事件。 派生类可以添加新成员或者重写基类的现有成员,以更改这些成员的行为。
下面的示例包含两个在脚本中定义的类: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 xmlns="http://www.w3.org/1999/xhtml"> <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>接口
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 xmlns="http://www.w3.org/1999/xhtml"> <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>