Javascript In Anger
 
Support Ukraine

Javascript In Anger

Developing Bigshot was a great learning experience for me. Coming from the Java world as I was, I had my ideas about "how things should be done", and while I got a lot of things right, the inherent mismatch between the Javascript world and the Java world caused some difficulties. This article is intended as a "best practices"-document. As far as I can tell, what is written here is the way to do large-scale Javascript development. The document will change as I learn, something which I expect to do. I'll try to explain how these practices came into being, and why they are the best, by contrasting them with other ways of doing things; in most cases these "other ways" are my own false starts.

1. Object Orientation

The first step is to organize code into classes. The Javascript global namespace gets crowded fast, so the more we can get our functions and their data away from it, the better off we'll be. Object literals, free functions, and so on certainly have their place in the world (and in this article), but for organizing large chunks of reusable code, classes remain the best way.

1.1. Classes

The class itself is a constructor function.

1.1.1. Rationale

This is standard Javascript class-based Object-Orientation. See Douglas Crockford's website[a] for more about prototype-based OO in Javascript.

1.2. Methods

Methods are attached to the prototype of the class object.

1.2.1. Rationale

There are two places to put methods: Either in the prototype object or on the instance itself. (One can also have free methods, but we lose a lot of object orientation that way.) If the methods are attached to the instance naively we end up with once function object per owning object instance:

function MyClass () {
    // One per instance
    this.sayHello = function () { ... };
}

Second best is to define the function outside of the constructor and then reference it, giving us one reference per instance:

function MyClass_sayHello = function () { ... };

function MyClass () {
    // One reference per instance
    this.sayHello = MyClass_sayHello; 
}

The two methods above are passable, and unless you create lots of objects with lots of data, you are unlikely to notice any sharp increase in memory use. However, adding methods to the prototype is both easy and saves memory. Some optimizers, such as Google's Closure Compiler[b] expects methods to be on the prototype object[1]. Therefore, we will do the equivalent of:

function MyClass () {
}

// One reference, period.
MyClass.prototype.sayHello = function () { 
    ... 
};

1.3. Closures

Javascript has an oddity where the this reference isn't captured in an inner function, even though you'd expect it to be. Therefore, closures uses the convention of a that variable to refer to the owning object.

1.3.1. Rationale

The oddity comes from the Javascript rule that says that the this reference will refer to the object that owns the function at the time of call:

myFunction = function () { 
    console.log (this.name); 
}

var a = { 
    name : "a", 
    func : myFunction 
};

var b = { 
    name : "b",
    func : myFunction 
};

// Will output "a" to the console, because we
// are calling the function through the "a" 
// object.
a.func ();

// Will output "b" to the console, because we
// are calling the function through the "b" 
// object.
b.func ();

In short, Javascript behaves as if every function had a hidden this argument, such that a function declaration like function func (a, b) really was function func (this, a, b); and that the statement object.member (parameters) was really member (object, parameters). Most of the time we can ignore this, but when we create an inner function and it gets called without accessing it through an owner object, the this reference gets lost:

function callback (f) {
    f(); // no owner object!
}

var object = {
    doStuff : function () {
        callback (function () {
            console.log (this);
        });
    }
}

The code above will result in undefined being logged, because the function call in callback(f) is not done through an owner object. However, if we copy the this reference to a local variable, it is preserved and the code below does what you would expect it to:

function callback (f) {
    f(); // no owner object!
}

var object = {
    doStuff : function () {
        var that = this;
        callback (function () {
            console.log (that);
        });
    }
}

1.3.2. Function Wrapper

If it is too cumbersome to use a that variable, or if other concerns make it desirable to have "proper" closures, the following code will take an owner object and a member function and return a single function that preserves the this reference. The code below illustrates this:

function callback (f) {
    f(); // no owner object!
}

function closure (owner, method) {
    return function () {
        // Set the "this" reference and pass along
        // any arguments
        return method.apply (owner, arguments);
    };
}

var object = {
    doStuff : function () {
        var that = this;
        callback (closure (this, function () {
            console.log (this);
        }));
    }
}

1.4. Fields

All fields are public and set on the this object.

1.4.1. Rationale

This would appear to be a strict departure from the principle of information hiding, but we don't really have much choice. Since we decided to put all instance methods in the prototype object (see §1.2. Methods), the only way to access instance data is through the this reference.

Javascript has the concept of privileged methods, which are methods that are attached to the object instance and that access variables defined in the constructor:

function MyClass () {
    var hidden = "secret";
    
    this.getHidden = function () { 
        return hidden; 
    };

    this.setHidden = function (value) { 
        hidden = value; 
    };
};

var m = new MyClass ();
alert (m.hidden); // shows "undefined"

However, using privileged methods to create private fields still leaves us with no way to hide the privileged methods themselves. Like the fields, in order for the methods to be useful we must be able to reach them through the this reference.

var m = new MyClass ();
alert (m.getHidden ()); // shows "secret"

This makes privileged methods a lot less useful than they would be if we had simply attached all methods to the object instance. Their primary benefit is that we make the fields themselves unreachable from the outside and enforce the use of setters to modify them; but in cases where that is called for, client code should be accessing the field through a public setter method anyway.

Another reason to hide fields is to avoid namespace pollution in the case of inheritance. Since an object from a derived class shares the same this reference as the base class, there is always the risk that adding a field to a superclass will accidentally cause a name collision with a subclass.

For this reason we prefer composition to inheritance, and make explicit what gets added to the this object. See §1.7. Inheritance below.

1.5. Interfaces

Interfaces are declared as object literals with function values:

HelloSayer = {
    sayHello : function (who) {},
    setDefault : function (who) {}
}

1.5.1. Rationale

Javascript doesn't have the concept of declared interfaces, instead, we use a technique called "duck typing"[d]. The term "duck typing" comes from the saying "when I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck". The Javascript version would be "when I see an object that has all members declared in an interface, I say that object implements that interface".

Duck typing comes with the advantage of not having to declare interfaces. For example, if we have two classes from two systems, each having a close () method (and the semantics of the methods are equivalent enough), then duck typing allows us to talk about a Closeable interface with a close () method, even though neither class actually declares that interface. In Java, we would have to create the Closeable interface and then write adapters for the two classes.

Duck typing also comes with the disadvantage that static type checking on the receiver side is impossible or at least impractical - a Javascript method can't declare argument types at all. Still, having a way to express interfaces in a way that can somehow be used to validate object instances is a good thing: While static checking is very difficult, we can do very useful dynamic checking at runtime once we start using a module system. See §4.3.1. Validating Interfaces for a description of how this is done.

1.6. Statics

Static members are attached to the class/function object after it has been declared:

HelloSayerImpl = function () {
    ...
};

HelloSayerImpl.WORLD = "World";

HelloSayerImpl.createDefault = function () {
    return new HelloSayerImpl (HelloSayerImpl.WORLD);
};

1.6.1. Rationale

Having the static members right on the class object makes for an easy to understand syntax.

1.7. Inheritance

When discussing inheritance we must separate it into two cases:

  • Tight coupling: When the base class needs access to members of the derived class. This is the case when inheriting from an abstract base class for specialization.

  • Loose coupling: When the base class can be used from the derived class without the former having access to the latter. An example of this is when we derive from an EventDispatcher class in order to have event dispatching functionality in the new class.

We will deal with these two cases in different ways:

  • Tight coupling: In this case we will do classic inheritance, where the derived class inherits everything in the base class. This carries with it the risk of namespace pollution, for reasons described in more detail in §1.4. Fields, but in short: all members from the base class are inherited - there are no private members and no member hiding. If used sparingly, however, this risk can be managed. See §1.7.1. Classic Inheritance.

  • Loose coupling: Instead of classic inheritance we will use inheritance by composition. This greatly reduces the risk of namespace collisions and maintains the loose coupling between base and derived class. See §1.7.2. Inheritance by Composition

1.7.1. Classic Inheritance

Classic inheritance is done by copying over methods from the base classes to the derived class.

Base = function () { ... }
Base.prototype.sayHello = 
    function () { ... };

Derived = function () { ... };

// Do the inheritance
for (var k in Base.prototype) {
    if (!Derived.prototype[k]) {
        Derived.prototype[k] = 
            Base.prototype[k];
    }
}

The constructor then invokes the base classes's constructors via Function.apply.

Base = function (baseParam) {
    ...
}

AnotherBase = function (anotherBaseParam) {
    ...
}

Derived = function (baseParam, anotherBaseParam, derivedParam) {
    // Using the same "this" for the base
    Base.apply (this, baseParam);
    AnotherBase.apply (this, anotherBaseParam);
    ...
}
1.7.1.1. Rationale

This way of doing inheritance has very few surprises for someone coming from an object-oriented language such as Java or C++. Methods end up where you expect them to, and work the way you expect.

It may appear ugly to those familiar with how inheritance in a prototype-based language is supposed to work. The questions would be "Why copy the methods?" and "Why not use the fact that Javascript name lookup proceeds to the prototype of the prototype object, and so on?". The answer is that we're not really doing this the way Javascript wants us to. We're doing a kind of pseudo class-based OO in a prototype-based language. For the prototype-chaining to work, we have to create an instance of the superclass. That is, we can't set Derived.prototype = Base;, we must do Derived.prototype = new Base ();.

1.7.2. Inheritance by Composition

Inheritance by composition works by instancing the base class as a member of the derived class, and then exposing the public base class interface on the derived class. Let us look at the example of an event dispatcher. We want to put all the event-dispatching functionality in a class (EventDispatcher), and then have our classes obtain that functionality. We also want an interface that all event-dispatching classes implement (IEventDispatcher).

IEventDispatcher = {
    addEventListener : function (...) { ... },
    removeEventListener : function (...) { ... }
};

function EventDispatcher () {
    ...
}

EventDispatcher.prototype = {
    addEventListener : function (...) { ... },
    removeEventListener : function (...) { ... },
    fireEvent : function (...) { ... }
};

The above are all parts of the event dispatch system. We have the client interface that allows for a listener to add or remove itself, and the event dispatcher class, that, besides the add and remove methods also allows for firing events. The inheritance is then done by composition, given a hypothetical Button class:

function Button () {
    this.eventDispatcher = new EventDispatcher ();
}

for (var methodName in IEventDispatcher) {
    // Button.prototype[methodName] becomes a 
    // function that simply passes on all 
    // arguments to 
    // this.eventDispatcher[methodName].
    Button.prototype[m] = 
        makeInterfaceFunction (
            "eventDispatcher", 
            methodName);
}

/**
 * Creates a function that passes all 
 * parameters unchanged to the corresponding 
 * function in the target object.
 */
makeInterfaceFunction = function (
    targetFieldName, methodName) {
    return function () {
        var thisPtr = this[targetFieldName];
        thisPtr[methodName].apply (
            thisPtr,
            arguments);
    };
}

What we end up with is a Button class that exposes the addEventListener and removeEventListener methods on its public interface, but not the fireEvent method. Code in the Button class can fire events by calling this.eventDispatcher.fireEvent (...).

1.7.2.1. Rationale

Since Javascript doesn't have any equivalent to the protected access level, and because private members are just private, classic inheritance where the base class shares the this-reference with the derived class greatly increases the risk for name collisions in class hierarchies.

Using composition in the above example results in the derived class not needing to worry about name collisions with the members of the EventDispatcher class, only about name for the member that holds the event dispatcher instance - which is under the complete control of the derived class - and method names declared in the IEventDispatcher interface, which can be assumed to be a lot less volatile than the definition of the implementing class.

We can of course factor out the presentation of an interface on a derived class as a function, in order to make the code cleaner:

function Button () {
    this.eventDispatcher = new EventDispatcher ();
}

implementInterfaceVia (Button, IEventDispatcher, "eventDispatcher");

/**
 * Exposes methods in a field via the owner object.
 *
 * @param {Class} derived the object that shall expose the methods
 * @param {Class} iface the interface to expose
 * @param {string} name of the member of <code>object</code> that implements 
 *                 <code>iface</code>
 */
function implementInterfaceVia (derived, iface, fieldName) {
    var makeInterfaceFunction = function (fieldName, methodName) {
        return function () {
            // The "this" here will refer to an instance of
            // derived
            var thisPtr = this[fieldName];
            return thisPtr[methodName].apply (
                thisPtr,
                arguments);
        };    
    };

    for (var k in iface) {
        derived.prototype[k] = makeInterfaceFunction (fieldName, k);
    }
};

2. Modules

Now that we have our code neatly organized into classes, we need a way to organize the classes themselves into a functioning whole. One of the hurdles one must get over before doing large-scale Javascript development is the lack of a module or dependency management system. Since the loading and parsing of Javascript is a single-pass process based on inclusion, like the C preprocessor, we have to make sure that that multiple declarations don't happen and that dependencies are included before the module that depends on them. We can do all that manually, or we can create a module system to handle it for us. Small- to medium-scale development can get by with the former, large-scale development requires the latter.

!

I must state here that Bigshot is not using a module system; mostly because I am loath to make integration with an existing system harder than it absolutely has to be. It is, however, just on the upper limit of what is possible to comfortably develop without one.

2.1. Architecture

The end result of a module load will be that an object, called the module object, is added to the global scope. If the name of the module contains multiple components separated by periods, all components except the last will be assumed to refer to a tree structure of objects in the global scope. For example, "mypackage.impl.MyClass" will, given an empty global scope, result in these objects being created:

mypackage = {}; 
mypackage.impl = {}; 
mypackage.impl.MyClass = ...module object...

2.1.1. Rationale

In Javascript, most things are untyped objects. We will put restrictions on what counts as a "class" and "interface" in our system, and will add ways to (almost always) enforce this at runtime, but allowing a module to be an object of unspecified type is good enough at this level.

The naming convention is lifted straight from the Javascript scope resolution syntax.

2.2. Bootstrapping

The module system will always be the first script to be loaded. All other scripts that use the module system will be loaded by the system. In case of other module systems being used together with this system, we will detect them and play nice.

2.2.1. Rationale

In order for dependency management to work we have to run every dependency-checked and -managed module through the system.

2.3. Operation

A module is created by first getting a builder object from the module system. The various parts of the module is then created via method chaining, making the whole process look like a domain-specific language.

Modules.create ("mypackage.MyClass")
.require ("hellopackage.HelloSayer")
.as (
    function (name) {
        this.sayer = new hellopackage.HelloSayer (name);
    }
);

The above will cause the following chain of events:

  1. The module hellopackage.HelloSayer is loaded and created.

  2. The mypackage scope is created:

    mypackage = {};
  3. The MyClass module object is created:

    mypackage.MyClass = function (name) {
        this.sayer = new hellopackage.HelloSayer (name);
    };

2.3.1. Rationale

The syntax looks like some kind of pseudo-LISP, but it is eminently readable and gives us near full control of module creation.

2.4. Dynamically Loading Javascript

Javascript sources are loaded by adding a <script src="..."/> element to the document body. The browser will load the source script and evaluate it.

2.4.1. Rationale

There are a number of other ways to handle dependencies:

  • Server-side: We can instruct the web server to resolve all dependencies for a script and return them, properly ordered. For example, if the web page requested MyClass, the server would inspect the class, discover that it was dependent on (for example) MyBase. It would then prepend the sources for MyBase to the source for MyClass and return the result.

    The upside of this method is that much is done server-side. We don't have to worry about doing anything in Javascript, giving us a nice, comfortable environment. The downside is that loading multiple scripts from a page will almost certainly result in multiple inclusions of the same source. If the web page requests MyClass and MyOtherClass, both of which are dependent on MyBase, then we either need to wrap these two requests into a higher-level request, or somehow make the server aware that this web page has already asked for, and received, MyBase when the second request comes. In the end, we may end up with more complexity on the client side. Another downside is that the concatenation of source code results in error message line numbers are changed. Instead of knowing that the error comes from MyClass.js, line 59, it'll be from MyBase-and-dependencies.js, line 453. The difference may seem trivial, but if you are like me, you'll be seeing a lot of errors and the extra clicks and extra time required to figure out exactly which line in the original source the compound line number corresponds to add up.

  • XMLHttpRequest and eval(): eval() is evil(), but can it be made to do good here? The answer is a depressing no. The upside of using XMLHttpRequest is that we control when the source code for a module is evaluated. The method shares the downside of the previous method in not giving us proper source code line numbers.

    Another issue is lack of cross-browser, cross-domain requests. While all major browsers support cross-domain requests now, support in older browsers is lacking, making this a constant compatibility issue and potential source of errors.

  • Preprocessing: Let's just forget about doing anything dynamic and add a separate compilation step to our build script. We'll just take all the source code, figure out what should go in and what shouldn't, in what order the "in"-stuff should go, and then just output one big source file.

    The obvious downside with this is that we lose a lot of what makes Javascript development quite nice: The absence of a compilation step. To further worsen the deal, we don't get any syntax or type checking from it. It also has the downside of the two previous methods - lack of proper source line numbers. For development, this isn't throwing the baby out with the bath water; this is throwing the baby out and keeping the water. For deployment, on the other hand, this works great, as we'll see in §2.5. Deployment.

Given the problems inherent in the alternatives, and the relative simplicity of using <script src="..."/> elements to load code in a cross-browser compatible way, I decided to use the latter method.

2.4.1.1. Out-of-Order Loading

This way of loading Javascript has the downside that the modules will be loaded in the opposite order of dependency. That is, if MyClass requires MyBase, we will not find out about this until MyClass has loaded, and as part of that process, requests MyBase.

A consequence of this is that the module will be evaluated in a scope where no dependencies exist. Suppose we have a module MyClass, dependent on MyMath:

Modules.create ("MyClass")
.require ("MyMath")
.withPrototype (
    {
        BASE_ANGLE : MyMath.PI
    }
)
.as (
    function () { 
        ... 
    }
)

When this module is loaded and parsed by the browser, the class MyMath hasn't loaded yet - only by the browser's evaluation of the .require ("MyMath") statement does the module system find out that this class is even needed. The result is that the BASE_ANGLE member isn't set. Since MyMath doesn't exist, the Javascript interpreter will throw an error.

The only solution is to set the BASE_ANGLE member in a class initializer that runs after all dependencies (and their dependencies) have loaded:

Modules.create ("MyClass")
.require ("MyMath")
.withPrototype (
    {
    }
)
.withPrototypeInitializer (
    function () {
        // "this" points to MyClass.prototype
        this.BASE_ANGLE = MyMath.PI;
    }
)
.as (
    function () { 
        ... 
    }
)

The module system will, when all dependencies have completed loading and initialization, call the prototype initializer with MyClass.prototype as the this reference.

2.5. Deployment

For deployment the Javascript source files are first concatenated in dependency order and then compressed using Yahoo's YUI Compressor.

Finding the dependency order is easy due to the require() statements. We can start with one or more "root" files and parse them. Whenever we hit a require() statement for a module that we haven't processed yet, we recursively parse it. When we are at the end of the source file, all statements except the require() statements are output to the concatenated file. That way, we end up with a file where the dependencies precede the dependent module.

The output can then be fed as a single unit to the YUI compressor.

2.6. Imports

So you've been really good and put your 3d-point class in com.fooware.javascript.common.graphics.math.geometry.primitives.Point3D; all neatly categorized and good to go. As soon as you type your first new involving that class, you realize that you should have named it g.Point3D. What we want to do is to be able to use short names for classes when using them. Java has the import statement, C++ has the using namespace statement, but Javascript has no equivalent.

In practice, this doesn't matter much. Object creation is uncommon compared to much other code. It is, however, aesthetically unpleasing; and ugly source code tend to be bad source code - if a section of code is crisp and clear it is so much easier to debug than if you have to sort through a visual mess to get at the meaning of it. We should therefore strive to minimize clutter.

I haven't found a good method, so let me describe the problem.

In order to have imports, we must add the short class names to the current scope when the module source is evaluated:

mypackage.MyComponent = function () { ... }

mypackage.MyClass = function () {
    // Fails, because MyComponent isn't in scope
    this.component = new MyComponent ();
}

The obvious way to solve this is to wrap the declaration of mypackage.MyClass in an anonymous function in order to create a new variable scope:

(function() {
    var MyComponent = mypackage.MyComponent;
    mypackage.MyClass = function () {
        // Works, because MyComponent is in scope
        this.component = new MyComponent ();
    }
})();

This also gives us a way to control when the class body is evaluated. If we remove the final function call, we can turn it into an object, MyClassCreator, that when invoked creates MyClass:

MyClassCreator = function() {
    var MyComponent = mypackage.MyComponent;
    mypackage.MyClass = function () {
        // Works, because MyComponent was captured 
        // on evaluation of MyClassCreator
        this.component = new MyComponent ();
    }
};

Nice from a Javascript-hacker point of view, but how many will understand it? Having complexity like this, just to avoid long class names seems like trading one problem for another. I'll update this section if and when I come up with a solution.

3. Documentation

Like Javadoc for Java, there is really only one code documentation tool for Javascript: JsDoc Toolkit[e].

3.1. Rationale

The only competitor is doxygen[f], but it requires helpers for Javascript[g] which are less than optimal[h].

3.2. Dynamic vs. Static

Statically typed languages contain a lot of information about the code in the code itself: Expected argument types, access modifiers, exception declarations, and so on. A language as dynamic as Javascript, where these declarations aren't even optional - they're not allowed - puts the onus on the developer to include the relevant information in the documentation. Types and access modifiers tend to be the most important pieces of information to include. "Correct" exception lists are rarely needed even in Java, because as soon as you involve callbacks, you often end up with a method that throws the base exception class (that is, the methods simply throw Exception).

3.3. Types

I've found it useful to use the "types" int, float, boolean, String and Object. Only the two last ones are actual class names in Javascript, but having a float as well as an int, instead of using the vague Number makes it easier to express just what a method requires: A count (int) or a magnitude (float).

3.3.1. Function Types

Non-trivial Function types are best declared as non-existent functions:

/**
 * @name MyClass.HelloCallback
 * @function
 * @param {String} message the greeting message
 * @type boolean
 * @return true if the greeting was sent
 */
 
/**
 * Formats a greeting message and passes it to the callback
 * function. 
 *
 * @param {String} who name of the entity to greet
 * @param {MyClass.HelloCallback} callback function that
 *        performs the actual greeting
 * @return the value returned from the callback.
 */
MyClass.prototype.sayHello (who, callback) {
    return callback ("Hello " + who + "!"));
}

3.4. Classes

Classes are documented using the @lends tag on the prototype and statics objects. Note that the @lends on the prototype has a "#" on the end, marking the members of the object literal as instance members, and the one on the statics object doesn't.

Modules.create ("mypackage.MyClass")
.withPrototype (
    /** @lends mypackage.MyClass# */
    {
        myMethod : function () {
            alert ("Hello " + this.who + "!");
        }
    }
)
.withStatic (
    /** @lends mypackage.MyClass */
    {
        createDefault : function () {
            return new mypackage.MyClass ("World");
        }
    }
)
.as (
    /**
     * Creates a new instance of MyClass.
     * @name mypackage.MyClass 
     * @class Says hello to anything.
     */
    function (who) {
        this.who = who;
    }
);

3.5. Methods

Methods should have the following elements documented: Access permissions if not public, argument and return types.

3.5.1. Rationale

The access permissions keep private methods out of the public API documentation, while still making it possible to produce a complete reference. The argument and return types correct a major hole in the Javascript syntax; even if the interpreter doesn't need to know the types, programmers sure do in order to construct a functioning program in reasonable time.

3.6. Fields

Fields should all be declared private unless there's a good reason not to. The only required declaration is the type of the field.

4. Testing

Testing Javascript code is a challenge. First, the code you will be testing is often the kind that isn't easy to set up for automated tests. Second, you are deploying on multiple platforms (browsers), making testing even more important. Third, the dynamic, scripted nature of Javascript means that you don't get the benefit of the compiler checking your variable names and types. To summarize: You get code that is harder to test, that you need to test more, while getting less testing done for free.

However, the dynamic nature of Javascript also provides opportunities to relieve at least a little of the burden. The principal tool we will use is dynamic augmentation of classes by wrapping methods and constructors.

4.1. Wrapping a Method

Wrapping a method is easy, thanks to the apply method of all Function objects, and the arguments variable:

function wrap (f) {
    return function () {
        return f.apply (this, arguments);
    }
}

Example Usage:

mypackage = {};

mypackage.Base = function (message) {
    this.message = message;
}

mypackage.Base.prototype.sayHello = function () {
    document.write (this.message);
}

var original = mypackage.Base.prototype.sayHello;
mypackage.Base.prototype.sayHello = function () {
    document.write ("<b>");
    original.apply (this, arguments);
    document.write ("</b>");
}

var instance = new mypackage.Base ("Hello World!");

// Outputs "Hello World!" in bold.
instance.sayHello ();

This can be done to add log statements, parameter validation, and so on.

4.2. Wrapping a Constructor

The corresponding operation for constructors is slightly more complex, as there is no method corresponding to Function.apply for constructing objects. Given a class constructor ctor and a prototype object prototype, the constructor can be wrapped using the following snippet, which returns the constructed object o:

var NOP = function () {};
NOP.prototype = prototype;
var o = new NOP();

var ret = ctor.apply (o, arguments);
if (ret != null) {
    o = ret;
}

// Do any post-construction augmentation of the
// object here.

return o;

4.2.1. Rationale

Let's go through this method step by step.

Object construction involves two steps: Allocation and construction. Allocation is when a new, empty object instance is created. Construction is when the constructor is invoked with the newly allocated object as this. Our first attempt, given a constructor function ctor would therefore be:

var o = {}; // Allocate
ctor.apply (o, arguments); // Construct

But now we forgot about the prototype. When the constructor runs, this.prototype is not set. We add the prototype to the newly allocated object:

var o = {}; // Allocate
o.prototype = ctor.prototype; // Set prototype
ctor.apply (o, arguments); // Construct

We'd almost be there now, but for one thing: The prototype must be set before we do the allocation. The rule is that "o = new C(); shall result in o.prototype = C.prototype;"[2]. Remember that var o = {}; is equivalent to var o = new Object (); - it is here that the real prototype for the instance is set. So we create a dummy class, NOP, set the prototype on it, and then allocate our new instance using that class.

4.3. Using the Module System

We can make the dynamic nature of Javascript work for us by using it to augment code, and since basically all code that is loaded will have to pass through the module system, it is an excellent point for this. By putting some consistency checks in place we can catch many programmer mistakes at the point of loading, instead of having them turn up as bugs later.

4.3.1. Validating Interfaces

Large scale development is dependent on being able to ignore most of the system. A programmer that has to absorb an entire code base into their head will never get anything done. Absorbing the large-scale structure and a few interfaces, however, is something that is easily done. Those interfaces, however, are worth nothing unless we can somehow ensure that they are actually valid.

Given the interface definitions, it is easy to provide a very basic consistency check. The plan is the following: When loading a class, we will wrap the constructor function in a function that checks that the constructed object has a function entry for all functions declared in the interface.

function validateInterface (iface, object) {
        for (var k in iface) {
            if (object[k] == null ||
                typeof object[k] != 'function' ||
                object[k].length != iface[k].length) {
                throw new Error ("Implementation does not implement " + k);
            }
        }
    }

Checking this every time an object is constructed takes too much time, so we'll just check the first object constructed and then, if it passes, let all others through without checking:

function validateInterface (iface, ctor) {
    var checkData = { passed : false };
    return function () {
        var o = ctor.apply (this, arguments);
        if (checkData.passed) {
            // We have already passed the check once.
            return o;
        } else {
            for (var k in iface) {
                if (o[k] == null ||
                    typeof o[k] != 'function' ||
                    o[k].length != iface[k].length) {
                    throw new Error ("Implementation does not implement " + k);
                }
            }
            // Set flag so we don't check again
            checkData.passed = true;
            return o;
        }
    };
}

Footnotes

2012-02-24, updated 2015-12-18