DCSIMG
ASP.NET AJAX - understanding the Type type - Pini Dayan

Pini Dayan

The best thing about a boolean is even if you are wrong, you are only off by a bit.

ASP.NET AJAX - understanding the Type type

Today , I started to debug some page that uses ASP.NET Ajax. I decided that instead of writing my OO (Object Oriented) using simple JavaScript as I often do , i will use the built in functionality from ASP.NET Ajax ( The client half of it). So I debugged some page and they using the script document ( you can see it in the solution explorer when debugging) i checked the "Type" type that I need to use to write my classes, enums and doing some JavaScript reflection.

Here is the mentioned widow:as you can see it shows the JavaScript that are being used for the current page.

image

So looking at the code of one of those script (The  "MicrosoftAjax.debug.js" file that comes with the "System.Web.Extentions" assembly) one can see that the definition of the "Type" type is declared as follow:

if (!window) this.window = this;
window.Type = Function;

So basically "Type" is nothing more than an alias to the Function object which is a built in object in JavaScript ( and contains several methods of it's own like apply , call etc).The only reason Microsoft did this alias is so that it will be more .NET framework like syntax.

Like every JavaScript object (String,Int, Object Date,Array etc) , The Type type has a property named "prototype" that the defines the properties and methods this type has. The Function object is no exception. What this means is that we can extend the functionality of the Type type (The Function Type) easily.  This is what Microsoft guy did exactly.Looking several lines of code under i can see the extesion that was done to the Type(Function type):

Type.prototype.getBaseMethod = function Type$getBaseMethod(instance, name) {...

Type.prototype.getBaseType = function Type$getBaseType() {....

Type.prototype.getName = function Type$getName() {......

Type.prototype.initializeBase = function Type$initializeBase(instance, baseArguments) {.....

and much much more.

Now pay attention.What this means is that every function in JavaScript(which is an object , a custom object if you like - a constructor function) will get the newly added methods we just saw.This is very powerful feature which we get by only adding the ScriptManager to our page.

If we take a look at the internal implementation of the "registerClass" function added to the "Type" type, we will notice a few interesting points:

Type.prototype.registerClass = 
function Type$registerClass(typeName, baseType, interfaceTypes) {
/// <summary locid="M:J#Type.registerClass" />
/// <param name="typeName" type="String"></param>
/// <param name="baseType" type="Type" optional="true" mayBeNull="true"></param>
/// <param name="interfaceTypes" parameterArray="true" type="Type"></param>
/// <returns type="Type"></returns>
var e = Function._validateParams(arguments, [
{name: "typeName", type: String},
{name: "baseType", type: Type, mayBeNull: true, optional: true},
{name: "interfaceTypes", type: Type, parameterArray: true}
]);
  if (e) throw e;
  if (!Type.__fullyQualifiedIdentifierRegExp.test(typeName)) 
throw Error.argument('typeName', Sys.Res.notATypeName);
var parsedName;
try {
     parsedName = eval(typeName);
    }
    catch(e) {
        throw Error.argument('typeName', Sys.Res.argumentTypeName);
    }
if (parsedName !== this) throw Error.argument('typeName', Sys.Res.badTypeName);
if (Sys.__registeredTypes[typeName]) throw Error.invalidOperati
(String.format(Sys.Res.typeRegisteredTwice, typeName));
if ((arguments.length > 1) && (typeof(baseType) === 'undefined'))
 throw Error.argumentUndefined('baseType');
if (baseType && !baseType.__class) throw Error.argument('baseType',
 Sys.Res.baseNotAClass
this.prototype.constructor = this;
    this.__typeName = typeName;
    this.__class = true;
    if (baseType) {
        this.__baseType = baseType;
        this.__basePrototypePending = true;
    }
    Sys.__upperCaseTypes[typeName.toUpperCase()] = this;
    if (interfaceTypes) {
        this.__interfaces = [];
        this.resolveInheritance();
        for (var i = 2, l = arguments.length; i < l; i++) {
            var interfaceType = arguments[i];
            if (!interfaceType.__interface) 
throw Error.argument('interfaceTypes[' + (i - 2) + ']', Sys.Res.notAnInterface);
            for (var methodName in interfaceType.prototype) {
                var method = interfaceType.prototype[methodName];
                if (!this.prototype[methodName]) {
                    this.prototype[methodName] = method;
                }
            }
            this.__interfaces.push(interfaceType);
        }
    }
    Sys.__registeredTypes[typeName] = true;
    return this;
}
  • There is a lot of code here so I will concentrate on the main thing to notice.

  • The first param is "typeName". The function uses it in the this.__typeName = typeName;  statment.This is an internal field in the this. The this will be the object on which we called this function , Like the Person object i will show soon.

  • An internal filed named __class is set to true.This is for later reflecting and checking if this type is being used as a class. (Just like in C#, it's like a metadata).

  • Sys.__registeredTypes[typeName] = true; It sets a global object with the type name we sent as a argument. Also for reflection purposes.

Here is a simple sample showing what i was writing about:

    <script>
        function Dog(name, lastName) {
            this.name = name;
            this.lastName = lastName;
            this.respondTo = function(name) {
                if (this.name == name) {
                    alert("Woof");
                }
            };
        }

        Dog.registerClass("Dog");
        var spot = new Dog("Spot", "Spoti");

        alert(spot.respondTo("Spot"));

</script>

Comments

No Comments

Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: