Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Typescript

jTypes: Under the Hood

5.00/5 (2 votes)
13 Jun 2013Apache8 min read 19.5K   94  
A detailed look at how jTypes compiles C#-like object definitions into prototype-based instance matrices.

Introduction

This article provides an in-depth look at jTypes and how it compiles C#-like object definitions into prototype-based instance matrices. These matrices allow jTypes to emulate the behavior of classical inheritance by constructing several prototype-chains for each access level during instantiation. This allows developers to build application libraries in JavaScript using encapsulation, inheritance, and polymorphism.

By observing the internal mechanics of jTypes and learning how it works, developers can gain a better understanding of how jTypes can be utilized, what benefits it can offer, and what obstacles it can present. When used appropriately, jTypes can provide a robust, organized, and maintainable code base for large JavaScript application libraries.

Background

jTypes is the most comprehensive and robust JavaScript library for overcoming differential inheritance with prototype-based objects. Its lightweight yet powerful design provides web programmers on any platform or browser the ability to emulate classical inheritance where objects are defined by classes.

More information can be found at www.jTypes.com along with source code, documentation, and examples. This article will utilize the latest experimental version (2.1.0b at the time of this writing) for its debug logging to the console in the Google Chrome developer tools. The logging of compiled definition objects and instance matrices to the console is only provided in experimental versions of jTypes.  

Using the code  

jTypes takes C#-like object definitions and constructs them into a set of compiled definitions objects. These compiled definitions are then utilized during instantiation as blueprints for building an instance matrix on top of the public prototype-chain. Each class definition requires its own row in the instance matrix during instantiation, and columns are created for each possible access level in the object instance. Private instances inherit from their respective protected instances through the prototype-chain, while all other instances in the matrix inherit from their corresponding access level instances in the base class as shown in the following figure: 

To see the compiled definitions objects and instance matrices in action, both example classes from the jTypes download at www.jTypes.com will be utilized. First, a Person class will be defined using the following code: 

JavaScript
var Person = $$(function($fName, $lName, $age)
{
    this.firstName = $fName;
    this.lastName  = $lName;
 
    this._age = $age;
},
{
    'public readonly firstName': '',
    'public readonly lastName': '',
 
    'protected _age': 0,
 
    'public getFullName': function()
    {
        return this.firstName + ' ' + this.lastName;
    },
 
    'public virtual triggerOneYearOlder': function()
    {
        this._age++;
    },
 
    'public age':
    {
        'get': function()
        {
            return this._age;
        },
        'set': function($v)
        {
            if ($v > 0)
                this._age = $v;
        }
    }
}); 

A derived class, Employee, will then be defined to show more complicated definitions objects and an instance matrix with multiple rows. The following code will be used to define the class: 

JavaScript
var Employee = $$(Person, function($fName, $lName, $age, $salary)
{
    this.__base($fName, $lName, $age);
    
    this._salary = $salary;
},
{
    'protected _salary': 0,
 
    'public override triggerOneYearOlder': function()
    {
        this.__base.triggerOneYearOlder();
 
        this._salary *= 1.03;
    },
 
    'public salary':
    {
        'get': function()
        {
            return this._salary;
        }
    }
});
After these definitions are entered into the console and evaluated, the jTypes compiler will display the compiled definitions for debugging purposes. The output will contain three compiled definitions objects for the private, protected, and public access levels and will look similar to the following screenshot: 

Since both class definitions were entered and evaluated in the same script, the compiled definitions objects for the Person and Employee classes are output to the console at the same time in their respective order. Both sets of definitions objects have been labeled in the previous screenshot as well.

If these definitions objects are expanded in the developer tools, the member definitions for each access level are listed as shown in the following screenshot: 

 

After expanding the protected and public definitions objects for the Person class (and not the private definitions object since there are no private definitions in the class), each member definition is listed in alphabetical order. The protected collection has definitions for the protected field _age and a special method ~constructor. The public collection has definitions for read-only fields such as firstName and lastName, methods such as getFullName and triggerOneYearOlder, and special get/set accessor methods for the age property. Any of these definitions can then be expanded to observe the data attached to each member definition as shown in the following screenshot: 

If the definition for the protected field _age is expanded as shown in the previous screenshot, the data attached to the member definition is displayed. The property ~iZd holds reference to the member name, ~uHy to the member type, ~vne to the member value, and ~Woo holds reference to the readonly flag of the field. In the definition for the getFullName method, ~AKc, ~XSY, and ~xTZ hold references to the abstract, final, and virtual flags of the method, respectively. These data properties are unique to each instance of jTypes and are only used internally by the library. They have been output to the console in this experimental version strictly for debugging purposes.  

The protected and public definitions objects for derived classes also inherit from their respective definitions objects in the base class using a prototype-chain. This can be observed in the following screenshot: 

 

In the public definitions object of the Employee class, it can be observed that there are only two member definitions on the object. The first is a method override, triggerOneYearOlder, and the second is a get accessor for the salary property. All remaining members in the public definitions object of the Employee class are inherited through the prototype-chain. As seen in the previous screenshot, the prototype of the public definitions object in the Employee class holds reference to the public definitions object of the Person class.

These compiled definitions objects are used by jTypes to build an instance matrix on top of the class prototype-chain during instantiation. The following screenshot shows the instantiation of both a Person and Employee object using the new operator: 

The instance matrix for each instantiation is logged in the console for debugging purposes. It is stored as a two-dimensional array where the indices are the class level and access level, respectively. Since the Person object has no base class, it has only one class level in the array. The Employee object however has two class levels, since it derives from a Person object. If a class level is expanded in the developer tools, each access level instance can be observed as shown in the following screenshot: 

When the only class level in the Person instance matrix is expanded as shown in the previous screenshot, four access level instances are listed. The first reference in the array maps to the private instance of the Person object, which is passed to all methods in the Person instance as the this context. The private instance also inherits from the protected instance, which maps to the second reference in the array. The third reference maps to the public instance of the Person object, which can be accessed through the private instance as __this. Finally, the last reference in the array maps to the base instance of the Person instance, which can be accessed through the private instance in any derived class as __base. It is also worth noting that the prototype instance that the matrix is built upon can be accessed through all private instances as __self, and is consequently the only reference which can be used when uniquely referencing the object across all instances. If these access level instances are expanded in the developer tools, each member for that respective access level is listed as shown in the following screenshot:  

In the previous screenshot, the protected instances at each class level are displayed for the Employee object. It can be observed in the console that the instance matrix stores class levels using a top-down approach. Therefore, the first class level in the array maps to the Employee instances, while the second class level maps to the Person instances. Subsequently, the protected and public Employee instances inherit from their respective instances in the Person instance through the prototype-chain. This can be seen in the output where the prototype of the protected instance in the Employee object (class level zero, access level one) holds reference to the protected instance of the Person object (class level one, access level one).

By building this instance matrix on top of the public prototype-chain, jTypes effectively emulates the behavior of classical inheritance using prototype-based objects. The as method attached to each instance then provides a means of mapping a class type-reference to a class level in the instance matrix. If a matching class level is found, the public instance for that class level is returned by the method. Again, the instance matrix is only used internally by the library, and is output to the console in this experimental version of jTypes for debugging purposes only. 

Points of Interest 

A new setting, lazy, is available in the latest experimental version of jTypes. This flag is true by default, which enables differential inheritance for instance matrices during instantiation. However, if this flag is set to false, no differential inheritance will be used in the construction of an instance matrix. This can be observed in the following screenshot where the lazy flag is set to false and an Employee object is then instantiated:



By setting the lazy flag to false, a more compact instance matrix was constructed when the Employee object was instantiated. Since this instance matrix was built without differential inheritance, member accesses no longer have to inherit through the prototype-chain. It also results in a more lightweight instance matrix that requires less objects to construct. This is due to the fact that only 3 access level instances are needed when not utilizing a prototype-chain to minimize member definitions. 

However, this method of building the instance matrix requires jTypes to make a second pass on each class level, resulting in significant performance decreases during instantiation. For this reason, jTypes defaults to the alternate method of constructing the instance matrix. This method could still be utilized though in applications that normally have some form of preloading, and would benefit from the reduced instance matrix objects and lack of member inheritance through the prototype-chain.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0