In this chapter:
Established in 1995 and initially integrated into the Netscape Navigator browser, JavaScript was capable of validating web-page user input without refreshing. Microsoft later packaged its own version, JScript, in Internet Explorer 3.0. In the following years, the language evolved, and developers began using it in conjunction with the Document Object Model (DOM) for Dynamic HTML pages. In 1998, the language was standardized as ECMAScript to facilitate the release of different versions.
Some JavaScript characteristics (weak typing, prototyping, the use of first-class functions), although typical for functional languages, may scare a programmer who works mostly with an object-oriented language like Java or C#. On the other hand, JavaScript has been used effectively to write complex and extraordinary client controls. You’ll soon discover why JavaScript is now the language of choice among Ajax developers.
This chapter will explain how the Microsoft Ajax Library enhances JavaScript’s object-oriented model. In the first section, we’ll review some of the language’s most important concepts, focusing on objects and functions. After an overview of the JSON data format, we’ll study Microsoft Ajax Library’s enhanced type system. By the end of the chapter, you’ll be able to write object-oriented code using JavaScript, perform reflection, and expose events on client objects.
JavaScript is a true object-oriented language. The notion of an object in JavaScript is different from that in object-oriented languages such as C# and VB.NET. Objects in JavaScript aren’t instances of classes, because JavaScript doesn’t support the notion of a class. Instead, you obtain the structure—or template—of an object by manipulating a special, native object called the prototype, which you’ll encounter in section 3.1.5.
A JavaScript object is nothing more than a collection of name and value pairs called properties. Usually, this kind of structure is also called a dictionary or an associative array. JavaScript provides also an array data type, which is a collection of values of different types. A major role in the language is played by functions, which are objects like dictionaries or arrays. Functions are probably the most powerful objects provided by JavaScript; they’re responsible for its great power and flexibility. In the following sections, we’ll do an overview of objects, arrays, and functions, which are the foundations of the JavaScript language.
To create a generic object, you can take two different approaches. First, you can use the new operator in conjunction with the Object type, as in the following code:
var book = new Object();
As you’ll discover in section 3.1.4, the new operator is useful when you want to create custom objects using a function as a constructor. In the previous statement, the new operator is syntactic sugar, because an object is usually created with an object literal, like so:
var book = {};
The object literal {} represents a sort of empty object. We’ll discuss the various JavaScript literals in detail in section 3.1.7. Once you’ve created an object, you can take advantage of an important characteristic of the language: You can expand objects at any time by adding new properties to them. This is different from what happens in class-based languages such as C# and VB.NET, where the structure of an object is specified in a class and instances can’t be modified at runtime.
To add a property to an object, you need to access it and assign a value. You can do this anywhere in the code, at any time. If the property doesn’t exist, it’s automatically created at runtime. The following code shows how to add a property called title to the book object you created before:
book.title = 'ASP.NET AJAX In Action';
As we said previously, objects in JavaScript are collections of name and value pairs. To demonstrate, you can add a property to an object using an indexed notation, as if you were accessing a collection such as an array or a hash table. The following is another way to add a title property to the book object:
book['title'] = 'ASP.NET AJAX In Action';
You can loop over an object’s properties by using the for-in construct. This construct is similar to the foreach construct of C#. For example, the following code displays the name and values of the properties of the book object:
for(var property in book) { alert('Name: ' + property + ', Value: ' + book[property]); }
In this code snippet, the property variable holds, at each iteration, a string with the name of a property of the object. As result, book[property] returns the value of that property. The name and value of each property are displayed using the JavaScript’s alert function, which displays a string in a message box on screen.
Values assigned to properties, such as strings, numbers, or Boolean values, expose properties and methods that can be accessed at runtime. For example, the value of the title property of the book object is of type String. It exposes methods such as toUpperCase, which converts a string to uppercase:
var stringToUpper = book.title.toUpperCase(); alert(stringToUpper);
Similar properties are exposed by the built-in Number and Boolean types, which represent numbers and Boolean values, respectively.
Next, we’ll examine one of the most used data structures: arrays.
You can find a quick reference to the JavaScript data types and their properties and methods at http://www.w3schools.com/jsref/default.asp.
Another fundamental and widely used data type is the array. In JavaScript, an array is an ordered collection of values that can be of different types. You access values by specifying their position in the array using classic indexed notation. As with objects, you can create an array using two different approaches. The first is to use the new operator in conjunction with the Array type:
var arr = new Array();
However, arrays are usually created with an array literal. The array literal [] represents an empty array, as in the following code:
var arr = [];
To add elements to an array, you can use indexed notation, just as you do to add properties to JavaScript objects:
Playing with JavaScript arrays is like playing with arrays in the .NET framework. But JavaScript arrays are more similar to generic lists in .NET 2.0: You can add new elements to an array at any time. An array can also hold elements of different types, such as strings, numbers, Boolean values, objects, and child arrays.
JavaScript arrays are sparse. That is, no space is allocated for unused elements.
Now, let’s discuss what’s probably the most powerful object that JavaScript provides: the function. The ability to manipulate functions as objects lets you create custom objects and simulate object-oriented constructs like classes, interfaces, and enumerations.
When a language treats functions as objects, it’s said to support first-class functions . This means functions can be instantiated, returned by other functions, stored as elements of arrays, assigned to variables, and so on. A function represents a portion of executable code and, at first look, seems similar to a method of an object in a classic object-oriented language such as Java, C#, or VB.NET.
As an example, here’s the code for a function called add that returns the sum of its arguments:
function add(a, b) { return a + b; }
Because JavaScript is a loosely typed language, you don’t need to specify the type of the arguments or the type of the returned value (if any). All the information about types is inferred at runtime. As a consequence, if the arguments a and b passed to the add function are numbers, the returned value is their sum. If, on the other hand, a and b are strings, the result is the concatenation of the two strings. If the arguments are of different types, strange things may happen, and an error may be raised at runtime. In chapter 13, we’ll discuss a technique called parameter validation, which the Microsoft Ajax Library uses to check that the arguments passed to a function are of the expected types.
First-class functions can be assigned to an object’s properties. The following code creates a calculator object and then assigns two functions to the object’s add and multiply properties:
var calculator = {}; calculator.add = function(a, b) { return a + b; } calculator.multiply = function(a, b) { return a * b; }
As shown in this example, you can declare a function without specifying a name. A function with no name is called an anonymous function. Interestingly, assigning an anonymous function to a property lets you invoke it through the property itself:
Note that this code treats the add and multiply properties as if they were the names of the functions they hold. Accessing a property that holds a function is similar to invoking a method of an object. For this reason, a JavaScript function stored in a property of an object is called a method.
Let’s continue our discussion of JavaScript functions by introducing two important topics. The first concerns the scope of a function: the context that you can access inside it. The second is another powerful characteristic of functions: the ability to bind them to an environment.
In JavaScript, every function is executed as a method of an object. But which object? A quick answer is, the object pointed to by the this keyword in the body of the function. This doesn’t seem strange; but life isn’t that easy. The original question is somehow still unanswered. Which object is referenced by this in a function?
Without going into too much detail, let’s determine which object is pointed to by this in the three cases you’ll encounter most often when programming in JavaScript:
Knowing which object is referenced by this in a function is fundamental in order to determine which variables, objects, and properties you can access in the body of the function. In chapter 2, we discussed client delegates, which make it possible to change the object referenced by this in a function. To make things even more interesting, JavaScript functions can be nested and even bound to a scope.
One of the characteristics of JavaScript functions is that they can be nested. This means you can declare a function in another function. Consider the following code:
This code defines a function called parent, which declares a local variable called testVariable. In the parent function, you declare the child function. The child function can access, in its body, the testVariable and arg variables that are defined outside its body. The last statement of the parent function invokes the child function, which should display the values of the variables in a message box on screen. This is what happens if you invoke the parent function this way:
parent("Yes, ");
Calling the parent function this way produces the results shown in figure 3.1.
What’s happening here? JavaScript functions are always bound to a scope, or an environment. As a consequence, child functions can access the scope of the parent function, including its local variables and the parameters passed to it. But the real, powerful thing is that if you make the parent function return its child function, the local variables continue to “live” outside the parent function. To demonstrate the power of such a feature, let’s rewrite the parent function as shown in listing 3.1.
This time, instead of invoking the child function, the parent function returns it. The curious thing is that even when the parent function has returned, its local variables continue to exist and can be accessed in the child function. To verify this, you have to call the parent function and then invoke the returned child function:
var child = parent("Yes "); child();
Surprisingly, a child function can access the variables of the parent function even if they were declared as local variables. The scope of the child function remains bound to the local variables of its outer function. Whenever this happens, you can proudly say that you’ve created a closure.
In listing 3.1, the child function displays a message on screen using two parameters defined in the scope of the parent function: arg and testVariable. Nonetheless, you can call the child function without supplying any parameters to it: You have transformed a function that takes multiple parameters into a function that takes fewer (zero in this case) parameters. This technique is known as currying.
Understanding closures and dealing with them can be difficult at first, because the most used object-oriented languages don’t support them. But closures have interesting applications; for example, the Function.createDelegate method illustrated in chapter 2 in section 2.3.4 is an application of closures. If you program in .NET using C# 2.0, then you may have heard of anonymous methods, which can’t be called closures but that implement a similar technique.
If you want to know more about C# anonymous methods, browse to http://msdn2.microsoft.com/en-us/library/0yw3tz5k.aspx.
So far, we’ve demonstrated that JavaScript functions are powerful objects, but you can do much more. For example, you can use functions to create custom objects, as you’ll see in the next section.
In section 3.1, you saw how JavaScript objects can be created as dictionaries with name and value pairs. Each pair represents a property of the object and the value of the property. This approach may be less comfortable for developers acquainted with the mechanisms of class-based object-oriented languages. In such languages, you typically specify the structure of an object in a class, and then you create instances of the class using the new operator. If you want to use a similar approach in JavaScript, it’s possible to define a function and use it in conjunction with the new operator to create custom objects. In this case, the function is called the constructor, and it’s used to define the properties of the new object. Following this approach, listing 3.2 shows how to declare the constructor for a Cat object.
function Cat() { this._name = ''; this._age = 0; }
A JavaScript function acts as a constructor when you use it together with the new operator. The following statement creates a new object using the Cat function as the constructor:
var cat = new Cat();
The new operator creates a new object and invokes the constructor. In the body of the constructor, this points to the newly created object. For this reason, accessing the properties of the this parameter in the Cat function is equivalent to adding properties to the new object. The use of the new operator causes the constructor to implicitly return the newly created object. As result, the cat variable in the previous statement holds an object with two properties: _name and _age.
Often, some properties of an object are prefixed with an underscore—as is the case with _name and _age—to suggest that they should be considered private. However, this remains a naming convention only because properties of objects can’t have a private scope. Despite what happens in Java or C#, where you can use the private modifier to prevent external objects from accessing a member of a class, in JavaScript the properties of an object are always publicly accessible. By using closures, you can treat local variables defined in a function as private members. But the convention offers a number of advantages, including the ability to inspect members from a debugger.
Every JavaScript object has a property called prototype that returns a reference to an internal object called the prototype. The prototype object plays a major role in JavaScript because it’s used to define the template of an object and to implement inheritance.
In a JavaScript object, the purpose of the prototype object is to hold all the properties that will be inherited by all the instances. The prototype object defines the structure of an object, in a manner similar to what is done with classes in many object-oriented languages. In the previous section, you saw how a function—the constructor—can be used to create custom objects and to add properties to the instances. Listing 3.3 shows how you can use the constructor’s prototype object to add additional properties and methods to instances.
function Cat() { this._name; this._age; } Cat.prototype.speak = function() { alert("Meeeeooow!"); }
In listing 3.3, you access the prototype of the Cat function and add a speak method. The speak method calls the alert function to display a string with the voice of a (hungry) cat. What are the consequences of adding a method to the prototype object of the constructor? First, whenever you create an object with the new operator and the Cat constructor, the new instance inherits the speak method, as shown in the following code:
var cat = new Cat(); cat.speak();
Second, all references to objects and arrays added to the prototype object are shared between all the instances.
Never store objects or arrays in the prototype object, unless you want to share them across all instances. Instead, store references to objects or arrays in the constructor. This way, each instance has its own copy of the object.
Adding methods to the prototype object is safe, because you’re sharing the same function objects between different instances. This can yield some advantages in terms of memory used to store multiple instances, because you’re sharing the same function objects. But accessing functions in the prototype is slightly slower than accessing them in the constructor, because they’re searched first in the current instance and then in the prototype. A common approach is to declare members in the constructor and methods in the prototype object; this is the approach we’ll follow in this book.
Now that we’ve introduced the prototype object, we’ll examine object extensibility. In the next section, we’ll recap the most common ways of adding properties to JavaScript objects.
In the previous sections, we explained how to add properties to objects. JavaScript’s dynamic features let you add a property to an object at any time by accessing a nonexistent property and assigning it a value, as shown in the following code:
var book = {}; book.title = 'ASP.NET AJAX in Action'; book.publisher = 'Manning';
In addition, you can extend instances of the built-in types by adding new properties to them. For example, you could expand an object of type String as follows:
var str = new String(); str.createdOn = new Date();
In this code, treating the String type as a constructor returns an object of type String. You add a createdOn property that returns a Date object containing the date when the string was created.
A second way to add a property to an object is to do so before an instance is created. You can do this using a constructor and its prototype object, as we explained in sections 3.1.4 and 3.1.5. For example, the following code shows how to define an object with two properties x and y, using a Point constructor:
function Point() { this.x = 0; this.y = 0; } Point.prototype.setLocation = function(x, y) { this.x = x; this.y = y; }
By using the Point constructor in conjunction with the new operator, you get back an object with the properties and methods defined in the constructor and in the prototype object:
var p = new Point(); p.setLocation(3, 6);
Usually, properties of objects are accessed through instances. Sometimes, though, it’s desirable to access methods through the type rather than through an instance, as you do with static or shared methods in C# and VB.NET. Creating static methods in JavaScript is easy because you add a property to the type or the constructor, as in the following example:
Date.now = function() { return new Date(); }
Here, you extend the built-in Date object with a now method that you can use to retrieve the current date and time. The now method is invoked directly on the Date type rather than on an instance:
var dateTime = Date.now();
You encountered static JavaScript methods when we talked about the extended Array object in chapter 2. Now, we’ll introduce literals, which are notations for representing values. In JavaScript, you can use literals to represent nearly every data type, including objects, arrays, and functions. Having a good knowledge of JavaScript literals will enable you to write compact, elegant, fast code.
In programming languages, a literal is a notation for representing a value. For example, "Hello, World!" represents a string literal in many languages, including JavaScript. Other examples of JavaScript literals are 5, true, false, and null, which represent an integer, the two Boolean values, and the absence of an object, respectively. JavaScript also supports literals for objects and arrays and lets you create them using a compact and readable notation. Consider the following statements which create an object with two properties called firstName and lastName:
var customer = new Object(); customer.firstName = 'John'; customer.lastName = 'Doe';
An equivalent way of creating a similar object is
var customer = { firstName: 'John', lastName: 'Doe' };
The right part of the assignment is an object literal. An object literal is a comma-separated list of name and value pairs enclosed in curly braces. Each pair represents a property of the object, and the two parts are separated by a colon. To create an array, you can create an instance of the Array object:
var somePrimes = new Array(); somePrimes.push(1, 2, 3, 5, 7);
But the preferred approach is to use an array literal, which is a comma-separated list of values enclosed in square braces:
var somePrimes = [ 1, 2, 3, 5, 7 ];
The previous examples demonstrate that object and array literals can contain other literals. Here is a more complex example:
var team = { name:'', members:[], count:function() { return members.length } }
The object assigned to the team variable has three properties: name, members, and count. Note that '' represents the empty string, and [] is an empty array. Even the value of the count property is a literal—a function literal:
function() { return members.length }
A function literal is constructed with the function keyword followed by an optional name and the list of arguments. Then comes the body of the function, enclosed in curly braces.
Having covered literals, we can now introduce JavaScript Object Notation (JSON), a notation that’s used to describe objects and arrays and that consists of a subset of JavaScript literals. JSON is becoming popular among Ajax developers because it can be used as a format for exchanging data, often in place of XML.
JSON is a textual data-interchange format. Its purpose is to offer a representation of structured data that is independent of the language or platform used. This makes it possible to interchange data between applications written in different languages and run the applications on different machines. Compared to XML, which is probably the best-known data-interchange format, JSON has a compact syntax. This means that often, less bandwidth is required to transmit JSON data through a network.
JSON is based on a subset of the JavaScript language. As a consequence, encoding and parsing are nearly immediate. Because the majority of Ajax developers are also JavaScript developers, there’s almost no learning curve.
JSON is built on two structures: a collection of name and value pairs, called an object; and an ordered list of values, called an array. In JSON, a value can be one of the following:
An object is represented by a JavaScript object literal, and an array is represented by a JavaScript array literal. The remaining values are represented by the corresponding literals.
Because JSON is a subset of JavaScript literals, there are some restrictions on the syntax. In a JSON object, the name part of a name/value pair must be a string, and the value part must be one of the supported values. The following is the JSON representation of an object with two properties:
{ "firstName":"John", "lastName":"Doe" }
The names of the properties (firstName and lastName) must be strings and must be enclosed in double quotes. Compare the previous code with the following, which represents a similar object:
{ firstName: "John", lastName: "Doe" }
In JavaScript, both the objects have the same structure. However, the second object isn’t a valid JSON representation, because the names of the properties aren’t enclosed in double quotes.
Restrictions also apply to JSON arrays, where elements must be supported values. For example, a Date object isn’t in the list of supported values and therefore can’t be an element of a JSON array or a property of a JSON object. A String has the same representation as a JavaScript string literal, except that strings must always be enclosed in double quotes. Numbers are similar to JavaScript number literals, but octal and hexadecimal formats aren’t supported. Here is an example of a JSON array:
[1, 2, 3, 5, 7]
The Boolean values true and false, as well as null, have the same representation as the corresponding JavaScript literals.
Methods can’t be represented using JSON, because function literals aren’t part of its syntax. Furthermore, the JavaScript new operator isn’t part of the JSON syntax and can’t be used in objects or arrays.
One of the advantages of JSON is that it’s easy to parse. Many JSON parsers, written for numerous languages, have been developed to automate the process of generating and parsing JSON. (A list is available at the official JSON site, http://json.org.) In JavaScript, the parsing process is immediate: All you have to do is pass the JSON string to the JavaScript eval function. If you have a jsonString variable that contains the JSON data, the following code parses it and returns the corresponding JavaScript object:
var parsedJson = eval('(' + jsonString + ')'),
Note that you should enclose the JSON data in parentheses before calling eval. By doing this, you force eval to consider the argument an expression, and an object literal {} won’t be interpreted as a code block. But the eval function can execute arbitrary code, which can lead to security issues if the data come from an untrusted source. For this reason, it’s always recommended that you validate the JSON data before calling the eval function.
The official JSON site, http://json.org, provides a regular expression for validating JSON data. You can find it in the JavaScript implementation downloadable from the website.
The Microsoft Ajax Library has its own JavaScriptSerializer object, contained in the Sys.Serialization namespace, which is responsible for encoding and decoding JSON data. Let’s see how it works.
The Microsoft Ajax Library provides the Sys.Serialization.JavaScriptSerializer object in order to encode and decode JSON. This object exposes two methods called serialize and deserialize. The serialize method accepts a JavaScript object as an argument and returns a string with the corresponding JSON representation:
var customer = {firstName: 'John', lastName: 'Doe'}; var serializer = Sys.Serialization.JavaScriptSerializer; var json = serializer.serialize(customer);
The json variable in this code holds a string with the JSON representation of the object stored in the customer variable. The deserialize method performs the inverse job. It takes a JSON string and returns the corresponding JavaScript object:
var customer = serializer.deserialize(json);
When you’re dealing with a JSON parser, be aware of how dates are represented. JavaScript doesn’t support a date literal. And expressions like new Date() can’t be embedded in a JSON object because the new keyword isn’t part of the protocol syntax. As a consequence, parsers need to establish a convention about how dates and times are represented.
You can represent a date by using a string or a number. For example, you could use the ISO 8601 format for date strings and the UTC format to represent a date as a number. In the UTC format, you specify the number of milliseconds elapsed from midnight January 1, 1970 (UTC). In some situations, however, these conventions aren’t enough to disambiguate between a date representation and a simple string or number. For example, how can you tell if 1169125740 should be interpreted as a simple number or as the representation of the date January 18, 2007, 13:09:00 AM?
The JavaScriptSerializer object provides a different, custom mechanism for parsing dates, which are represented using a string similar to the following:
/Date(1169125740)/
In this string, the number is the number of milliseconds since UTC. The / characters at the beginning and the end of the string are two escaped forward-slashes. Because JSON supports the backslash () as the escape character, the string is equivalent to /Date(62831853854)/. However, when the JavaScriptSerializer object detects the escape backslash, it recognizes the string as a date representation and instantiates the corresponding Date object. If you wrote the same string without the backslashes, it would be interpreted as a simple string instead of a date. This makes JSON strings fully compatible with the specification and with any deserializer, while allowing you to reliably pass dates with serializers that know this convention.
You’ll encounter JSON again in chapter 5, which is dedicated to the communication layer of the Microsoft Ajax Library. Now, it’s time to discuss the use of object-oriented constructs like classes, interfaces, and enumerations in JavaScript. In the following sections, we’ll explain how the Microsoft Ajax Library leverages the object model provided by JavaScript. The goal is to make it easy and straightforward to write object-oriented client code.
The Microsoft Ajax Library leverages the JavaScript type system to simulate object-oriented constructs not currently supported by JavaScript. Such constructs include classes, properties, interfaces, and enumerations. The idea is to use the dynamic capabilities of the language to extend the Function object and store additional information related to a particular type. Adding information to a function object makes it possible to treat constructors as classes and, as you’ll see later, to easily implement interfaces and inheritance. This enhanced type system offers the possibility to perform reflection on client types.
In this section, we’ll discuss how the Microsoft Ajax Library upgrades a JavaScript constructor to a client class. Throughout the book, we’ll use the term client class to refer to a class created in JavaScript with the Microsoft Ajax Library. From a developer’s point of view, the process is straightforward: All you have to do is add a single statement after the declaration of the constructor. Listing 3.4 illustrates this concept by showing how to create a Pet class starting from a Pet constructor.
function Pet() { this._name; this._age; } Pet.prototype = { speak : function() { throw Error.notImplemented(); } } Pet.registerClass('Pet'),
The last statement in listing 3.4 contains a call to the registerClass method. As we’ll discuss shortly, this method is responsible for setting up the constructor to make it behave as a class.
To recap, defining a client class is a three-step process:
1. Declare the constructor, which declares the fields of the class.
2. Fill the prototype object, which defines methods of the class.
3. Add a call to registerClass, which upgrades the constructor to a client class.
The registerClass method alone has the power to transform a simple JavaScript function into a client class. For this reason, it deserves some more attention.
As shown in listing 3.4, the call to registerClass is the only thing you have to add to a classic JavaScript function to make the Microsoft Ajax Library recognize it as a class. This method accomplishes three important tasks:
You store the type name in the constructor so you can access this information at runtime. As you’ll see in a moment, you usually declare classes by assigning an anonymous function to a namespaced variable. By doing so, there’s no way to programmatically know the name of the variable and thus know the fully qualified type name. This is why you need to register the type name by storing it as a string in the constructor.
Figure 3.2 shows how the Sys._Application class—whose single instance is the Application object—is registered in the MicrosoftAJAX.debug.js file.
The figure shows the complete syntax for the registerClass method, which is called as a static method of the function you’ll register as a class. Because the class can belong to a client namespace, you must pass a string with the fully qualified name of the class. This is the name of the class prefixed by the name of the containing namespace. In figure 3.2, Sys is the namespace that contains the _Application class. We’ll discuss client namespaces in section 3.3.4.
The second argument is a reference to the base class. In the Microsoft Ajax Library, a client class can have a single parent class. The advantage of specifying a base class in the call to registerClass is that you avoid writing the code needed to resolve the inheritance relationship. Instead, the library takes care of configuring instances of child classes automatically on your behalf.
The subsequent arguments specify the list of interfaces implemented by the class. In figure 3.2, the _Application class implements a single interface called Sys.IContainer. In general, a client class can implement multiple interfaces.
Because calls to registerClass are contained outside of any functions, they’re executed by the client runtime when the code is parsed. As part of the registration process, various pieces of information are stored in the constructor. In debug mode, checks are performed to ensure that you’re providing valid references to the base class and the interface types, and that you aren’t registering the same class twice. Finally, the new type is tracked by adding it to an internal collection stored in the Sys object. This completes the registration process for a client class.
Once you create a class, you can take advantage of the other object-oriented constructs provided by the Microsoft Ajax Library. For example, you can expose the values of private fields through client properties.
In this section, we’ll explain how to expose properties in JavaScript objects. In this case, the term property doesn’t refer to the properties of objects, as discussed in previous sections. Instead, we’ll talk about methods that let you read and write the values of class fields. In object-oriented languages, it’s a good practice to expose the values of private members through methods that enable you to read or write them. A method used to read a value is called an accessor, and a method used to write a value is called mutator. A class can selectively expose the values of its members and prevent an external object from directly accessing them. Also, by using accessors and mutators, code can perform additional logic before returning or storing a value.
In languages such as C# and VB.NET, this mechanism is available as a built-in construct called a property. A property is usually made with a getter (the block of logic used to read a value) and a setter (the block of logic used to write a value), but you can also have read-only and write-only properties.
In JavaScript, it’s not possible to define a private scope for an object’s fields. It may seem useless to rely on methods for reading and writing their values. But you can still use methods to perform additional logic, such as validation. The Microsoft Ajax Library defines a naming convention for declaring properties of a client class:
Following this convention, get_name and set_name are the methods used for reading and writing the value of the _name member.
The Microsoft Ajax Library relies on properties to perform many tasks such as configuring components and parsing XML Script code. We’ll talk about the client component model in chapter 8 and about the XML Script declarative language in chapter 11. We recommend that you always use properties to expose the values of class fields.
Listing 3.5 shows how to expose the values of the _name and _age members through two properties called name and age in the Pet class. Note that the setters (set_name and set_age) accept the value to store as an argument. In the set_age method, you also check that the value passed is an integer and ensure that it’s always greater than or equal to zero.
So far, you know how to create client classes and how to expose client properties. In the .NET framework, namespaces are used as containers of classes in order to minimize name conflicts. JavaScript doesn’t support namespaces, but you can simulate them using objects. Let’s see how you can create namespaces in JavaScript using the Microsoft Ajax Library.
A namespace is a container of classes. Its main purpose is to let you group classes in a logical and functional manner and even create classes with the same name, as long as they’re contained in different namespaces. This is useful when you’re attempting to avoid function name collisions from multiple script files. For example, an Ajax application may take advantage of multiple Ajax frameworks, and this increases the risk of name collisions. Or an application may need to download script files from different locations on the Internet, each with its own naming conventions; thus, conflicts are more likely to arise. JavaScript doesn’t support a namespace construct yet, but you can use objects to define a particular scope. For example, suppose you’ve defined an empty object named Samples. Let’s expand the Samples object by adding a method to it:
var Samples = {}; Samples.Pet = function() { this._name = ''; this._age = 0; }
This code assigns a constructor to the Pet property of the Samples object. The Samples object defines a new scope and can be seen as the containing namespace for the Pet constructor. You’ve assigned the constructor to the namespaced variable Samples.Pet. By turning the constructor into a client class, the name of the namespaced variable that holds the constructor becomes the fully qualified name of the class.
The Microsoft Ajax Library leverages the same pattern to simulate namespaces. The only difference is that you can take advantage of the Type.registerNamespace method to create a namespace automatically:
Type.registerNamespace('Samples'),
To create a child namespace, you have to append its name to the parent namespace. The library takes care of creating the corresponding child object and also the parents, if they don’t already exist:
Type.registerNamespace('Samples.ChildSpace'),
The Microsoft Ajax Library defines Type as a simple alias for Function.
You create a class in a namespace by assigning the constructor to a namespaced variable and then registering the constructor with the registerClass method. This procedure is shown in listing 3.6, in which the Pet class is declared in the Samples namespace. The namespace registration must always precede the declaration of any child class.
Type.registerNamespace('Samples'), Samples.Pet = function() { // Class fields. } Samples.Pet.prototype = { // Class methods. } Samples.Pet.registerClass('Samples.Pet'),
With classes, properties, and namespaces, writing object-oriented code in JavaScript is becoming similar to writing code in languages such as C# and VB.NET. Now, we’re ready to explore one of the main features of object-oriented languages: inheritance.
In object-oriented languages such as Java, C#, and VB.NET, inheritance is class-based. This means you can make a child class inherit all its public and protected members from a parent class. In JavaScript, things work differently because you don’t have classes. Inheritance is prototype-based, because properties and methods are usually inherited from the prototype object. In the following sections, we’ll do a quick overview of prototype-based inheritance. Then, we’ll explain how you can easily implement inheritance in JavaScript using the Microsoft Ajax Library.
In a prototype-based language like JavaScript, objects inherit all the properties defined in the prototype object. Let’s return for a moment on the Cat constructor defined in listing 3.3. The Cat constructor is an object of type Function that, following the principle, inherits all the properties defined in the prototype object of the Function object. In turn, Function inherits all the properties defined in the prototype of Object, which is the root type of all the JavaScript objects. The final result is that every object created using the Cat constructor will inherit the properties defined in the Function and Object prototypes, as illustrated in figure 3.3. The mechanism is similar to that of class-based inheritance; the main difference is that instead of having a chain of classes in parent-child relationship, you have a prototype chain.
In JavaScript, implementing inheritance is simple; but it’s done differently than in class-based languages, where the parent class can be specified in the class declaration. For example, one approach is to assign the object returned by the parent constructor to the prototype object of the child constructor. Without going into too much detail, the code in listing 3.7 shows how you can define a Cat object that inherits all the properties from a Pet object using prototype-based inheritance.
To create a Cat object that inherits from Pet, you perform three steps:
1. Inherit the properties defined in the Pet constructor by invoking the Pet constructor in the Cat constructor.
2. Inherit the properties defined in the prototype of Pet by assigning a new instance of Pet to the Cat prototype.
3. Override the inherited speak method by declaring a method with the same name in the Cat prototype. Note that this step isn’t mandatory: It provides a meaningful implementation of the speak method in the Cat constructor.
You can run the following code to ensure that Cat has effectively inherited all the properties from Pet:
var cat = new Cat(); cat.speak();
If you use the Microsoft Ajax Library, you have to specify the name of the base class when calling the registerClass method on the child class. Listing 3.8 shows how to define a Cat class that derives from Pet, using the Microsoft Ajax Library.
The reference to the base class is passed as the second argument to the registerClass method. When you derive from a base class, you must remember to invoke the initializeBase method in the constructor of the child class. The initializeBase method is always called on the child class with the this keyword as an argument. As shown in figure 3.4, the initializeBase method is responsible for walking the inheritance chain until the child class has inherited all the properties from the parent class and its ancestors.
Typically, when dealing with inheritance, you need to perform common tasks such as passing arguments to the base class’s constructor and overriding methods inherited by the base class. Let’s see how to perform these tasks with the help of the Microsoft Ajax Library.
To pass arguments to the constructor of the base class, you have to pass them to the initializeBase method. Let’s rewrite the constructor of the Pet class to accept the values of the _name and _age members as arguments:
Samples.Pet = function(name, age) { this._name = name; this._age = age; }
If you want to pass these arguments from the Cat class to the base Pet class, you add them to an array and pass it as the second argument to the initializeBase method. This process is illustrated in listing 3.9, where the constructor of the Cat class is rewritten to accept the same arguments as the Pet constructor.
At this point, an instance of the Cat class can be created as follows:
var cat = new Cat('Kitty', 1);
This statement creates an instance of the Cat class and sets the _name and _age members, inherited from the base Pet class, to Kitty and 1, respectively.
Object-oriented languages let you redefine the implementation of a method inherited from a parent class. When you redefine the implementation of an inherited method, you create an override. For example, in listing 3.7, you override, in the Cat class, the speak method inherited from the base class. In this case, invoking the speak method from an instance of the Cat class no longer throws a client exception, because the new function replaces the one inherited from the Pet class. Let’s see how you can override a base method and call the base class’s implementation using the Microsoft Ajax Library.
In JavaScript, you can override a method by assigning a new function to the same property: You replace the previous function with the new one. This raises an interesting question: If you’re deriving from a base class, how can you call the implementation of the base class if you’re replacing it? The answer is that you should store a reference to the base method before replacing it in the child class. The good news is that this is done automatically when you implement inheritance using the Microsoft Ajax Library. Base methods can be invoked through a method named callBaseMethod. Listing 3.10 shows how you can invoke the Pet implementation of the speak method from the child Cat class.
Samples.Cat.prototype = { speak : function() { Samples.Cat.callBaseMethod(this, 'speak'), } }
The first argument accepted by callBaseMethod is always the current instance, pointed to by this. The second argument is a string with the name of the method to invoke on the base class. In this case, because the base implementation of the speak method was defined in listing 3.4 to throw a client exception, the overridden method will behave in the same manner as the base method and throw the same client exception. If the base method accepts parameters, you encapsulate them into an array and pass it as the third argument to callBaseMethod, as shown in figure 3.5.
We’re almost at the end of our journey through the object-oriented constructs provided by the Microsoft Ajax Library. So far, you know how to create client classes and add them to namespaces, define client properties, and implement inheritance. The last two constructs we’ll examine are interfaces and enumerations.
JavaScript doesn’t support interfaces and enumerations, but the Microsoft Ajax Library simulates these constructs using functions as it does classes. The pattern is similar: You declare a function and then upgrade it to an interface or an enumeration using the registerInterface or registerEnum method, respectively. In the same manner as registerClass, these methods store in a function various pieces of information that allow them to be treated as interfaces or enumerations rather than as simple functions. Let’s start by examining interfaces; enumerations will follow.
An interface allows an object to know what another object can do, without knowing how. An interface is said to define a contract between a class and the outside world. Interfaces are different from classes in the sense that they define a list of methods and properties that a class must expose, but it’s the class’s responsibility to provide an implementation. Listing 3.11 contains the declaration of an interface called IComparable that exposes a single method, compareTo, which performs a generic comparison between objects.
To define the interface, you start by creating a JavaScript function. Because you’re dealing with an interface type, you need to prevent the function from being used as a constructor. To do this, you throw a client exception of type notImplemented as soon as you try to call the function.
The same thing must be done for the methods defined by the interface. When a client class implements an interface, its methods are copied into the constructor’s prototype. As a consequence, a client exception of type notImplemented will be thrown if you don’t override the implementation of the method. This is how interfaces work in the Microsoft Ajax Library.
The final step is to register the interface by calling the registerInterface method on the interface type. The only argument accepted by the method is a string with the fully qualified name of the interface. The call to registerInterface ensures that the interface is properly recognized by the type system.
Now, you’ll define a Temperature class that implements the IComparable interface. The compareTo method returns an integer based on the result of the comparison. The return value is 0 if the two temperature values are the same, -1 if the compared value is less than the value held by the object that performs the comparison, and +1 if it’s greater. The code for the Temperature class appears in listing 3.12.
As soon as you pass the Samples.IComparable interface to the registerClass method, all the methods defined in the interface are copied in the prototype object of the Temperature constructor. If you forget to specify the interface type, the runtime doesn’t raise an error, but the interface isn’t recognized as being implemented by the Temperature class.
Together with interfaces, enumerations are a feature supported by many object-oriented languages. Because JavaScript doesn’t support them, let’s see how you can fill this gap using the Microsoft Ajax Library.
Enumerations are a way to give names to numbers. The Microsoft Ajax Library lets you create enumerations to associate names with integer values. You can also specify values in hexadecimal format and create bit-field flags. Let’s start with the pattern for creating a Size enumeration, shown in listing 3.13.
Type.registerNamespace('Samples'), Samples.Size = function() { throw Error.notImplemented(); } Samples.Size.prototype = { Small: 1, Medium: 2, Large: 3 } Samples.Size.registerEnum('Samples.Size'),
As you did with interfaces, you define an enumeration by starting with a function. Because it makes no sense to instantiate an enumeration, you need to avoid the use of the Samples.Size function as a constructor. To do this, it’s enough to raise a client exception of type notImplemented as soon as someone attempts to call the function.
The enumeration names are defined in the prototype object, and they must be integers. They can also be specified using the hexadecimal format. The name and value pairs in the prototype object are considered the names and values of the enumeration. In the example, you define three names (Small, Medium, and Large) associated with three integers (1, 2, 3). You call the registerEnum method, passing the fully qualified name of the enumeration as an argument.
Let’s play a bit with enumerations to illustrate some of the methods available. Creating a variable of type Samples.Size is easy:
var size = Samples.Size.Medium;
At this point, the size variable holds the value 2. To display the name associated with a particular value, you call the toString method on the enumeration type, passing one of the enumeration values:
alert(Samples.Size.toString(size));
If you try to pass an invalid value, a client exception of type ArgumentOutOfRangeException is thrown. You can also parse a string as an enumeration value by calling the parse method on the enumeration type:
var smallSize = Samples.Size.parse('Small'),
Keep in mind that the string passed to the parse method is case sensitive. If the enumeration type doesn’t contain a matching name, a client exception of type ArgumentException is thrown.
You can also define enumerations to use flags. Flags are useful when you need to combine multiple values of an enumeration. Listing 3.14 shows an example that helps you understand the use of flags mode.
Type.registerNamespace('Samples'), Samples.FileAccess = function() { throw Error.notImplemented(); } Samples.FileAccess.prototype = { Read : 1, Write : 2, Execute : 4 } Samples.FileAccess.registerEnum('Samples.FileAccess', true);
In order for flags mode to work correctly, values must be powers of 2. To enable flags, you must pass true as the second argument to the registerEnum method. You can also combine flags by using the bitwise OR operator:
var readWritePermission = Samples.FileAccess.Read | Samples.FileAccess.Write;
To remove a flag, you have to AND-NOT the combined flags with the one you want to remove, as in the following statement:
var readPermission = readWritePermission & ~Samples.FileAccess.Write;
Finally, if you call the toString method when in flags mode, you obtain a string that contains all the combined names, separated by commas. For example, the following statement displays a message box with the string Read, Write:
alert(Samples.FileAccess.toString(readWritePermission));
We’ve now covered all the object-oriented constructs provided by the Microsoft Ajax Library. In the next section, you’ll see how this enhanced type system can be used to perform reflection on JavaScript objects.
Reflection is the process of discovering information about objects at runtime. For example, you might be interested in knowing whether an object has defined a particular property, or if a property is a function rather than an array. Based on this information, you can either take different actions or raise errors.
The Microsoft Ajax Library provides a group of methods to reflect on types created with the enhanced type system. As you’ll see, the goal is to be able to retrieve information about client objects while taking into account the enhanced type system and the object-oriented constructs provided by the library.
The Microsoft Ajax Library provides an enhanced type system together with object-oriented constructs. You might need to know whether a constructor has been registered as either a class or an interface. Also, you might need to know whether two classes are in a parent-child relationship, or whether a certain class implements a particular interface. Table 3.1 lists a group of methods defined by the Microsoft Ajax Library that can be used to retrieve information on client objects at runtime.
Method name |
Parameters |
Returns... |
---|---|---|
Type.isClass | Type | True if a function has been registered as a class |
Type.isInterface | Type | True if a function has been registered as an interface |
Type.isNamespace | Object | True if a function has been registered as a namespace |
getName | - | The name of the current type as a string |
getBaseType | - | A reference to the base class |
getBaseMethod | Object, String | A reference to a method with the given name from an object |
isInstanceOfType | Object | True if the given instance is of type Type |
getInterfaces | - | A list with all the interfaces implemented by a class |
implementsInterface | Type | True if an instance’s class implements the given interface |
isImplementedBy | Type | True if an interface is implemented by the given instance’s class |
The first set of methods, Type.isClass, Type.isInterface, and Type.isNamespace, determine how a particular object has been registered in the context of the enhanced type system. As we explained in the previous sections, JavaScript functions are objects, and the Microsoft Ajax Library leverages them in order to simulate classes, interfaces, and enumerations. For example, the Type.isInterface method accepts a reference to a function and returns true if the function has been registered as an interface. In the same manner, the Type.isClass method returns true if the function passed as an argument has been registered as a class using the registerClass method. In the following code, the petIsAClass variable holds true:
var petIsAClass = Type.isClass(Samples.Pet);
In section 3.3.4, you saw that namespaces can be simulated by expanding generic objects. The Type.isNamespace method accepts an object and checks whether it has been registered as a namespace using the Type.registerNamespace method. As a consequence, the isNamespace variable in the following code holds true:
var isNamespace = Type.isNamespace(Samples);
Let’s continue our exploration of the methods for reflecting on client objects by talking about the techniques that you can use to determine an object’s type.
In JavaScript, you can use the typeof operator to distinguish an object from another primitive type such as string, a number, or a Boolean value. However, the typeof operator doesn’t distinguish between objects and other objects. If you create objects using different constructors, the typeof operator always returns function, which is the type of the constructor.
To distinguish between objects instantiated with different constructors, you could use JavaScript’s instanceof operator. Due to the way inheritance is resolved, the instanceof operator doesn’t work with classes created with the Microsoft Ajax Library. Instead, you have to use the isInstanceOfType method. This method is called on the type that you will test. It accepts an object as an argument and returns true if the object is an instance of that type. In the following code, the test1 and test2 variables hold true because both instances are of type Pet. The test3 variable holds false because tmpr1 isn’t of type Cat:
var pet1 = new Samples.Pet(); var cat1 = new Samples.Cat(); var tmpr1 = new Samples.Temperature(); var test1 = Samples.Pet.isInstanceOfType(pet1); var test2 = Samples.Pet.isInstanceOfType(cat1); var test3 = Samples.Cat.isInstanceOfType(tmpr1);
To retrieve information about the inheritance relationship between classes, you use the inheritsFrom method. This method is called on a child class and accepts the potential parent class as an argument. It returns true if the class passed as an argument is effectively the parent class. In the following code, the catIsAPet variable hold true because Cat inherits from Pet:
var catIsAPet = Samples.Cat.inheritsFrom(Samples.Pet);
When talking about client classes, we stated that the Microsoft Ajax Library stores information about the type name in constructors. If you want to know the name of a type as registered by the Microsoft Ajax Library, you can call the getName method and get back a string with the type name:
var customTypeName = Samples.Pet.getName(); var booleanTypeName = Boolean.getName();
The first statement calls getName on the Pet class defined in section 3.3.1. The variable customTypeName holds the string Samples.Pet. In the second statement, you can see that the method also works on JavaScript’s built-in types, like Boolean. In this case, the variable booleanTypeName holds the string Boolean.
To complete our discussion of reflection in JavaScript, let’s combine some of the methods illustrated in the previous section to build a more complex example. In the next section, you’ll build a class browser for displaying the classes and interfaces defined in the root namespaces of the Microsoft Ajax Library.
In this section, you want to combine some of the reflection methods presented in the previous section to obtain a class browser for exploring classes and interfaces defined in the root namespaces of the Microsoft Ajax Library. Figure 3.6 shows the example running in Internet Explorer.
The code in listing 3.15 creates a list with the namespaces defined in the library. When the user chooses one from the list, you use some of the reflection methods to display all the classes and interfaces defined in that namespace.
The markup for the example defines a drop-down list with all the namespaces defined by the Microsoft Ajax Library. When a namespace is selected, the corresponding string is evaluated to obtain a reference to the namespace.
Then, you loop the selected namespace to search for classes and interfaces contained in it. To do this, you first check for functions (recall that classes and interfaces are simulated with functions by the Microsoft Ajax Library). You use the isClass and isInterface methods to determine whether you’ve found a class or an interface and then add it to the corresponding array.
Finally, you use a string builder instance to format the elements of the arrays and display the information about classes and interfaces in a label on screen. We discussed the Sys.StringBuilder class in chapter 2.
With this example, our discussion of the object-oriented constructs provided by the Microsoft Ajax Library is complete. JavaScript developers will benefit from the enhanced type system, and .NET developers will have a chance to become more comfortable with the JavaScript language.
In the following section, we’ll introduce the event model provided by the Microsoft Ajax Library. With this model, you can expose and raise events in JavaScript objects.
JavaScript developers are acquainted with the event model provided by the DOM. You can program against the DOM elements of a web page by hooking up their events and executing code in event handlers. For example, a button element can raise a click event when it’s clicked by the user. The window object raises a load event when the page is loaded and an unload event when the user navigates away from the page.
Although DOM objects can raise events, this isn’t true for generic JavaScript objects. The Microsoft Ajax Library provides an event model that lets you expose events in client objects, following a model that closely resembles the one used in the .NET framework. We’ll divide this discussion into two parts. First, you’ll see how to expose an event in a JavaScript object. Then, you’ll learn how to subscribe to and handle an event.
With the Microsoft Ajax Library, you can expose an event in a JavaScript object and subscribe to it with multiple handlers. This means that events are multicast, because you’re able to handle them with multiple functions. Exposing an event is a three-step process:
1. Create a method that adds an event handler.
2. Create a method that removes an event handler.
3. Create a method that is responsible for raising the event.
The methods responsible for adding and removing the event handlers must follow a naming convention defined by the Microsoft Ajax Library:
For example, if the object exposes an event called initialize, it has two methods called add_initialize and remove_initialize. These methods are responsible for adding and removing event handlers for the initialize event. The Application object, which we introduced in chapter 2, exposes some events, one of which is init. The Sys.Application object has two methods called add_init and remove_init.
In the .NET framework, the process for exposing events is conceptually similar. There, you use a delegate to add and remove event handlers, and you fire the event by executing the delegate. You can read a good article about the .NET event model at http://msdn.microsoft.com/msdnmag/issues/03/02/BasicInstincts/.
Figure 3.7 illustrates the pattern used in the Microsoft Ajax Library to expose an event in a JavaScript object.
In order to be able to manage multiple event handlers, the Microsoft Ajax Library provides a class called Sys.EventHandlerList, which encapsulates an object used to store multiple event handlers for multiple events. You access this through two class methods: addHandler and removeHandler. Objects that want to expose events usually create an instance of the class in the constructor and store it in a private property:
this._events = new Sys.EventHandlerList();
At this point, the event methods interact with the event handlers list to manage event handlers.
The base Sys.Component class, used for creating client components, comes with an instance of the Sys.EventHandlerList class and support for client events. Client components are discussed in chapter 8.
Listing 3.16 puts the theory into practice and shows how this is done in a Collection class that wraps an array. The class can raise an itemAdded event whenever a new element is added to the array. For simplicity, we show only the code for the add operation, but removing an element can be implemented with similar code.
The add_itemAdded and remove_itemAdded methods are responsible for adding and removing event handlers for the itemAdded event. They interact with the event-handlers list by invoking its addHandler and removeHandler methods. In the code, the list of event handlers is accessed through a method called get_event, which returns the instance stored in the _events field . Note that the creation of the Sys.EventHandlerList instance is done (lazily) only the first time the object tries to access the events property, so that constructing an instance doesn’t cost the creation of the event list if it’s not going to be used. This is done to remain consistent with the model used by client components, which encapsulate an instance of the Sys.EventHandlerList class by default. More information is given in chapter 8, which is entirely dedicated to client components.
Instead of writing a separate method to fire each event you’re exposing, it’s preferable to write a single _raiseEvent method and pass it a string with the event name and the event arguments. The _raiseEvent method calls getHandler on the event-handlers list to retrieve all the handlers for the given event. Stored in the handler variable is a function that, when called , executes all the handlers for the event.
When the handlers are called , each receives two arguments: a reference to the object that raised the event and an object that contains the event arguments. This paradigm should be familiar to .NET developers, because in the .NET framework, event handlers receive similar arguments. Note that if you don’t specify any event arguments, the _raiseEvent method uses Sys.EventArgs.Empty, which is the static, singleton instance of the Sys.EventArgs class. This instance represents the absence of event arguments.
In the add method, you explicitly pass the Sys.EventArgs.Empty instance when calling _raiseEvent to fire the itemAdded event. A full implementation would probably derive a custom class from Sys.EventArgs that includes a reference to the item that was just added.
Now that you know how to expose an event, let’s see how external objects can subscribe to and handle it.
In the previous section, we discussed how to expose an event in a client object. Now to this question: How can you subscribe to and handle such an event? Subscribing to an event is simple: All you have to do is call the method responsible for adding an event handler.
For example, if you want to subscribe to the itemAdded event raised by the Collection class defined in listing 3.16, you pass a JavaScript function to the add_itemAdded method of the Collection instance. This function is an event handler for the itemAdded event. If you pass the same function to the remove_itemAdded method, you remove it from the list of event handlers. To add multiple handlers, you have to invoke the add_itemAdded method multiple times. Translated into code, event subscription is performed as follows:
var collection = new Samples.Collection(); collection.add_itemAdded(onItemAdded);
Now, you need to define the onItemAdded function, which is the event handler that you passed to the add_itemAdded method. You can do so this way:
function onItemAdded(sender, e) { alert('Added an item to the collection'), }
The event handler is a function that accepts two arguments. As we explained in the previous section, the event handler receives references to the object that raised the event and an object that represents the event arguments. We called these arguments sender and e to emphasize the similarity with the event model used in the .NET framework. Finally, you add an item to the collection to fire the itemAdded event. This can be done as follows:
collection.add('test string'),
In this chapter, we reviewed the most important concepts of the JavaScript language and discussed how to leverage the Microsoft Ajax Library in order to become more productive with JavaScript.
We started by discussing the main data structures provided by JavaScript, such as objects and arrays. Then, we made an extensive overview of functions, which are objects you can use to define methods and create custom objects. After taking a peek at the various literals supported by JavaScript, we introduced JSON, a lightweight data format that lets Ajax applications exchange data; it’s often preferred to XML, due to its compactness and ease of parsing.
The Microsoft Ajax Library leverages the object model provided by JavaScript and provides object-oriented constructs commonly found in class-based languages, such as classes, interfaces, and enumerations. With the library, you can also easily create namespaces, implement inheritance, and override methods. The enhanced type system provided by the Microsoft Ajax Library lets you perform reflection on objects. For example, you can discover whether a type is a class rather than an interface. You can also determine whether an instance is of a certain type or whether a class is in parent-child relationship with another class.
Finally, we discussed how you can expose multicast events in JavaScript objects using the event model provided by the Microsoft Ajax Library, which closely resembles the one used in the .NET framework.
Now, it’s time to take a break from the Microsoft Ajax Library and focus on the server framework. In the next chapter, we’ll introduce the ASP.NET AJAX server controls and explain how to upgrade an ASP.NET website to ASP.NET AJAX.
3.15.10.64