Tải bản đầy đủ (.pdf) (54 trang)

ASP.NET AJAX Programmer’s Reference - Chapter 4 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (341.35 KB, 54 trang )

JavaScript Object-Oriented
Programming and Type
Reflection Extensions
The .NET Framework comes with the following two important programming capabilities:
❑ Fully fledged typing and type reflection capabilities, allowing you to perform
runtime-type inspections, discoveries, invocations, instantiations, and the like
❑ Fully fledged object-oriented capabilities, allowing you to take full advantage of all the
well-known benefits of object-oriented programming (OOP) such as classes, interfaces,
inheritance, and the like
Because the main goal of the ASP.NET AJAX client-side framework is to emulate the ASP.NET and
.NET Frameworks as much as possible, the ASP.NET AJAX client-side framework comes with a set
of extensions — known as the ASP.NET AJAX OOP and type reflection extensions — that add .NET-
like OOP and type reflection capabilities to JavaScript as much as possible.
You’ve already seen some reflection capabilities in Chapter 2 where the ASP.NET AJAX client-side
Framework extends the JavaScript
Object type to add support for the getType and getTypeName
methods.
The .NET Framework comes with an important class named
Type that provides most of the reflec-
tion capabilities of the Framework. Following the same pattern, the ASP.NET AJAX client-side
framework introduces a type named
Type , which provides both OOP and type reflection capabili-
ties, which I’ll discuss in this chapter.
First, I’ll examine the JavaScript technologies that the ASP.NET AJAX OOP and type reflection
extensions use under the hood to extend JavaScript to add OOP and type reflection support. This
examination will put you in a much better position to understand and to use the ASP.NET AJAX
client-side framework.
c04.indd 77c04.indd 77 8/20/07 6:00:31 PM8/20/07 6:00:31 PM
Chapter 4: JavaScript Object-Oriented Programming
78
JavaScript Functions


Every JavaScript function is an instance of a JavaScript type named Function and supports the follow-
ing properties:
❑ arguments : This property contains the parameters of a JavaScript function, which also includes
the parameters that the original definition of the function does not contain. You can use this
property to access the parameters of a function within the body of the function. As the following
code shows, you can even define a function without any parameters and use the
arguments
property to access the parameters. However, this is not a recommended practice.
function MyFunction()
{
for (var i = 0; i<arguments.length; i++)
alert (arguments[i]);
}

window.onload = function()
{
MyFunction(‘info1’);
MyFunction(‘info1’,’info2’);
}
❑ constructor : The constructor property references the function or constructor that was invoked
to create an object. For example, if you run the following code, the alert will show
function
Function () { [ native code ] }
:
<%@ Page Language=”C#” %>
<html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<script language=”JavaScript” type=”text/javascript”>
function MyFunction()
{

// Body of the function goes here
}

window.onload = function() {
alert(MyFunction.constructor);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
</form>
</body>
</html>
❑ prototype : The prototype property allows you to extend the functionality of a type to add sup-
port for new instance properties and methods. JavaScript guarantees that all instances of the type
will automatically inherit these new properties and methods. As you’ll see later, the ASP.NET AJAX
client-side framework makes extensive use of this property to add OOP support to JavaScript.
c04.indd 78c04.indd 78 8/20/07 6:00:31 PM8/20/07 6:00:31 PM
Chapter 4: JavaScript Object-Oriented Programming
79
Every JavaScript function also supports two methods, call and apply , that you can use to invoke a
function object. Using these methods to invoke a function may sound redundant because you can invoke
a function by simply naming it. For example, you can invoke the
MyFunction JavaScript function
defined in the previous code fragment by simply calling
MyFunction(); . Why would anyone then call

MyFunction.call() to invoke the function when you can directly call the function itself?
The
call and apply methods enable you to specify the this value used inside a JavaScript function. As

you can see, JavaScript enables you to specify not only the parameters passed into a JavaScript function
but also the
this value. As such, the first parameter of the call and apply methods references a
JavaScript object, which is used to set this value. Note that this JavaScript object does note have to own
the JavaScript function on which the call or apply method is invoked. As you’ll see later, the ASP.NET
AJAX client-side framework uses this feature when it’s adding OOP support to JavaScript.
Based on the fact that both the
call and apply methods do the same thing — that is, invoke their asso-
ciated method — you may be wondering why there are two methods. The main difference between these
two methods is in how the parameters of their associated JavaScript functions are passed into these two
methods. If your parameters are already loaded into an array, you can call the
apply method and pass
the array directly to this method. Otherwise, you can call the
call method, passing the parameters as a
list of comma-separated items.
JavaScript Classes
JavaScript is inherently an object-based programming language, not an object-oriented programming
language. As such, it has limited OOP support, which is discussed in this section. There is no JavaScript
keyword equivalent to the C# or VB.NET
class keyword. The constructor of a JavaScript class also
defines the class itself. Listing 4-1 presents an example of a JavaScript class named
Employee .
Listing 4-1: A JavaScript Class
<%@ Page Language=”C#” %>
<html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>

<script language=”JavaScript” type=”text/javascript”>
function Employee (firstName, lastName)
{

this._firstName = firstName;
this._lastName = lastName;
}

Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}

(continued)
c04.indd 79c04.indd 79 8/20/07 6:00:32 PM8/20/07 6:00:32 PM
Chapter 4: JavaScript Object-Oriented Programming
80
Listing 4-1: (continued)
window.onload = function()
{
var e = new Employee (“Shahram”, “Khosravi”);
alert(e.get_firstName());
e.set_firstName(“Shahram1”);
alert(e.get_firstName());
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
</form>
</body>

</html>
Type
As mentioned earlier, the ASP.NET AJAX client-side framework introduces a new type or class named
Type . Let’s take a look under the hood to see what the Type class is:
window.Type = Function;
As you can see, Type is basically a new alias for the Function class. This aliasing is done because
“Type” makes more sense in the context of the .NET Framework. Keep in mind that the main goal of
ASP.NET AJAX is to make the client-side framework act like the .NET Framework as much as possible.
Aliasing
Function to Type is just a simple first step toward this goal. The next step is to extend the Type
(formerly known as
Function ) class to add support for new methods and properties that will help make
the client-side programming more like server-side .NET programming.
As discussed previously,
Type (or Function ) features a property named prototype . The JavaScript
engine guarantees that every instance of
Type automatically inherits every method and property
assigned to the
prototype property. This means that every JavaScript function will automatically inherit
or pick up every method and property assigned to the
prototype property of the Type or Function
class. Because the constructor of every JavaScript class, including your own custom classes such as

Employee , is nothing but a JavaScript function, this also means that every JavaScript class, including
your own custom classes, will automatically inherit every method and property assigned to the

prototype property of the Type or Function class.
Next, you’ll learn how the ASP.NET AJAX client-side framework takes full advantage of this powerful
feature of JavaScript to extend
Type to add support for common OOP features such as namespaces,

classes, inheritance, interfaces, and the like. Each of the following sections covers one of the new meth-
ods or properties that the ASP.NET AJAX client-side framework has added to
Type (or Function ). Each
section consists of three parts. The first part describes what the method does. The second part presents
an example where the method is used. The third part looks under the hood to show you how the method
is implemented internally. Knowing the internals of these methods and properties will put you in a
much better position to understand and to extend the ASP.NET AJAX client-side framework.
c04.indd 80c04.indd 80 8/20/07 6:00:32 PM8/20/07 6:00:32 PM
Chapter 4: JavaScript Object-Oriented Programming
81
register Class
The ASP.NET AJAX client-side framework extends the functionality of Type to add support for a new
method named
registerClass . As the name implies, this method registers a specified class with the
ASP.NET AJAX client-side framework.
To add a new class to the ASP.NET AJAX client-side framework, you first need to implement the class.
For example, the following code implements a class named
Employee :
Employee = function (firstName, lastName)
{
this_firstName = firstName;
this_lastName = lastName;

}

Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},

set_lastName : function (value) {this._lastName = value;}
}
Then, call the registerClass function of the Employee class to register your new class with the
ASP.NET AJAX client-side framework, as follows:
Employee.registerClass(“Employee”);
Listing 4-2 presents a page that defines, registers, and uses the new Employee class.
Listing 4-2: Registering the Employee Class
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

(continued)
c04.indd 81c04.indd 81 8/20/07 6:00:33 PM8/20/07 6:00:33 PM
Chapter 4: JavaScript Object-Oriented Programming
82
Listing 4-2 (continued)
Employee.prototype =
{
get_firstName : function () {return this._firstName;},

set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}

Employee.registerClass(“Employee”);

var e = new Employee (“Shahram”, “Khosravi”);
alert(e.get_firstName());
e.set_firstName(“Shahram1”);
alert(e.get_firstName());
</script>
</form>
</body>
</html>
The following line of code seems to suggest that the Employee class has a method named

registerClass :
Employee.registerClass(“Employee”);
However, as Listing 4-2 shows, the Employee class does not contain this method. To understand how
this is possible, you need to look at the internal implementation of the
registerClass method
shown in Listing 4-3 . As this code listing shows, the
registerClass is assigned to the prototype
property of the
Type or Function class. As discussed before, every JavaScript class, including your
own custom classes, automatically inherits any method or property assigned to the
prototype
property of
Type .

Listing 4-3: The Portion of the Internal Implementation of the register Class Function
Type.prototype.registerClass = function(c, b, d)
{
. . .
this.prototype.constructor = this;
this.__typeName = c;
this.__class = true;
. . .
if(!window.__classes)
window.__classes = {};

window.__classes[c.toUpperCase()] = this;
. . .
return this;
};
Note that Listing 4-3 presents a portion of the internal implementation of the registerClass function.
You’ll see the rest of the implementation of this function in the following sections. Also notice that
c04.indd 82c04.indd 82 8/20/07 6:00:33 PM8/20/07 6:00:33 PM
Chapter 4: JavaScript Object-Oriented Programming
83
registerClass takes three arguments. The second and third arguments are discussed later. As
Listing 4-3 shows, the
registerClass method takes these actions:
1. It assigns its first parameter to an internal field named __typeName :
this.__typeName = c;
As Listing 4-2 shows, this parameter contains the name of the class being registered — for
example,
“Employee” .
2. It sets an internal Boolean field named __class to true to specify that the entity being
registered is a class:

this.__class = true;
3. It instantiates a global object named _classes , if it hasn’t already been instantiated:
if(!window.__classes)
window.__classes = {};
4. It uses the name of the class as an index to store the current class in the _classes object:
window.__classes[c.toUpperCase()] = this;
This means that the ASP.NET AJAX client-side framework maintains an internal object that contains all
the classes registered with the framework. This allows you to perform runtime class reflection queries,
similar to .NET class reflection queries.
This also means that every class registered with the ASP.NET AJAX client-side framework maintains
metadata information, such as the type name, in its internal fields, such as
_typeName . This enables you
to perform runtime object reflections similar to .NET object reflections on registered classes. You’ll see an
example of this reflection in the next section.
get Name
The getName method returns the name of the specified type, as shown in the following example. This is
a simple example of the reflection capabilities of the ASP.NET AJAX client-side framework.
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
(continued)
c04.indd 83c04.indd 83 8/20/07 6:00:33 PM8/20/07 6:00:33 PM
Chapter 4: JavaScript Object-Oriented Programming
84

Employee = function (firstName, lastName)
{
this_firstName = firstName;
this._lastName = lastName;
}

Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}

Employee.registerClass(“Employee”);
alert(Employee.getName());
</script>
</form>
</body>
</html>
Once again, note that the getName method is called directly on the Employee class, implying that this
class contains this method. As the following code shows, this is possible because the
getName method is
assigned to the
prototype property of Type :
Type.prototype.getName = function()
{
return typeof this.__typeName === “undefined” ? “” : this.__typeName;
};
Notice that the getName function simply returns the value of the __typeName field discussed

previously.
is Class
The isClass method is a static method of the Type class, which means that you must call this method
directly on the
Type itself. This method returns a Boolean value that specifies whether the parameter
passed into it is a class. For example, the call into the
isClass function in the boldfaced portion of the
following code listing returns true, because
Employee is registered as a class:
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<script language=”JavaScript” type=”text/javascript”>
c04.indd 84c04.indd 84 8/20/07 6:00:34 PM8/20/07 6:00:34 PM
Chapter 4: JavaScript Object-Oriented Programming
85
Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},

get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}

Employee.registerClass(“Employee”);
alert(Type.isClass(Employee));
</script>
</form>
</body>
</html>
As the following code shows, the isClass method is a static method because it’s not defined on the

prototype property. Note that this method simply returns the value of the _class private field dis-
cussed in Listing 4-3 . The
isClass method is yet another example of the ASP.NET AJAX client-side
framework’s type reflection capabilities.
Type.isClass = function(a)
{
if(typeof a === “undefined” || a === null)
return false;
return !!a.__class;
};
register Namespace
The idea of a namespace is one of the fundamental OOP concepts, but JavaScript does not support
namespaces. The ASP.NET AJAX client-side framework extends the functionality of
Type to add support
for a static method named
registerNamespace that makes it possible to define namespaces in
JavaScript. Because this method is static, you must call it directly on the
Type itself. Here is an example:

<%@ Page Language=”C#” %>

<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“MyNamespace”);

(continued)
c04.indd 85c04.indd 85 8/20/07 6:00:34 PM8/20/07 6:00:34 PM
Chapter 4: JavaScript Object-Oriented Programming
86
MyNamespace.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

MyNamespace.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}

MyNamespace.Employee.registerClass(“MyNamespace.Employee”);

alert(Type.isClass(MyNamespace.Employee));
</script>
</form>
</body>
</html>
This example first registers a namespace named MyNamespace :
Type.registerNamespace (“MyNamespace”);
Then it defines a class named Employee that belongs to this namespace:
MyNamespace.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

MyNamespace.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}
Finally, it registers the class with the ASP.NET AJAX client-side framework:
MyNamespace.Employee.registerClass(“MyNamespace.Employee”);
Note that the namespace of a class is part of the name of the class.
Listing 4-4 presents the internal implementation of the
registerNamespace method. As the first
line of code shows, the ASP.NET AJAX client-side framework adds a new global array named
_rootNamespaces to the window object. As you’ll see shortly, the registerNamespace method
adds the global namespace being registered to this global array. In other words, this global array
contains all the global namespaces registered with the ASP.NET AJAX client-side framework.

c04.indd 86c04.indd 86 8/20/07 6:00:34 PM8/20/07 6:00:34 PM
Chapter 4: JavaScript Object-Oriented Programming
87
Listing 4-4: The Internal Implementation of the register Namespace Function
window.__rootNamespaces = [];

Type.registerNamespace = function(f)
{
var d = window, c = f.split(“.”);
for(var b = 0; b < c.length; b ++ )
{
var e = c[b], a = d[e];
if( ! a)
{
a = d[e] = {};
if(b === 0)
window.__rootNamespaces[window.__rootNamespaces.length] = a;

a.__namespace = true;
a.__typeName = c.slice(0, b + 1).join(“.”);
a.getName = function()
{
return this.__typeName;
};
}
d = a;
}
}
Now, I’ll walk through the code shown in Listing 4-4 to examine how the ASP.NET AJAX client-side
framework manages to add a namespace capability to JavaScript. In general, there are two types of

namespaces: global and local. A local namespace is one that is a subset of another namespace. A global
namespace is a namespace that does not belong to any other namespace. For example, you could have a
global namespace named
Department , which in turn may contain one or more local namespaces, such
as
Section , as in Department.Section . The Section sub-namespace in turn may contain one or more
namespaces, such as
SubSection , as in Department.Section.SubSection .
As Listing 4-4 shows, the ASP.NET AJAX client-side framework maintains all global namespaces in the

_rootNamespaces array. In the following section of the listing, the object that represents a namespace
features a Boolean field named
_namespace that specifies that this object is a namespace, a string field
named
_typeName that contains the fully qualified name of the namespace such as Department
. Section
, and a getter method named getName that returns the fully qualified name of the namespace:
a.__namespace = true;
a.__typeName = c.slice(0, b + 1).join(“.”);
a.getName = function()
{
return this.__typeName
}
The object that represents a namespace, such as Department , also acts as a container (hash) for the
objects that represent its sub-namespaces, such as
Section .
c04.indd 87c04.indd 87 8/20/07 6:00:35 PM8/20/07 6:00:35 PM
Chapter 4: JavaScript Object-Oriented Programming
88
is Namespace

The isNamespace method is a static method of the Type class. This method returns a Boolean value that
specifies whether the specified object is a namespace. For example, the call into the
isNamespace function in
the boldfaced portion of the following code returns true because
MyNamespace is registered as a namespace:
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“MyNamespace”);
MyNamespace.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

MyNamespace.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;}
}

MyNamespace.Employee.registerClass(“MyNamespace.Employee”);

alert(Type.isNamespace(MyNamespace));
</script>
</form>
</body>
</html>
Listing 4-5 presents the internal implementation of the isNamespace method. This method simply
returns the value of the
_namespace field of the object. As you may recall from Listing 4-4 , the

registerNamespace method sets the _namespace field of the object that represents the namespace to

true to signal that the object is a namespace. This is yet another example of the type reflection capabili-
ties of the ASP.NET AJAX client-side framework.
Listing 4 -5: The Internal Implementation of is Namespace
Type.isNamespace = function(a)
{
if(typeof a === “undefined” || a === null)
return false;
return ! ! a.__namespace;
};
c04.indd 88c04.indd 88 8/20/07 6:00:35 PM8/20/07 6:00:35 PM
Chapter 4: JavaScript Object-Oriented Programming
89
register Interface
The ASP.NET AJAX client-side framework extends Type to add support for a new method named

registerInterface , which enables you to register an interface with the framework. The best way to
understand this is to walk through the example shown in Listing 4-6 .
Listing 4 -6: An Example that uses the register Interface
<%@ Page Language=”C#” %>

<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“Department”);
Department.IEmployee = function Department$IEmployee()
{
throw Error.notImplemented();
};

function Department$IEmployee$get_employeeID ()
{
throw Error.notImplemented();
};

function Department$IEmployee$set_employeeID ()
{
throw Error.notImplemented();
};

Department.IEmployee.prototype =
{
get_employeeID : Department$IEmployee$get_employeeID,
set_employeeID: Department$IEmployee$set_employeeID
}


Department.IEmployee.registerInterface(“Department.IEmployee”);

Department.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Department.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
(continued)
c04.indd 89c04.indd 89 8/20/07 6:00:35 PM8/20/07 6:00:35 PM
Chapter 4: JavaScript Object-Oriented Programming
90
Listing 4-6 (continued)
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;},
get_employeeID : function () {return this._employeeID;},
set_employeeID : function (value) {this._employeeID = value;}
}

Department.Employee.registerClass(“Department.Employee”, null,
Department.IEmployee);
</script>
</form>
</body>
</html>
Obviously, you have to first define the interface before you can register it. Defining an interface is pretty

much like defining a class, with one big difference: The constructors, methods, and properties raise
exceptions.
Next, you need to register the interface, as follows:
Department.IEmployee.registerInterface(“Department.IEmployee”);
Listing 4-6 shows you how to write a class that implements the interface. First you need to define the
class. As the boldfaced portion of the following code shows, the
Employee class implements the
get_employeeID and set_employeeID methods of the IEmployee interface:
Department.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Department.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;},
get_employeeID : function () {return this._employeeID;},
set_employeeID : function (value) {this._employeeID = value;}
}
Next, you need to register your class, like this:
Department.Employee.registerClass(“Department.Employee”, null, Department .IEmployee);
Note that the registerClass method takes a third parameter, which references the interface. Passing
this parameter into the
registerClass tells the ASP.NET AJAX client-side framework that the class
c04.indd 90c04.indd 90 8/20/07 6:00:36 PM8/20/07 6:00:36 PM
Chapter 4: JavaScript Object-Oriented Programming

91
being registered implements the specified interface, as you can see here:
Department.Employee.prototype.getEmployeeID = function ()
{
return this._employeeID;
};

Department.Employee.prototype.setEmployeeID = function (value)
{
this._employeeID = value;
};
Listing 4-7 presents the internal implementation of the registerInterface method. This method
simply sets the
_typeName string field to the name of the interface being registered and the _interface
Boolean field to
true to specify that the current object is an interface. As you can see, the registration
simply creates the metadata necessary for .NET-like reflection.
Listing 4 -7: The Internal Implementation of registerInterface
Type.prototype.registerInterface = function(a)
{
this.prototype.constructor = this;
this.__typeName = a;
this.__interface = true;
window.__registeredTypes[a] = true;
return this;
};
Listing 4-3 presented a portion of the implementation of the registerClass function. The first parame-
ter of the
registerClass method contains the fully qualified name of the class being registered, includ-
ing its namespace hierarchy — for example,

Department.Employee . The second parameter is discussed
in later sections of this chapter. The third optional parameter of
registerClass contains the interfaces
that the class being registered implements. The highlighted portion of Listing 4-8 shows the internal
implementation of the
registerClass method that handles the third parameter.
Listing 4 -8: The Portion of the Internal Implementation of the register Class Function
Type.prototype.registerClass = function(c, b, d)
{
this.prototype.constructor = this;
this.__typeName = c;
this.__class = true;
. . .
if(!window.__classes)
window.__classes = [];
window.__classes[c.toUpperCase()] = this;
if(d)
{
this.__interfaces = [];
for(var a = 2; a < arguments.length; a ++ )
(continued)
c04.indd 91c04.indd 91 8/20/07 6:00:36 PM8/20/07 6:00:36 PM
Chapter 4: JavaScript Object-Oriented Programming
92
Listing 4-8 (continued)
{
var e = arguments[a];
this.resolveInheritance();
for (var methodName in interfaceType.prototype)
{

var method = interfaceType.prototype[methodName];
if (!this.prototype[methodName])
{
this.prototype[methodName] = method;
}
}
this.__interfaces.push(e)
}
}
return this
};
The highlighted portion of Listing 4-8 takes these steps:
1. It defines and instantiates a new array field named _interfaces . As you’ll see shortly, the

registerClass method uses this array field as a stack, which JavaScript implements as an array.
this.__interfaces = [];
2. It iterates through the interfaces that the third parameter of registerClass contains and
pushes each enumerated interface onto the top of the stack:
var e = arguments[a];
this.__interfaces.push(e)
As these steps show, each class maintains an internal stack that contains the interfaces that the class
implements. As you’ll see in the next sections, this internal stack enables you to perform .NET-like
interface-related reflections on a given type or class. This stack is an example of .NET-like metadata.
get Interfaces
The getInterfaces method enables you to query a type for all the interfaces that the type and its
ancestor types implement. The boldfaced portion of the following code first calls the
getInterfaces
function on the
Department.Employee type to return an array that contains all the interfaces that
this type and its ancestor types implement, and then iterates through these interfaces and displays

their names:
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
c04.indd 92c04.indd 92 8/20/07 6:00:36 PM8/20/07 6:00:36 PM
Chapter 4: JavaScript Object-Oriented Programming
93
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“Department”);
Department.IEmployee = function Department$IEmployee()
{
throw Error.notImplemented();
};

function Department$IEmployee$get_employeeID ()
{
throw Error.notImplemented();
};

function Department$IEmployee$set_employeeID ()
{
throw Error.notImplemented();
};

Department.IEmployee.prototype =

{
get_employeeID : Department$IEmployee$get_employeeID,
set_employeeID: Department$IEmployee$set_employeeID
}

Department.IEmployee.registerInterface(“Department.IEmployee”);

Department.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Department.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;},
get_employeeID : function () {return this._employeeID;},
set_employeeID : function (value) {this._employeeID = value;}
}

Department.Employee.registerClass(“Department.Employee”, null,
Department.IEmployee);

var interfaces = Department.Employee.getInterfaces();
for (var i = 0; i<interfaces.length; i++)
alert(interfaces[i].getName());
</script>

</form>
</body>
</html>
c04.indd 93c04.indd 93 8/20/07 6:00:37 PM8/20/07 6:00:37 PM
Chapter 4: JavaScript Object-Oriented Programming
94
Listing 4-9 presents the internal implementation of the getInterfaces function.
Listing 4-9: The Internal Implementation of the get Interfaces Method
Type.prototype.getInterfaces = function()
{
var a = [], b = this;
while(b)
{
var c = b.__interfaces;
if(c)
{
for(var d = 0, f = c.length; d < f; d ++ )
{
var e = c[d];
if(!Array.contains(a, e))
a[a.length] = e;
}
}
b = b.__baseType;
}
return a;
};
As you can see in this listing, the getInterfaces function takes the following steps:
1. It defines and instantiates a local JavaScript array:
var a = []

2. It assigns the current type to a local variable:
b = this;
3. It accesses the interfaces that the current type implements: As you saw in Listing 4-7 , every type
maintains the list of the interfaces that it implements in an internal array named
_interfaces :
var c = b.__interfaces;
4. It iterates through the interfaces that the current type implements and adds each enumerated
interface to the local JavaScript array defined in step 1:
a[a.length] = e
5. It assigns the base type of the current type to the local variable defined in step 2, which means
that the base type is now the current type:
b = b.__baseType
6. It repeats steps 3, 4, and 5.
As these steps show, the
getInterfaces method returns all the interfaces that the type and all its
ancestor types implement.
c04.indd 94c04.indd 94 8/20/07 6:00:37 PM8/20/07 6:00:37 PM
Chapter 4: JavaScript Object-Oriented Programming
95
is Interface
You can use the isInterface function to determine whether a specified object is an interface. Note
that this method is static, which means that you must call this method directly on the
Type itself. The
bo ldfaced portion of the following code calls the
isInterface function to determine whether

Department.IEmployee is an interface:
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>

</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“Department”);
Department.IEmployee = function Department$IEmployee()
{
throw Error.notImplemented();
};

function Department$IEmployee$get_employeeID ()
{
throw Error.notImplemented();
};

function Department$IEmployee$set_employeeID ()
{
throw Error.notImplemented();
};

Department.IEmployee.prototype =
{
get_employeeID : Department$IEmployee$get_employeeID,
set_employeeID: Department$IEmployee$set_employeeID
}

Department.IEmployee.registerInterface(“Department.IEmployee”);


Department.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Department.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;},
(continued)
c04.indd 95c04.indd 95 8/20/07 6:00:37 PM8/20/07 6:00:37 PM
Chapter 4: JavaScript Object-Oriented Programming
96
get_employeeID : function () {return this._employeeID;},
set_employeeID : function (value) {this._employeeID = value;}
}

Department.Employee.registerClass(“Department.Employee”, null,
Department.IEmployee);
var isInterface = Type.isInterface(Department.IEmployee);
alert(isInterface);
</script>
</form>
</body>
</html>
Listing 4-10 contains the internal implementation of isInterface . isInterface simply returns the
value of the

_interface Boolean field discussed earlier. This is yet another example of the .NET-like
type reflection capabilities of the ASP.NET AJAX client-side framework.
Listing 4-10: The Internal Implementation of is Interface
Type.isInterface = function(a)
{
if(typeof a === “undefined” || a === null)
return false;
return ! ! a.__interface;
};
Inheritance
One of the main characteristics of any OOP language is support for the class inheritance. The ASP.NET
AJAX client-side framework extends JavaScript to add support for this all-important feature. As an
example, Listing 4-11 implements a new class named
Department.Manager that inherits the

Department.Employee class.
Listing 4-11: A Page that uses Inheritance
<%@ Page Language=”C#” %>
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />

<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“Department”);
Department.IEmployee = function Department$IEmployee()
{
throw Error.notImplemented();

};

c04.indd 96c04.indd 96 8/20/07 6:00:38 PM8/20/07 6:00:38 PM
Chapter 4: JavaScript Object-Oriented Programming
97
function Department$IEmployee$get_employeeID ()
{
throw Error.notImplemented();
};

function Department$IEmployee$set_employeeID ()
{
throw Error.notImplemented();
};

Department.IEmployee.prototype =
{
get_employeeID : Department$IEmployee$get_employeeID,
set_employeeID: Department$IEmployee$set_employeeID
}

Department.IEmployee.registerInterface(“Department.IEmployee”);

Department.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Department.Employee.prototype =

{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;},
get_employeeID : function () {return this._employeeID;},
set_employeeID : function (value) {this._employeeID = value;}
}

Department.Employee.registerClass(“Department.Employee”, null,
Department.IEmployee);

Department.Manager = function (firstName, lastName, department)
{
Department.Manager.initializeBase(this,[firstName,lastName]);
this._department = department;
};

Department.Manager.prototype =
{
get_department : function () {return this._department;},
set_department : function (value) {this._department = value;}
};

Department.Manager.registerClass(“Department.Manager”, Department.Employee);

var mgr = new Department.Manager(“SomeFirstName”, “SomeLastName”,
“SomeDepartment”);
var str = ”First Name: “ + mgr.get_firstName() + “\n”;
(continued)

c04.indd 97c04.indd 97 8/20/07 6:00:38 PM8/20/07 6:00:38 PM
Chapter 4: JavaScript Object-Oriented Programming
98
Listing 4-11 (continued)
str += (“Last Name: “ + mgr.get_lastName() + “\n”);
str += (“Department: “ + mgr.get_department() + “\n”);
alert(str);
</script>
</form>
</body>
</html>
The first order of business is to define the new Manager class or the new Manager constructor as shown
in Listing 4-12 .
Listing 4-12: The Manager Constructor in JavaScript
Department.Manager = function (firstName, lastName, department)
{
Department.Manager.initializeBase(this,[firstName,lastName]);
this._department = department;
};
Note that the Manager constructor first calls a method named initializeBase . To understand the role
of this method, take a look at the
Manager constructor in an OOP language such as C#, as shown in
Listing 4-13 .
Listing 4-13: The Manager Constructor in C#
public Manager(string firstName, string lastName:base(firstName, lastName),
string department)
{
this._department = department;
}
The Manager constructor uses the boldfaced syntax shown in Listing 4-13 to call the constructor of its

base class — that is, the
Employee class. The Manager constructor in the ASP.NET AJAX client-side
framework, on the other hand, uses the boldfaced syntax shown in Listing 4-12 to achieve the same
goal — that is, to call the constructor of the base class. Therefore, calling the
initializeBase method is
equivalent to calling the
base syntax shown in Listing 4-13 . I’ll discuss the internal implementation of
the
initializeBase method later in this chapter. For now, suffice it to say that the constructor of a base
class must first call the
initializeBase method.
As mentioned, the first order of business in subclassing an existing class such as the
Department
.Employee
class is to define the constructor of the subclass (see Listing 4-12 ). The next order of business
is to register the subclass with the ASP.NET AJAX client-side framework, like this:
Department.Manager.registerClass(“Department.Manager”, Department.Employee);
Note that you must pass the base class itself as the second parameter of the registerClass method.
This tells the ASP.NET AJAX client-side framework that the class being registered is a subclass of the
specified class.
c04.indd 98c04.indd 98 8/20/07 6:00:38 PM8/20/07 6:00:38 PM
Chapter 4: JavaScript Object-Oriented Programming
99
Because the Department.Manager class derives from the Department.Employee class, it inherits the

get_firstName , set_firstName , get_lastName , and set_lastName methods from its base class. You
can now instantiate an instance of the
Department.Manager class and call these four methods on the
instance even though the class itself does not directly contain these four methods:
var mgr = new Department.Manager(“SomeFirstName”, “SomeLastName”,

“SomeDepartment”);
var str = ”First Name: “ + mgr.get_firstName() + “\n”;
str += (“Last Name: “ + mgr.get_lastName() + “\n”);
str += (“Department: “ + mgr.get_department() + “\n”);
alert(str);
Listings 4 -3 and 4 -8 presented portions of the internal implementation of the registerClass method.
Listing 4 -14 presents the complete code for this method.
Listing 4 -14: A Portion of the Internal Implementation of the registerClass Function
Type.prototype.registerClass = function(c, b, d)
{
this.prototype.constructor = this;
this.__typeName = c;
this.__class = true;
if(b)
{
this.__baseType = b;
this.__basePrototypePending = true;
}

if(!window.__classes)
window.__classes = [];

window.__classes[c.toUpperCase()] = this;
if(d)
{
this.__interfaces = [];
for(var a = 2; a < arguments.length; a ++ )
{
var e = arguments[a];
this.resolveInheritance();

for (var methodName in interfaceType.prototype)
{
var method = interfaceType.prototype[methodName];
if (!this.prototype[methodName])
{
this.prototype[methodName] = method;
}
}
this.__interfaces.push(e);
}
}
window.__registeredTypes[c] = true;
return this;
};
c04.indd 99c04.indd 99 8/20/07 6:00:39 PM8/20/07 6:00:39 PM
Chapter 4: JavaScript Object-Oriented Programming
100
The highlighted portion of this code takes the following steps:
1. It assigns the base class to a field named _baseType . Think of this field as .NET-like metadata,
which allows you to query a type for its base type.
this.__baseType = b;
2. It sets a Boolean field named _basePrototypePending to true . I’ll discuss the significance of
this field in the next section.
get BaseType
The getBaseType method enables you to access the _baseType metadata of a specified type.
This metadata references the base type of the type. Listing 4-15 presents a page that uses the

getBaseType method.
Listing 4-15: A Page that uses the get BaseType Method
<%@ Page Language=”C#” %>

<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />

<script language=”JavaScript” type=”text/javascript”>
Type.registerNamespace(“Department”);
Department.IEmployee = function Department$IEmployee()
{
throw Error.notImplemented();
};

function Department$IEmployee$get_employeeID ()
{
throw Error.notImplemented();
};

function Department$IEmployee$set_employeeID ()
{
throw Error.notImplemented();
};

Department.IEmployee.prototype =
{
get_employeeID : Department$IEmployee$get_employeeID,
c04.indd 100c04.indd 100 8/20/07 6:00:39 PM8/20/07 6:00:39 PM
Chapter 4: JavaScript Object-Oriented Programming
101

set_employeeID: Department$IEmployee$set_employeeID
}

Department.IEmployee.registerInterface(“Department.IEmployee”);

Department.Employee = function (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}

Department.Employee.prototype =
{
get_firstName : function () {return this._firstName;},
set_firstName : function (value) {this._firstName = value;},
get_lastName : function() {return this._lastName;},
set_lastName : function (value) {this._lastName = value;},
get_employeeID : function () {return this._employeeID;},
set_employeeID : function (value) {this._employeeID = value;}
}

Department.Employee.registerClass(“Department.Employee”, null,
Department.IEmployee);

Department.Manager = function (firstName, lastName, department)
{
Department.Manager.initializeBase(this,[firstName,lastName]);
this._department = department;
};


Department.Manager.prototype =
{
get_department : function () {return this._department;},
set_department : function (value) {this._department = value;}
};

Department.Manager.registerClass(“Department.Manager”, Department.Employee);
alert(Department.Manager.getBaseType());
</script>
</form>
</body>
</html>
Note that the line of code in the pageLoad method calls the alert method to display the result of the
call into the
getBaseType method of the Manager class:
alert(Department.Manager.getBaseType());
If you run this code, you’ll get the pop-up message shown in Figure 4-1 .
c04.indd 101c04.indd 101 8/20/07 6:00:39 PM8/20/07 6:00:39 PM

×