Implementation Details / Rationale
**********************************

The majority of the development time spent on ease.js was not hacking away at the source code. Rather, it was spent with pen and paper. Every aspect of ease.js was heavily planned from the start. Every detail was important to ensure a consistent implementation that worked, was fast and that developers would enjoy working with. Failures upfront or alterations to the design in later versions would break backwards compatibility unnecessarily and damage the reputation of the project.

When using ease.js, developers may wonder why things were implemented in the manner that they were. Perhaps they have a problem with the implementation, or just want to learn how the project works. This project was an excellent learning experience that deals very closely with the power and flexibility of prototypal programming. In an attempt to appease both parties, this appendix is provided to provide some details and rationale behind ease.js.

Class Module Design
===================

The CLASS module, which is accessible via require( 'easejs' ).Class, is the backbone of the entire project. In a class-based Object-Oriented model, as one could guess by the name, the class is the star player. When the project began, this was the only initial implementation detail. Everything else was later layered atop of it.

As such, developing the Class module took the most thought and presented the largest challenge throughout the project. Every detail of its implementation exists for a reason. Nothing was put in place because the author simply ``felt like it''. The project aims to exist as a strong, reliable standard for the development of JavaScript-based applications. If such a goal is to be attained, the feature set and implementation details would have to be strongly functional, easy to use and make sense to the Object-Oriented developer community.

The design also requires a strong understanding of Object-Oriented development. Attention was paid to the nuances that could otherwise introduce bugs or an inconsistent implementation.

Class Declaration Syntax
------------------------

Much thought was put into how a class should be declared. The chosen style serves as syntatic sugar, making the declarations appear very similar to classes in other Object-Oriented languages.

The original style was based on John Resig's blog post about a basic means of extending class-like objects (see About). That style was Class.extend() to declare a new class and Foo.extend() to extend an existing class. This implementation is still supported for creating anonymous classes. However, a means needed to be provided to create named classes. In addition, invoking extend() on an empty class seemed unnecessary.

The next incarnation made the CLASS module invokable. Anonymous classes could be defined using Class( {} ) and named classes could be defined by passing in a string as the first argument: Class( 'Foo', {} ). Classes could still be extended using the previously mentioned syntax, but that did no justice if we need to provide a class name. Therefore, the Class( 'SubFoo' ).extend( Supertype, {} ) syntax was also adopted.

JavaScript's use of curly braces to represent objects provides a very convenient means of making class definitions look like actual class definitions. By convention, the opening brace for the declaration object is on its own line, to make it look like an opening block.

    Class( 'Foo' )
        .implement( Bar )
        .extend(
    {
        'public foo': function()
        {
        }
    } );
Figure: Syntax and style of class definition

Syntax for implementing interfaces and extending classes was another consideration. The implementation shown above was chosen for a couple of reasons. Firstly, verbs were chosen in order to (a) prevent the use of reserved words and (b) to represent that the process was taking place at runtime, as the code was being executed. Unlike a language like C++ or Java, the classes are not prepared at compile-time.

Class Storage
-------------

One of the more powerful features of ease.js is how classes (and other objects, such as Interfaces) are stored. Rather than adopting its own model, the decision was instead to blend into how JavaScript already structures its data. Everything in JavaScript can be assigned to a variable, including functions. Classes are no different.

One decision was whether or not to store classes internally by name, then permit accessing it globally (wherever ease.js is available). This is how most Object-Oriented languages work. If the file in which the class is defined is available, the class can generally be referenced by name. This may seem natural to developers coming from other Object-Oriented languages. The decision was to not adopt this model.

By storing classes only in variables, we have fine control over the scope and permit the developer to adopt their own mechanism for organizing their classes. For example, if the developer wishes to use namespacing, then he/she is free to assign the class to a namespace (e.g. org.foo.my.ns.Foo = Class( {} )). More importantly, we can take advantage of the CommonJS format that ease.js was initially built for by assigning the class to module.exports. This permits require( 'filename' ) to return the class.

This method also permits defining anonymous classes (while not necessarily recommended, they have their uses just as anonymous functions do), mimic the concept of Java's inner classes and create temporary classes (see Temporary Classes). Indeed, we can do whatever scoping that JavaScript permits.

Memory Management
.................

Memory management is perhaps one of the most important considerations. Initially, ease.js encapsulated class metadata and visibility structures (see Hacking Around the Issue of Encapsulation).  However, it quickly became apparent that this method of storing data, although excellent for protecting it from being manipulated, caused what appeared to be memory leaks in long-running software. These were in fact not memory leaks, but ease.js keeping references to class data with no idea when to free them.

To solve this issue, all class data is stored within the class itself (that is, the constructor in JavaScript terms). They are stored in obscure variables that are non-enumerable and subject to change in future releases. This ensures that developers cannot rely on using them for reflection purposes or for manipulating class data during runtime. This is important, since looking at such members can give access to protected and private instance data. In the future, the names may be randomly chosen at runtime to further mitigate exploits. Until that time, developers should be aware of potential security issues.

If the globally accessible model would have been adopted (storing classes internally by class name rather than in variables), classes would not have been freed from memory when they went out of scope. This raises the memory footprint unnecessarily, especially for temporary classes. It would make sense that, after a temporary class is done being used, that the class be freed from memory.

Given this fact alone, the author firmly believes that the model that was chosen was the best choice.

Constructor Implementation
--------------------------

ease.js uses a PHP-style constructor. Rather than using the class name as the constructor, a __construct() method is used. This was chosen primarily because ease.js does not always know the name of the class. In fact, in the early stages of development, named classes were unsupported. With the PHP-style constructor, the class name does not need to be known, allowing constructors to be written for anonymous and named classes alike.

In addition, the PHP-style constructor is consistent between class definitions. To look up a constructor, one need only search for ``__construct'', rather than the class name. This makes certain operations, such as global searching (using grep or any other utility), much simpler.

One difference from PHP is the means of preventing instantiation. In PHP, if the constructor is declared as non-public, then an error will be raised when the developer attempts to instantiate the class. ease.js did not go this route, as the method seems cryptic. Instead, an exception should be thrown in the constructor if the developer doesn't wish the class to be instantiated. In the future, a common method may be added for consistency/convenience.

The constructor is optional. If one is not provided, nothing is done after the class is instantiated (aside from the internal ease.js initialization tasks).

The constructor is called after all initialization tasks have been completed.

Static Implementation
---------------------

The decisions behind ease.js's static implementation were very difficult. More thought and time was spent on paper designing how the static implementation should be represented than most other features in the project. The reason for this is not because the concept of static members is complicated. Rather, it is due to limitations of pre-ECMAScript 5 engines.

How Static Members Are Supposed To Work
.......................................

The first insight into the problems a static implementation would present was the concept itself. Take any common Object-Oriented language such as C++, Java, or even PHP. Static members are inherited by subtypes by reference. What does this mean? Consider two classes: FOO and SUBFOO, the latter of which inherits from the former. FOO defines a static property COUNT to be incremented each time the class is instantiated. The subtype SUBFOO, when instantiated (assuming the constructor is not overridden), would increment that very same count. Therefore, we can represent this by stating that Foo.count === SubFoo.count. In the example below, we demonstrate this concept in pseudocode:

    let Foo = Class
        public static count = 0
    let SubFoo extend from Foo

    Foo.count = 5
    SubFoo.count === 5 // true
    SubFoo.count = 6
    Foo.count === 6 // true
Figure: Representing static properties in pseudocode

As you may imagine, this is a problem. The above example does not look very JS-like. That is because it isn't. JS does not provide a means for variables to share references to the same primitive. In fact, even Objects are passed by value in the sense that, if the variable is reassigned, the other variable remains unaffected. The concept we are looking to support is similar to a pointer in C/C++, or a reference in PHP.

We have no such luxury.

Emulating References
....................

Fortunately, ECMAScript 5 provides a means to emulate references -- getters and setters. Taking a look at f:static-ref-pseudocode, we can clearly see that FOO and SUBFOO are completely separate objects. They do not share any values by references. We shouldn't share primitives by reference even if we wanted to. This issue can be resolved by using getters/setters on SUBFOO and forwarding gets/sets to the supertype:

    var obj1 = { val: 1 },
        obj2 = {
            get val()
            {
                return obj1.val;
            },

            set val( value )
            {
                obj1.val = value;
            },
        }
    ;

    obj2.val; // 1
    obj2.val = 5;
    obj1.val; // 5

    obj1.val = 6;
    obj2.val // 6
Figure: Emulating references with getters/setters (proxy)

This comes with considerable overhead when compared to accessing the properties directly (in fact, at the time of writing this, V8 doesn't even attempt to optimize calls to getters/setters, so it is even slower than invoking accessor methods). That point aside, it works well and accomplishes what we need it to.

There's just one problem. This does not work in pre-ES5 environments! ease.js needs to support older environments, falling back to ensure that everything operates the same (even though features such as visibility aren't present).

This means that we cannot use this proxy implementation. It is used for visibility in class instances, but that is because a fallback is possible. It is not possible to provide a fallback that works with two separate objects. If there were, we wouldn't have this problem in the first place.

Deciding On a Compromise
........................

A number of options were available regarding how static properties should be implemented. Methods are not a problem -- they are only accessed by reference, never written to. Therefore, they can keep their convenient Foo.method() syntax. Unfortunately, that cannot be the case for properties without the ability to implement a proxy through the use of getters/setters (which, as aforementioned, requires the services of ECMAScript 5, which is not available in older environments).

The choices were has follows:

 1. Add another object to be shared between classes (e.g. Foo.$).

 2. Do not inherit by reference. Each subtype would have their own distinct value.

 3. Access properties via an accessor method (e.g. Foo.$('var')), allowing us to properly proxy much like a getter/setter.

There are problems with all of the above options. The first option, which involves sharing an object, would cause awkward inheritance in the case of a fallback. Subtypes would set their static properties on the object, which would make that property available to the supertype! That is tolerable in the case of a fallback. However, the real problem lies in two other concepts: when a class has two subtypes that attempt to define a property with the same name, or when a subtype attempts to override a property. The former would cause both subtypes (which are entirely separate from one-another, with the exception of sharing the same parent) to share the same values, which is unacceptable. The latter case can be circumvented by simply preventing overriding of static properties, but the former just blows this idea out of the water entirely.

The second option is to not inherit by reference. This was the initial implementation (due to JavaScript limitations) until it was realized that this caused far too many inconsistencies between other Object-Oriented languages. There is no use in introducing a different implementation when we are attempting to mirror classic Object-Oriented principals to present a familiar paradigm to developers. Given this inconsistency alone, this option simply will not work.

The final option is to provide an accessor method, much like the style of jQuery. This would serve as an ugly alternative for getters/setters. It would operate as follows:

    // external
    Foo.$('var'); // getter
    Foo.$( 'var, 'foo' ); // setter

    // internal
    this.__self.$('var'); // getter
    this.__self.$( 'var', 'foo' ); // setter
Figure: Accessor implementation for static properties

Obviously, this is highly inconsistent with the rest of the framework, which permits accessing properties in the conventional manner. However, this implementation does provide a number key benefits:

  * It provides an implementation that is consistent with other Object-Oriented languages. This is the most important point.

  * The accessor method parameter style is common in other frameworks like jQuery.

  * The method name ($) is commonly used to denote a variable in scripting languages (such as PHP and shells, or to denote a scalar in Perl).

  * It works consistently in ES5 and pre-ES5 environments alike.

So, although the syntax is inconsistent with the rest of the framework, it does address all of our key requirements. This makes it a viable option for our implementation.

Appeasing ES5-Only Developers
.............................

There is another argument to be had. ease.js is designed to operate across all major browsers for all major versions, no matter how ridiculous (e.g. Internet Explorer 5.5), so long as it does not require unreasonable development effort. That is great and all, but what about those developers who are developing only for an ECMAScript 5 environment? This includes developers leveraging modern HTML 5 features and those using Node.js who do not intend to share code with pre-ES5 clients. Why should they suffer from an ugly, unnecessary syntax when a beautiful, natural [and elegant] implementation is available using proxies via getters/setters?

There are certainly two sides to this argument. On one hand, it is perfectly acceptable to request a natural syntax if it is supported. On the other hand, this introduces a number of problems:

  * This may make libraries written using ease.js unportable (to older environments). If written using an ES5-only syntax, they would have no way to fall back for static properties.

  * The syntax differences could be very confusing, especially to those beginning to learn ease.js. They may not clearly understand the differences, or may go to use a library in their own code, and find that things do not work as intended. Code examples would also have to make clear note of what static syntax they decided to use. It adds a layer of complexity.

Now, those arguing for the cleaner syntax can also argue that all newer environments moving forward will support the clean, ES5-only syntax, therefore it would be beneficial to have. Especially when used for web applications that can fall back to an entirely different implementation or refuse service entirely to older browsers. Why hold ease.js back for those stragglers if there's no intent on ever supporting them?

Both arguments are solid. Ultimately, ease.js will likely favor the argument of implementing the cleaner syntax by providing a runtime flag. If enabled, static members will be set using proxies. If not, it will fall back to the uglier implementation using the accessor method. If the environment doesn't support the flag when set, ease.js will throw an error and refuse to run, or will invoke a fallback specified by the developer to run an alternative code base that uses the portable, pre-ES5 syntax.

This decision will ultimately be made in the future. For the time being, ease.js will support and encourage use of the portable static property syntax.

Visibility Implementation
=========================

One of the major distinguishing factors of ease.js is its full visibility support (see Access Modifiers). This feature was the main motivator behind the project. Before we can understand the use of this feature, we have to understand certain limitations of JavaScript and how we may be able to work around them.

Encapsulation In JavaScript
---------------------------

Encapsulation is a cornerstone of many strong software development paradigms (see Encapsulation). This concept is relatively simply to achieve using closures in JavaScript, as shown in the following example stack implementation:

var stack = {};

( function( exports )
{
    var data = [];

    exports.push = function( data )
    {
        data.push( data );
    };

    exports.pop = function()
    {
        return data.pop();
    };
} )( stack );

stack.push( 'foo' );
stack.pop(); // foo
Figure: Encapsulation example using closures in JavaScript

Because functions introduce scope in JavaScript, data can be hidden within them. In f:js-encapsulation-ex above, a self-executing function is used to encapsulate the actual data in the stack (DATA). The function accepts a single argument, which will hold the functions used to push and pop values to/from the stack respectively. These functions are closures that have access to the DATA variable, allowing them to alter its data. However, nothing outside of the self-executing function has access to the data. Therefore, we present the user with an API that allows them to push/pop from the stack, but never allows them to see what data is actually in the stack (The pattern used in the stack implementation is commonly referred to as the module pattern and is the same concept used by CommonJS. Another common implementation is to return an object containing the functions from the self-executing function, rather than accepting an object to store the values in. We used the former implementation here for the sake of clarity and because it more closely represents the syntax used by CommonJS.).

Let's translate some of the above into Object-Oriented terms:

  * PUSH and POP are public members of STACK.

  * DATA is a private member of STACK.

  * STACK is a Singleton.

We can take this a bit further by defining a Stack prototype so that we can create multiple instances of our stack implementation. A single instance hardly seems useful for reuse. However, in attempting to do so, we run into a bit of a problem:

var Stack = function()
{
    this._data = [];
};

Stack.prototype = {
    push: function( val )
    {
        this._data.push( val );
    },

    pop: function()
    {
        return this._data.pop();
    },
};

// create a new instance of our Stack object
var inst = new Stack();

// what's this?
inst.push( 'foo' );
console.log( inst._data ); // [ 'foo' ]

// uh oh.
inst.pop(); // foo
console.log( inst._data ); // []
Figure: Working easily with instance members in JavaScript breaks
encapsulation

By defining our methods on the prototype and our data in the constructor, we have created a bit of a problem. Although the data is easy to work with, it is no longer encapsulated. The _DATA property is now public, accessible for the entire work to inspect and modify. As such, a common practice in JavaScript is to simply declare members that are "supposed to be" private with an underscore prefix, as we have done above, and then trust that nobody will make use of them. Not a great solution.

Another solution is to use a concept called privileged members, which uses closures defined in the constructor rather than functions defined in the prototype:

var Stack = function()
{
    var data = [];

    this.push = function( data )
    {
        data.push( data );
    };

    this.pop = function()
    {
        return data.pop();
    };
};

// create a new instance of our Stack object
var inst = new Stack();

// can no longer access "privileged" member _data
inst.push( 'foo' );
console.log( inst._data ); // undefined
Figure: Privileged members in JavaScript

You may notice a strong similarity between f:js-encapsulation-ex and f:js-privileged-members. They are doing essentially the same thing, the only difference being that f:js-encapsulation-ex is returning a single object and f:js-privileged-members represents a constructor that may be instantiated.

When using privileged members, one would define all members that need access to such members in the constructor and define all remaining members in the prototype. However, this introduces a rather large problem that makes this design decision a poor one in practice: Each time STACK is instantiated, PUSH and POP have to be redefined, taking up additional memory and CPU cycles. Those methods will be kept in memory until the instance of STACK is garbage collected.

In f:js-privileged-members, these considerations may not seem like much of an issue. However, consider a constructor that defines tens of methods and could potentially have hundreds of instances. For this reason, you will often see the concepts demonstrated in f:js-proto-inst-noencapsulate used more frequently in libraries that have even modest performance requirements.

Hacking Around the Issue of Encapsulation
-----------------------------------------

Since neither f:js-encapsulation-ex nor f:js-privileged-members are acceptable implementations for strong Classical Object-Oriented code, another solution is needed. Based on what we have seen thus far, let's consider our requirements:

  * Our implementation must not break encapsulation. That is - we should be enforcing encapsulation, not simply trusting our users not to touch.

  * We must be gentle with our memory allocations and processing. This means placing all methods within the prototype.

  * We should not require any changes to how the developer uses the constructor/object. It should operate just like any other construct in JavaScript.

We can accomplish the above by using the encapsulation concepts from f:js-encapsulation-ex and the same prototype model demonstrated in f:js-proto-inst-noencapsulate. The problem with f:js-encapsulation-ex, which provided proper encapsulation, was that it acted as a Singleton. We could not create multiple instances of it and, even if we could, they would end up sharing the same data. To solve this problem, we need a means of distinguishing between each of the instances so that we can access the data of each separately:

var Stack = ( function()
{
    var idata = [],
        iid   = 0;

    var S = function()
    {
        // set the instance id of this instance, then increment it to ensure the
        // value is unique for the next instance
        this.__iid = iid++;

        // initialize our data for this instance
        idata[ this.__iid ] = {
            stack: [],
        };
    }:

    S.prototype = {
        push: function( val )
        {
            idata[ this.__iid ].stack.push( val );
        },

        pop: function()
        {
            return idata[ this.__iid ].stack.pop();
        }
    };

    return S;
} )();

var stack1 = new Stack();
var stack2 = new Stack();

stack1.push( 'foo' );
stack2.push( 'bar' );

stack1.pop(); // foo
stack2.pop(); // bar
Figure: Encapsulating data per instance

This would seem to accomplish each of our above goals. Our implementation does not break encapsulation, as nobody can get at the data. Our methods are part of the STACK prototype, so we are not redefining it with each instance, eliminating our memory and processing issues. Finally, STACK instances can be instantiated and used just like any other object in JavaScript; the developer needn't adhere to any obscure standards in order to emulate encapsulation.

Excellent! However, our implementation does introduce a number of issues that we hadn't previously considered:

  * Our implementation is hardly concise. Working with our ``private'' properties requires that we add ugly instance lookup code (We could encapsulate this lookup code, but we would then have the overhead of an additional method call with very little benefit; we cannot do something like: this.stack.), obscuring the actual domain logic.

  * Most importantly: this implementation introduces memory leaks.

What do we mean by ``memory leaks''? Consider the usage example in f:js-encapsulate-instance. What happens when were are done using STACK1 and STACK2 and they fall out of scope? They will be GC'd. However, take a look at our IDATA variable. The garbage collector will not know to free up the data for our particular instance. Indeed, it cannot, because we are still holding a reference to that data as a member of the IDATA array.

Now imagine that we have a long-running piece of software that makes heavy use of STACK. This software will use thousands of instances throughout its life, but they are used only briefly and then discarded. Let us also imagine that the stacks are very large, perhaps holding hundreds of elements, and that we do not necessarily pop() every element off of the stack before we discard it.

Imagine that we examine the memory usage throughout the life of this software. Each time a stack is used, additional memory will be allocated. Each time we push() an element onto the stack, additional memory is allocated for that element. Because our IDATA structure is not freed when the STACK instance goes out of scope, we will see the memory continue to rise. The memory would not drop until STACK itself falls out of scope, which may not be until the user navigates away from the page.

From our perspective, this is not a memory leak. Our implementation is working exactly as it was developer. However, to the user of our stack implementation, this memory management is out of their control. From their perspective, this is indeed a memory leak that could have terrible consequences on their software.

This method of storing instance data was ease.js's initial ``proof-of-concept'' implementation (see Class Storage). Clearly, this was not going to work; some changes to this implementation were needed.

Instance Memory Considerations
..............................

JavaScript does not provide destructors to let us know when an instance is about to be GC'd, so we unfortunately cannot know when to free instance data from memory in f:js-encapsulate-instance. We are also not provided with an API that can return the reference count for a given object. We could provide a method that the user could call when they were done with the object, but that is not natural to a JavaScript developer and they could easily forget to call the method.

As such, it seems that the only solution for this rather large issue is to store instance data on the instance itself so that it will be freed with the instance when it is garbage collected (remember, we decided that privileged members were not an option in the discussion of f:js-privileged-members). Hold on - we already did that in f:js-proto-inst-noencapsulate; that caused our data to be available publicly. How do we approach this situation?

If we are adding data to an instance itself, there is no way to prevent it from being accessed in some manner, making true encapsulation impossible. The only options are to obscure it as best as possible, to make it too difficult to access in any sane implementation. For example:

  * The property storing the private data could be made non-enumerable, requiring the use of a debugger or looking at the source code to determine the object name.

       * This would work only with ECMAScript 5 and later environments.

  * We could store all private data in an obscure property name, such as ___$$PRIV$$___, which would make it clear that it should not be accessed.

       * We could take that a step further and randomize the name, making it very difficult to discover at runtime, especially if it were non-enumerable (Note that ease.js does not currently randomize its visibility object name.).

Regardless, it is clear that our data will only be ``encapsulated'' in the sense that it will not be available conveniently via a public API. Let's take a look at how something like that may work:

var Stack = ( function()
{
    // implementation of getSomeRandomName() is left up to the reader
    var _privname = getSomeRandomName();

    var S = function()
    {
        // define a non-enumerable property to store our private data (will only
        // work in ES5+ environments)
        Object.defineProperty( this, _privname, {
            enumerable:   false,
            writable:     false,
            configurable: false,

            value: {
                stack: []
            }
        } );
    };

    S.prototype = {
        push: function( val )
        {
            this[ _privname ].stack.push( val );
        },

        pop: function()
        {
            return this[ _privname ].stack.pop();
        },
    };

    return S;
} );

var inst = new Stack();
inst.push( 'foo' );
inst.pop(); // foo
Figure: Using a random, non-enumerable property name to store private members

Now we are really starting to hack around what JavaScript provides for us. We seem to be combining the encapsulation issues presented in f:js-proto-inst-noencapsulate and the obscurity demonstrated in f:js-encapsulate-instance. In addition, we our implementation depends on ECMAScript 5 (ideally, we would detect that and fall back to normal, enumerable properties in pre-ES5 environments, which ease.js does indeed do). This seems to be a case of encapsulation through obscurity (A play on ``security through obscurity''.). While our implementation certainly makes it difficult to get at the private member data, it is also very obscure and inconvenient to work with. Who wants to write Object-Oriented code like that?

Other Considerations
....................

We have conveniently omitted a number of other important factors in our discussion thus far. Before continuing, they deserve some mention and careful consideration.

How would we implement private methods? We could add them to our private member object, just as we defined STACK in f:js-obscure-private, but that would cause it to be redefined with each instance, raising the same issues that were discussed with f:js-privileged-members. Therefore, we would have to define them in a separate ``prototype'', if you will, that only we have access to:

var Stack = ( function()
{
    // implementation of getSomeRandomName() is left up to the reader
    var _privname = getSomeRandomName();

    var S = function()
    {
        // define a non-enumerable property to store our private data (will only
        // work in ES5+ environments)
        Object.defineProperty( this, _privname, {
            // ... (see previous example)
        } );
    };

    // private methods that only we will have access to
    var priv_methods = {
        getStack: function()
        {
            // note that, in order for 'this' to be bound to our instance, it
            // must be passed as first argument to call() or apply()
            return this[ _privname ].stack;
        },
    };

    // public methods
    S.prototype = {
        push: function( val )
        {
            var stack = priv_methods.getStack.call( this );
            stack.push( val );
        },

        pop: function()
        {
            var stack = priv_methods.getStack.call( this );
            return stack.pop();
        },
    };

    return S;
} );

var inst = new Stack();
inst.push( 'foo' );
inst.pop(); // foo
Figure: A possible private method implementation

While this does solve our problem, it further reduces code clarity. The implementation in f:js-obscure-private-methods is certainly a far cry from something like this._getStack(), which is all you would need to do in ease.js.

Another consideration is a protected (see Access Modifiers) member implementation, the idea being that subtypes should inherit both public and protected members. Inheritance is not something that we had to worry about with private members, so this adds an entirely new layer of complexity to the implementation. This would mean somehow making a protected prototype available to subtypes through the public prototype. Given our implementation in the previous figures, this would likely mean an awkward call that somewhat resembles: this[ _protname ].name.

Although the implementations show in f:js-obscure-private and f:js-obscure-private-methods represent a creative hack, this is precisely one of the reasons ease.js was created - to encapsulate such atrocities that would make code that is difficult to use, hard to maintain and easy to introduce bugs. One shouldn't have to have a deep understanding of JavaScript's prototype model in order to write the most elementary of Classical Object-Oriented code. For example, the constructors in the aforementioned figures directly set up an object in which to store private members. ease.js will do this for you before calling the __construct() method. Furthermore, ease.js does not require referencing that object directly, like we must do in our methods in f:js-obscure-private. Nor does ease.js have an awkward syntax for invoking private methods. We will explore how this is handled in the following section.

The Visibility Object
---------------------

Let's consider how we may rewrite STACK in f:js-obscure-private-methods using ease.js:

var Stack = Class( 'Stack',
{
    'private _data': [],

    'public push': function( val )
    {
        this._data.push( val );
    },

    'public pop': function()
    {
        return this._data.pop();
    }
} );

var inst = Stack();
inst.push( 'foo' );
inst.pop(); // foo
Figure: Stack implementation using ease.js

The above implementation is much less impressive looking than our prior examples. What we have done is encapsulate the excess logic needed to emulate a class and got right down to business. ease.js will take the class definition above and generate an object much like we had done in the prior examples, with a few improvements.

If you have not read over the previous sections, you are recommended to do so before continuing in order to better understand the rationale and finer implementation details.

The secret behind ease.js's visibility implementation (see Access Modifiers) is referred to internally as the visibility object (or, in older commits and some notes, the property object). Consider the problem regarding the verbosity of our private property accessors and method calls in f:js-obscure-private-methods. It would be much more convenient if the properties and methods were bound to THIS so that they can be accessed more naturally, as would be expected by a programmer familiar with classes in other Classical Object-Oriented languages (see f:stack-using-easejs). This can be done using call() or apply():

function getName()
{
    return this.name;
}

var obj = { name: "foo" };
getName.call( obj ); // "foo"
Figure: Calling a function within the context of a given object

f:function-context demonstrates the concept we are referring to. Given an arbitrary object OBJ, we can call any given method (in this case, getName(), binding THIS to that object. This is precisely what ease.js does with each method call. To understand this process, we have to explore two concepts: the visibility object itself and method wrapping. We will start by discussing the visibility object in more detail and cover method wrapping later on (see Method Wrapping).

Visibility Object Implementation
................................

The visibility object is mostly simply represented in the following diagram:

Figure: Structure of the visibility object

Specifically, the visibility object is a prototype chain containing the private members of the class associated with the method currently being invoked on the current instance, its protected members (including those inherited from its supertype) and the public members (also including those inherited from the supertype). To accomplish this, the visibility object has the following properties:

  * The private object is swappable - that is, it is the only portion of the prototype chain that is replaced between calls to various methods.

       * It is for this reason that the private object is placed atop the prototype chain. This allows it to be swapped very cheaply by simply passing different objects to be bound to this.

  * Both the private and protected objects are initialized during instantiation by the __initProps() method attached by ClassBuilder to each class during definition.

       * Properties are cloned to ensure references are not shared between instances.

       * Methods are copied by reference, since their implementations are immutable.

       * This must be done because neither protected nor private members may be added to the prototype chain of a class.

            * Doing so would effectively make them public.

            * Doing so would also cause private members to be inherited by subtypes.

  * Public members are a part of the class prototype chain as you would expect in any conventional prototype.

       * Public properties only are initialized by __initProps(), just as private and protected properties, to ensure that no references are shared between instances.

As a consequence of the above, it is then clear that there must be a separate visibility object (prototype chain) for each supertype of each instance, because there must be a separate private object for each subtype of each instance. Let us consider for a moment why this is necessary with the following sample of code:

var C1 = Class(
    {
        'private _name': 'Foo',

        'public getName': function()
        {
            return this._name;
        },

        // ...
    } ),

    // note the naming convention using descending ids for the discussion
    // following this example
    C0 = C1.extend(
    {
        // ...
    } );

C1().getName(); // "Foo"
C0().getName(); // "Foo"
Figure: Why private member swapping is necessary

f:priv-swap-rationale demonstrates why the private object swapping (The term ``swapping'' can be a bit deceptive. While we are swapping in the sense that we are passing an entirely new private object as the context to a method, we are not removing an object from the prototype chain and adding another in place of it. They do, however, share the same prototype chain.) is indeed necessary. If a subtype does not override a super method that uses a private member, it is important that the private member be accessible to the method when it is called. In f:priv-swap-rationale, if we did not swap out the object, _NAME would be undefined when invoked on C2.

Given this new information, the implementation would more formally be represented as a collection of objects V for class C and each of its supertypes as denoted by C\_N, with C\_0 representing the class having been instantiated and any integer N >= 0 representing the closest supertype, such that each V\_N is associated with C\_N, V\_N\^X is the visibility object bound to any method associated with class C\_X and each V shares the same prototype chain P\_N for any given instance of C\_N:

Figure: Collection of visibility objects V for each class C

Fortunately, as shown in f:visobj-collection, the majority of the prototype chain can be reused under various circumstances:

  * For each instance of class C\_N, P\_N is re-used as the prototype of every V\_N.

  * C\_N is re-used as the prototype for each P\_N.

Consequently, on instantiation of class C\_N, we incur a performance hit from __initProps() for the initialization of each member of V\_X and P\_X, as well as each property of C\_X, recursively for each value of M >= X >= N (that is, supertypes are initialized first), where M is equal to the number of supertypes of class C\_N + N. (There is room for optimization in this implementation, which will be left for future versions of ease.js.)

The instance stores a reference to each of the visibility objects V, indexed by an internal class identifier (which is simply incremented for each new class definition, much like we did with the instance id in f:js-encapsulate-instance. When a method is called, the visibility object that matches the class identifier associated with the invoked method is then passed as the context (bound to THIS) for that method (see Method Wrapping).

Property Proxies
................

Astute readers may notice that the visibility implementation described in the previous section (see Visibility Object Implementation) has one critical flaw stemming from how prototypes in JavaScript are implemented: setting a property on the visibility object bound to the method will set the property on that object, but not necessarily on its correct object. The following example will demonstrate this issue:

var pub = {
        foo:    'bar',
        method: function()
        {
            return 'baz';
        },
    },

    // what will become our visibility object
    priv = function() {}
;

// set up our visibility object's prototype chain (we're leaving the protected
// layer out of the equation)
priv.prototype = pub;

// create our visibility object
var vis = new priv();

// retrieving properties works fine, as do method invocations
vis.foo;       // "bar"
vis.method();  // "baz"

// but when it comes to setting values...
vis.foo = 'newval';

// ...we stop short
vis.foo;  // "newval"
pub.foo;  // "bar"

vis.foo = undefined;
vis.foo;  // undefined
delete vis.foo;
vis.foo;  // "bar"
pub.foo;  // "bar"

pub.foo = 'moo';
vis.foo; // "moo"
Figure: Demonstrating property set issues with prototype chains

Retrieving property values and invoking methods are not a problem. This is because values further down the prototype chain peek through ``holes'' in objects further up the chain. Since VIS in f:proto-set-issue has no value for property FOO (note that a value of undefined is still a value), it looks at its prototype, PUB, and finds the value there.

However, the story changes a bit when we try to set a value. When we assign a value to member FOO of VIS, we are in fact setting the property on VIS itself, not PUB. This fills that aforementioned ``hole'', masking the value further down the prototype chain (our value in PUB). This has the terrible consequence that if we were to set a public/protected property value from within a method, it would only be accessible from within that instance, for only that visibility object.

To summarize:

  * Methods are never an issue, as they are immutable (in the sense of a class).

  * Reading properties are never an issue; they properly ``peek'' through holes in the prototype chain.

  * Writing private values are never an issue, as they will be properly set on that visibility object. The value needn't be set on any other visibility objects, since private values are to remain exclusive to that instance within the context of that class only (it should not be available to methods of supertypes).

  * We run into issues when setting public or protected values, as they are not set on their appropriate object.

This issue is huge. Before ECMAScript 5, it may have been a show-stopper, preventing us from using a familiar this.prop syntax within classes and making the framework more of a mess than an elegant implementation. It is also likely that this is the reason that frameworks like ease.js did not yet exist; ECMAScript 5 and browsers that actually implement it are still relatively new.

Fortunately, ECMAScript 5 provides support for getters and setters. Using these, we can create a proxy from our visibility object to the appropriate members of the other layers (protected, public). Let us demonstrate this by building off of f:proto-set-issue:

// proxy vis.foo to pub.foo using getters/setters
Object.defineProperty( vis, 'foo', {
    set: function( val )
    {
        pub.foo = val;
    },

    get: function()
    {
        return pub.foo;
    },
} );

vis.foo; // "moo"
pub.foo; // "moo"

vis.foo = "bar";
vis.foo; // "bar"
pub.foo; // "bar"

pub.foo = "changed";
vis.foo; // "changed"
Figure: Using getters/setters to proxy values to the appropriate object

The implementation in f:proto-getset is precisely how ease.js implements and enforces the various levels of visibility. (One may wonder why we implemented a getter in f:proto-getset when we had no trouble retrieving the value to begin with. In defining a setter for FOO on object VIS, we filled that ``hole'', preventing us from ``seeing through'' into the prototype (PUB). Unfortunately, that means that we must use a getter in order to provide the illusion of the ``hole''.) This is both fortunate and unfortunate; the project had been saved by getters/setters, but with a slight performance penalty. In order to implement this proxy, the following must be done:

  * For each public property, proxy from the protected object to the public.

  * For each protected property, proxy from the private object to the protected. (One may also notice that we are not proxying public properties from the private member object to the public object. The reason for this is that getters/setters, being functions, are properly invoked when nestled within the prototype chain. The reader may then question why ease.js did not simply convert each property to a getter/setter, which would prevent the need for proxying. The reason for this was performance - with the current implementation, there is only a penalty for accessing public members from within an instance, for example. However, accessing public members outside of the class is as fast as normal property access. By converting all properties to getters/setters, we would cause a performance hit across the board, which is unnecessary.)

Consequently, this means that accessing public properties from within the class will be slower than accessing the property outside of the class. Furthermore, accessing a protected property will always incur a performance hit (How much of a performance hit are we talking? This will depend on environment. In the case of v8 (Node.js is used to run the performance tests currently), getters/setters are not yet optimized (converted to machine code), so they are considerably more slow than direct property access.

For example: on one system using v8, reading public properties externally took only 0.0000000060s (direct access), whereas accessing the same property internally took 0.0000001120s (through the proxy), which is a significant (18.6x) slow-down. Run that test 500,000 times, as the performance test does, and we're looking at 0.005s for direct access vs 0.056s for proxy access.), because it is always hidden behind the provide object and it cannot be accessed from outside of the class.  On the upside, accessing private members is fast (as in - ``normal'' speed). This has the benefit of encouraging proper OO practices by discouraging the use of public and protected properties. Note that methods, as they are not proxied, do not incur the same performance hit.

Given the above implementation details, it is clear that ease.js has been optimized for the most common use case, indicative of proper OO development - the access of private properties from within classes, for which there will be no performance penalty.

Method Wrapping
---------------

The visibility object (see The Visibility Object) is a useful tool for organizing the various members, but we still need some means of binding it to a method call. This is accomplished by wrapping each method in a closure that, among other things (The closure also sets the __super() method reference, if a super method exists, and returns the instance if THIS is returned from the method.), uses apply() to forward the arguments to the method, binding THIS to the appropriate visibility object. This is very similar to the ES5 Function.bind() call.

The following example demonstrates in an overly-simplistic way how ease.js handles class definitions and method wrapping. (ease.js, of course, generates its own visibility objects internally. However, for the sake of brevity, we simply provide one in our example.)

/**
 * Simple function that returns a prototype ("class"), generated from the given
 * definition and all methods bound to the provided visibility object
 */
function createClass( vis, dfn )
{
    var C      = function() {},
        hasOwn = Object.hasOwnProperty;

    for ( name in dfn )
    {
        // ignore any members that are not part of our object (further down the
        // chain)
        if ( hasOwn.call( dfn, name ) === false )
        {
            continue;
        }

        // simply property impl (WARNING: copies by ref)
        if ( typeof dfn[ name ] !== 'function' )
        {
            C.prototype[ name ] = dfn[ name ];
            continue;
        }

        // enclose name in a closure to preserve it (otherwise it'll contain the
        // name of the last member in the loop)
        C.prototype[ name ] = ( function( mname )
        {
            return function()
            {
                // call method with the given argments, bound to the given
                // visibility object
                dfn[ mname ].apply( vis, arguments );
            };
        } )( name );
    }

    return C;
};

var vis = { _data: "foo" },

    Foo = createClass( vis,
    {
        getData: function()
        {
            return this._data;
        },
    } );

var inst = new Foo();

// getData() will be bound to vis and should return its _data property
inst.getData(); // "foo"
Figure: Basic "class" implementation with method binding

There are some important considerations with the implementation in f:method-wrapping, as well as ease.js's implementation:

  * Each method call, unless optimized away by the engine, is equivalent to two function invocations, which cuts down on the available stack space.

       * The method wrapping may complicate tail call optimization, depending on the JavaScript engine's implementation and whether or not it will optimize across the stack, rather than just a single-depth recursive call.

       * As such, for operations that are highly dependent on stack space, one may wish to avoid method calls and call functions directly.

  * There is a very slight performance hit (though worrying about this is likely to be a micro-optimization in the majority of circumstances).

As mentioned previously, each visibility object is indexed by class identifier (see Visibility Object Implementation). The appropriate visibility object is bound dynamically on method invocation based on the matching class identifier. Previously in this discussion, it was not clear how this identifier was determined at runtime. Since methods are shared by reference between subtypes, we cannot store a class identifier on the function itself.

The closure that wraps the actual method references the arguments that were passed to the function that created it when the class was defined. Among these arguments are the class identifier and a lookup method used to determine the appropriate visibility object to use for binding. (See lib/MethodWrappers.js for the method wrappers and ClassBuilder.getMethodInstance() for the lookup function.) Therefore, the wrapper closure will always know the appropriate class identifier. The lookup method is also passed THIS, which is bound to the instance automatically by JavaScript for the method call. It is on this object that the visibility objects are stored (non-enumerable; see Instance Memory Considerations), indexed by class identifier. The appropriate is simply returned.

If no visibility object is found, null is returned by the lookup function, which causes the wrapper function to default to THIS as determined by JavaScript, which will be the instance that the method was invoked on, or whatever was bound to the function via a call to call() or apply(). This means that, currently, a visibility object can be explicitly specified for any method by invoking the method in the form of: inst.methodName.apply( visobj, arguments ), which is consistent with how JavaScript is commonly used with other prototypes. However, it should be noted that this behavior is undocumented and subject to change in future releases unless it is decided that this implementation is ideal. It is therefore recommended to avoid using this functionality for the time being. (One one hand, keeping this feature is excellent in the sense that it is predictable. If all other prototypes work this way, why not ``classes'' as created through ease.js? At the same time, this is not very class-like. It permits manipulating the internal state of the class, which is supposed to be encapsulated. It also allows bypassing constructor logic and replacing methods at runtime. This is useful for mocking, but a complete anti-pattern in terms of Classical Object-Oriented development.)

Internal Methods/Objects
========================

There are a number of internal methods/objects that may be useful to developers who are looking to use some features of ease.js without using the full class system. An API will be provided to many of these in the future, once refactoring is complete. Until that time, it is not recommended that you rely on any of the functionality that is not provided via the public API (index.js or the global EASEJS object).