The Microsoft AJAX Library adds a type system and extensions to JavaScript objects to provide frequently used object-oriented features that resemble features in the .NET Framework. They enable you to write AJAX-enabled ASP.NET applications in a structured way that improves maintainability, makes it easier to add features, and makes it easier to layer functionality. Microsoft AJAX Library extensions add the following capabilities to JavaScript:
Classes
Namespaces
Inheritance
Interfaces
Enumerations
Reflection
The library also provides helper functions for strings and arrays.
Classes, Members, and Namespaces
The Microsoft AJAX Library includes base classes, and objects and components that derive from them. Together, these classes enable you to use an object-oriented programming model for writing client script.
The Type class adds object-oriented features such as namespaces, classes, and inheritance to JavaScript programming. Any JavaScript object that is registered by using the Type class automatically has access to this functionality. The following example shows how to use the Type class to create and register a namespace and a class in a JavaScript file:
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();
Classes can have four kinds of members: fields, properties, methods, and events. Fields and properties are name/value pairs that describe characteristics of an instance of a class. Fields are composed of primitive types and are accessed directly, as in the following example:
myClassInstance.name="Fred"
Properties can represent any primitive or reference type. You access property values with get and set accessor methods. In the Microsoft AJAX Library, the get and set accessors are functions, which by convention use the prefix "get_" or "set_" in the function name. For example, to get or set a value for a property such as cancel, you call the get_cancel or set_cancel methods.
The Microsoft AJAX Library raises events in response to actions that occur during the life-cycle of an AJAX client application. The Microsoft AJAX Library also provides a standard way for you to create custom events for AJAX client components. For more information see Creating Custom Client Events and AJAX Client Life-Cycle Events.
The Microsoft AJAX Library provides a way to register namespaces so that you can group common functionality. The following example demonstrates how to add a Person class to the Demo namespace using the Type.registerNamespace and .registerClass methods.
To enable AJAX functionality for an ASP.NET Web page, you must add a ScriptManager control to the page. When the page is rendered, the appropriate script references to AJAX client-script libraries are generated automatically. The following example shows a page with a ScriptManager control.
<asp:ScriptManager runat="server" ID="scriptManager" />
The following example shows how to register the namespace, create the class, and then register the class.
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 >
<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', 'john.smith@example.com');
alert(testPerson.getFirstName() + " " +
testPerson.getLastName() );
return false;
}
</script>
</body>
</html>
Access Modifiers
Most object-oriented programming languages include the concept of access modifiers, which allow you to specify under what contexts a class or member is available, such as to outside programs, internal classes within the same namespace, or only within a specific code block. There are no access modifiers in JavaScript. However, the Microsoft AJAX Library follows the convention that members with names that start with the underscore character ("_") are considered private and not accessed outside the class that they are a part of.
Inheritance
Inheritance is the ability of one class to derive from another class. A derived class automatically inherits all the fields, properties, methods and events of the base class. A derived class can add new members or override existing members of the base class to change their behavior.
The following example contains two classes defined in script: Person and Employee, where Employee derives from Person. Both classes illustrate the use of private fields, and both have public properties and methods. In addition, Employee overrides the toString implementation of the Person class, and uses the base class functionality.
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', 'jane.doe@example.com');
}
function GetTestEmployee()
{
return new Demo.Employee('John', 'Doe', 'john.doe@example.com', '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>
Interfaces
An interface defines the input and output requirements of classes that implement it. This enables a function to interact with classes that implement the same interface regardless of what other functionality the class implements.
The following example defines a Tree base class and an IFruitTree interface. Apple and Banana, two derived classes, implement the IFruitTree interface, but the Pine class does not. Any class that implements the IFruitTree interface makes sure that a bearFruit method is a member of that class.
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>
Enumerations
An enumeration is a class that contains a set of named integer constants. You access the values like properties, as in the following example:
myObject.color = myColorEnum.red
Enumerations provide an easily readable alternative to integer representations. For more information about enumerations in the Microsoft AJAX Library, see Type.registerEnum Method (ASP.NET AJAX).
The following example defines an enumeration of named colors that represent hexadecimal values.
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>
Reflection
Reflection is the ability to examine the structure and components of a program at run time. The APIs that implement reflection are extensions of the Type class. These methods enable you to collect information about an object, such as what it inherits from, whether it implements a particular interface, and whether it is an instance of a particular class.
The following example uses reflection APIs to test the GreenApple class from the previous interface example.
<!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>