bacrjg
Last Updated: February 25, 2016
·
5.228K
· swivelgames

Using a Closure to Organize Prototype Declarations

This probably needs some good cleaning up, but I hope this will help everyone out when writing a new prototype object. This is what I have gravitated towards over the past couple of years of writing prototype after prototype. It is certainly not necessary for simple objects, but for true Prototypes, this is a great style of coding. We currently encourage our developers to follow this style, as it seems to keep everything clean.

The Prototype Closure coding style utilizes standard JavaScript prototyping methods inside of a closure to better organize code related to the object.

General Closure Abstract

The Prototype Closure coding style organizes the constructor and prototype methods inside a closure for easy identification of the prototype object.

var AdditionUtility = (function(){
    var Constructor = function(value){
        this.value = value || 0;
    };

    Constructor.prototype = {
        value: null,
        add: function(numToAdd) {
            this.value += numToAdd;
        }
    };

    return Constructor;
})();

At the top of the closure function, the constructor is declared. Afterwhich, properties, setters, getters, and methods are defined in the prototype property. This type of coding style aims to restrict the declaration of the constructor, properties, and methods inside the closure to ensure organization. A programmer from a traditional, object-oriented programming language, as well as a seasoned JavaScript developer, should easily be able to identify and modify this object and its properties. The key things we're trying to ensure here is: Readability and Organization of Code (which both contribute to maintainability). It also makes the code more portable, given that the object is not heavily coupled with other prototypes, functions, or objects.

Guidelines

Although the parser does not force a specific pattern of coding when using any method of prototype object declaration, it is important to establish and abide by guidelines by which each prototype object will be constructed and utilized. Some of these will be expanded on later.

Constructor Declaration

The simplest illustration of the Prototype Closure pattern is the example given above. As a general rule, you should typically name the main function "Constructor" to distinguish itself in the code. This variable is only within the scope of the Closure, of course, and will not be accessible outside of it. Instead, you would use the AdditionUtility object, which will contain the resulting Prototype Object.

Prototype Object Declaration

Using this coding style, I typically declare the prototype object in a single statement. For extending other prototypes, I usually refer to ECMAScript 5's Object.create. For standard prototype object creation, as seen on line 6 of the example above, I simply utilize JavaScript's object literal syntax.

An example of Object.create can be found on MDN

Property Declaration

In my experience with this, properties should always be declared inside the prototype object as a best practice. Doing so will allow you to define setters, getters, or literal properties, and it will also ensure readability and proper organization of code in regards to this pattern.

ALL properties should be declared and initialized if they are to be utilized within the object. There should never be a declaration of a new property within a method. ALWAYS declare all your properties within the closure, and then set their values in their appropriate methods.

As a best practice, I typically do not set a property's value dynamically in the property declaration, and instead, I do so in methods (e.g. in the constructor). Typically the only value that a property is given during its declaration is a primitive value (string, number, boolean, null, undefined, etc...)

Also note JavaScript's inherent method of referencing a variable's value for non-primitive. When declaring an object or array as a property within the closure, instead of null or undefined and then instantiating the object or array within the constructor or a method, a reference is stored in the property, rather than the actual object. Because of this, all future instances of the prototype object will contain a reference to that object, rather than a new instance for each new instance of your prototype object.

Explained:

var Foo = { "greeting": "Hello" }, Bar;
Bar = Foo;
Bar.greeting = "Hello World";
console.log(Foo.greeting);

In the example above, an object was instantiated with a property "greeting" whose value was "Hello", and a reference to that object was stored into the variable "Foo", linking the two together.

On line 2 ( Bar = Foo), the reference to said object was copied into Bar, rather than the actual object. Because of this, both Bar and Foo now point to the same object. Thus, changes to the greeting property are reflected in the Foo variable as well.

The last statement in the example above will alert "Hello World", rather than "Hello".

In order to ensure uniqueness among instantiated prototype objects, it is advised to declare objects and arrays within the constructor or methods, rather than during declaration within the closure. Instead, these properties should be declared as "null" or "undefined", with a comment referencing the name of the method in which they are initially set.

Object declarations, however, are allowed when explicitly creating a "global" that all instantiated objects will share, like a defaultOptions object, which should be the same across all instantiated objects.

Setter/Getter Declaration

Best practices call for the Setters and Getters to always be declared after properties inside the prototype object declaration, like in the example below.

When using Setters and Getters in reference to a counterpart variable, the counterpart property should always have the same name as the setter or getter prepended with an underscore as a best practice. This is illustrated in the example below as well, on line 4.

Constructor.prototype = {
    foo: null,

    set bar: function(val){
        this._bar = val;
    },

    get bar: function(val){
        return this._bar;
    }
};

Just as within a Property Descriptor for usage with the Object.create functionality, Setters and Getters for the same property reference should be grouped together, preferably in the order of: setter, getter; this is outlined in the example above. Likewise, getters should not be grouped with getters, and setters should not be grouped with setters, rather they should be grouped by their shared property reference, to ensure readability.

Method Declaration

Methods should always be declared after the properties, setters, and getters.

As a general rule, methods should be declared in the following order:

  1. public methods (camel-case)
  2. pseudo-private methods (camel-case, prepended with two underscores "__")
  3. utility methods (like toString, etc...)

Pseudo-private methods should be prepended with two underscores (e.g. this.__generateKey()). Their "private" status should be honored, and thus they should never be called outside of the prototype object itself. Because JavaScript does not honor private methods, it is not feasible to prevent access to these methods outside of the object itself except by conventions.

Non-Declarative Statements within the Closure's Immediate Scope

In order to maintain readability, and to ensure proper coding of objects, there should never be any non-declarative statements within the immediate scope of the closure. All logical code should be contained within the constructor, methods, or setters/getters to ensure best practices are followed.

No statements should be immediately contained within the closure except for the constructor declaration, prototype object declaration, and the return statement.

Execution and Utilization

Instantiation

var AdditionUtility = (function(){
[...]
})();

var Y = new AdditionUtility(13);

Instantiation is normally done using the "new" operator. In the instantiation process, the parser executes the constructor which begins at line 2 of the first example of this tip.

Singleton Pattern

Because of the design of the Prototype Closure pattern, instantiation can be done to the closure to produce a "singleton"-like object. With this, the prototype is still available by traditional means for adaptation to other objects using Object.create, but this provides a clean way to create a simple object that should only be used once, without the need for two variables (one containing the prototype object, the other containing an instantiated version of said object). In this case, parameters would be passed to the closure and automatically fed to the constructor. The singleton form of this pattern slightly differs, and can be reviewed in the below example:

var Test = (function(){
     var Constructor = function(message,obj) {
          console.log(message);
          this.obj = obj;
     };
     return new Constructor(
          "Hello World!",
          { "foo": "bar" }
     );
}());

Notice the change in closure syntax on line 6. Simply executing the above statement would produce a message in the console containing "Hello World!" and an object "Test" with a property "obj" whose value is outlined in the second argument starting on line 8.

Utilization and Interaction

The object and its instances will function just like any other prototype object in JavaScript.

var AdditionUtility = (function(){
    var Constructor = function(value){
        this.value = value || 0;
    };

    Constructor.prototype = {
        value: null,
        add: function(numToAdd) {
            this.value += numToAdd;
        }
    };

    return Constructor;
})();
var Y = new AdditionUtility(13);
Y.add(1);
Y.value;

The above example creates a new instance of AdditionUtility, passing "13" as the argument for the "value" parameter in the constructor on line 15.

The last statement in the above code example would result "14".

Because this is a prototype object, access to the methods and properties are available via the "prototype" property of the "AdditionUtility" variable.

The below statement will produce the same result of "14", while utilizing the Function.prototype.call function available to all Function type variables. (See also, Function.prototype.apply)

var Z = new AdditionUtility(13);
AdditionUtility.prototype.add.call(Z,1);
Z.value;

Drawbacks

Code Suggestion in IDEs and Editors

Because the object is the result of the execution of a closure, hints based on the constructor may not provide a list of parameters for the object in some IDEs and Editor. For some editors, this cannot be avoided. However, I have found that most editors will honor the Prototype Closure pattern and adequately provide property and parameter suggestions for the objects.

Between The Lines Coding

A dangerous mistake an uninformed or inexperienced developer can make is putting non-declarative statements outside of the constructor or methods, and inside the closure between the constructor or prototype object declarations. This can be dangerous as it is hard to read, may result in undesired behavior, and is often a result of the developer's lack of understanding of this type of coding style. While JavaScript and IDEs do not know any better, it is important that developers do not write statements other than constructor and prototype declarations inside the closure. Instead, try to leave all logical and functional coding inside the constructor and methods.

See Also

3 Responses
Add your response

22789

I'm still confused about how to access properties. For example, I have the following code:
var Config = (function () {

var Constructor = function () {
    this.dir = "js";
    this.enginedir = "js/Engine";
};

Constructor.prototype = {

    include: [
        this.dir+"/Common.js",
        this.enginedir+"/Class.js"
    ]
};

return Constructor;

})();
MyConfig = new Config();

The properties in the prototype (this.dir and this.enginedir) are undefined...?

over 1 year ago ·
22851

joe_44850,

Notice how, in my examples, my properties aren't defined until the constructor itself is called. There are many reasons for this, especially when dealing with objects. Property declarations can exist within the prototype object declaration, but in order to make sure things are maintainable and written with multiple instances in mind, a property's value assignment should happen within the Constructor or methods.

Thus, the example you gave should instead be:

var Config = (function () {
    var Constructor = function () {
            this.include = [
                this.dir+"/Common.js",
                this.enginedir+"/Class.js"
            ];
    };

    Constructor.prototype = {
        dir: "js",
        enginedir: "js/Engine",

        include: null
    };

    return Constructor;
})();

var MyConfig = new Config();

Like many other languages, the properties are not available until an instance of the object is created. So, until the constructor is executing, access to the properties is unavailable. This is by design.

Notice, however, in the refactored version above that the declaration and assignment of dir and enginedir is happening at the same time. There's a reason why this can be acceptable, versus the assignment of the include property.

Remember that all primitive values are immutable, and non-primitive values are Objects (inherently). When variables are assigned an object, the variable simply references the object created at assignment. Because of this, when the JavaScript engine parses the code to define Constructor.prototype, it is then that the object will be created and then stored into Constructor.prototype.include. Since Objects are references, modifying the property within one instance will also inadvertently change the property within another instance. This is because we're changing the Object itself, and every instance of Config.include will point to that object, rather than each instance having its own object to manipulate.

In the example I provided, you'll notice that only during instantiation of a new Config is the include property set, thus each instance will have its own include property.

This goes back to non-declarative statements within the main closure, although the dir and enginedir are breaking this rule. Mainly, the rule is in place for logic and non-primitive value assignments. Primitive value assignments are really the only available exception, and strictly within the prototype object declaration.

Because dir and enginedir are set to primitive values, you can technically get away with this exception.

over 1 year ago ·
22853

The article goes into depth on this as well; See section about Property Declaration and the example in there:

In order to ensure uniqueness among instantiated prototype objects, it is advised to declare objects and arrays within the constructor or methods, rather than during declaration within the closure. Instead, these properties should be declared as "null" or "undefined", with a comment referencing the name of the method in which they are initially set.

Object declarations, however, are allowed when explicitly creating a "global" that all instantiated objects will share, like a defaultOptions object, which should be the same across all instantiated objects.

over 1 year ago ·