Click here to Skip to main content
15,867,568 members
Articles / Web Development / HTML

Safe Factory Pattern - Private instance state in JavaScript

Rate me:
Please Sign up or sign in to vote.
4.85/5 (11 votes)
18 Jul 2014CPOL24 min read 77.9K   166   26   21
Enabling private state in any JavaScript object.

Introduction

(Some years later, I've published an implementation of the pattern in this github project)

JavaScript objects are very similar to a dictionary. They have properties - identified by names - and these have an associated value, of any JavaScript type. Properties of an object can be changed, added, or removed at any time, anywhere, and by any code, as long as you have a reference to the object. This loose object model is one of the many great qualities of JavaScript. The following JavaScript code exemplifies these operations on a plain object:

JavaScript
var obj = {}; // <=> new Object();
 
// Add 'prop1'
obj.prop1 = 1;
 
// Add 'prop2'
obj.prop2 = 2;
 
// Delete 'prop1'
delete obj.prop1;
 
// Change value of 'prop2'
obj.prop2 = 3;

This wild and dynamic world also means that there are no certainties about what is stored on a given object's properties.

While JavaScript code is small and sweet, this is a wonderful world to live in. But as it grows bigger and older, structured and complex, the lack of certainty becomes the code's greatest threat to staying alive.

Does ECMAScript 5 bring certainty?

The latest ECMAScript standard, version 5, already contains features that address this concern. Happily, most major browsers (some still in beta) already provide partial support for these features (see ECMAScript compatibility table). One of the greatest improvements is the possibility to define properties as being constant - this allows creating objects that are immutable, objects whose properties cannot be deleted or its values changed. This is great!

However, constant properties are not useful if the required certainties and invariants apply to mutable properties - those that are required to change, to serve an object's mutable needs, throughout its lifetime. To be able to keep certainties and invariants on these changing properties, keeping them private is the only way - otherwise, all the effort put into writing correct code may be thrown away due to public exposure of properties.

In this respect, ECMAScript 5 brings another great feature to JavaScript: accessor properties. These look and are used just like ordinary properties. But like methods, they are really just a façade to pure state. A pair of get and set methods define an accessor property and control what is actually stored on the value properties of an object. The following example shows the new object literal syntax that supports defining accessor properties:

JavaScript
var region = {
    _inside: 0,
    
    get inside(){
        return this._inside;
    },
    
    set inside(value){
        if(this._inside === 0){
           throw new Error("there's no one inside to leave"); 
        }   
 
        this._inside = value;
    }
};
 
alert(region.inside === 0);
region.inside = 1;
alert(region.inside === 1);

This is also great! As you can see, you have more control on what gets stored on an object and with a much more appropriate syntax. However, note that the _inside property of the region object is still public and thus susceptible to containing the invalid value "foo".

So, between now and the release and adoption of some other future JavaScript version that will bring us all a class-based object model that features private properties, is JavaScript code doomed to stay small or grow messy?

I think so... yes. Unless... you find that the pattern I present in this article is a solution to the problem. It uses JavaScript closures and its private goodness to provide private state to any JavaScript object. Private state is kept with objects, in public properties, that I call "safes". But only privileged code, that holds their "key", can open them.

The reader should be aware that there are others who share quite different opinions regarding the "public vs. private state in JavaScript" issue. Specially, on the practical implications of public state on code quality, stability, maintainability, etc. You may want to have a look at Private variables are evil, or at some of the comments of other sources I also refer.

The following section will show how JavaScript scopes and closures can be used to create private state. If you are already familiar with these concepts, you may well skip it and go directly to the section Safe Factory Pattern.

Text regions, scopes, closures, and privacy

Luckily, not everything in JavaScript is public. Variable names, functions, and function parameters live only within a confined textual (or lexical) region of code. These names cannot be referred to from code written outside of their enclosing region. A text region and all the identifiers there declared is also called a scope.

Types of scope

There are three types of scopes in JavaScript: global scope, function scope, and object scope:

  • Global scope corresponds to the top-level text region of the code, that of script files. It includes all variables and functions that are declared at top-level (not within a function or a with statement).
  • Function scope corresponds to the textual region of a function declaration, excluding nested function declarations and "with" scopes. It includes all parameter names, all variables, and all named functions declared there.
  • Object scope, or with scope, corresponds to the text region inside a with statement. It allows placing a given JavaScript object "in scope": its properties become independently resolvable identifiers (without a dot), like if they were variables.

The following code shows examples of each:

JavaScript
// global variable
var author = {
    firstName: "Duarte",
    lastName:  "Leão"
};
 
// global function (a singleton)
function Outer(value){
    var offset = 1;
    
    // Local (nested/inner) function
    // One is created per execution of Outer
    function Inner(){
        var offset = -1;
        return value + offset;
    }
 
    return Inner;
}
 
// More global variables
var inner1 = Outer(1);
var inner2 = Outer(2);
 
alert(inner1() === 0);
alert(inner2() === 1);
 
with(author){
    alert(firstName === "Duarte");
    alert(inner2() === 1);
}

From nested regions to nested scopes and the scope chain

Nested textual regions form nested scopes. And nested scopes form what is called a scope chain (not to be confused with the prototype chain), where every scope has a parent or outer scope. On a given scope, identifiers of an outer scope are accessible as long as they are not shadowed by a local identifier of the same name. Note the variable offset in the function Inner. It shadows the variable offset of function Outer, making the latter inaccessible to code in the function Inner. The global scope is the outermost scope of any scope chain.

From function movability to the lifetime of execution scopes

What I have presented so far is the image of a static, textually scoped world. And it would remain just like that, only if JavaScript functions weren't objects. You actually can store a function in a variable and then pass it around, as a parameter to a function call, as the return value of a function call, or as the value of an object's property. Later, the function may be executed by code that doesn't know exactly what it does, or how. This promiscuity between code and data can be observed in the previous example. A new instance of the function Inner is created and returned from each execution of the function Outer. The first returned instance is stored in the global variable inner1 and the second one in the global variable inner2. Later, the function in the variable inner2 is called twice. This is another great thing of JavaScript: code and data live together.

The movability of code, in the form of function objects (that are executable by any code, at any time, anywhere) shakes the static scope picture we formed before. We must shift into thinking of scopes as if they were real objects, that have properties representing identifiers, and that travel with function instances created within them. In our thinking, the difference between execution scopes and JavaScript objects should only be their accessibility. The former are not directly accessible, but their properties are accessible via naked identifiers. The latter are directly accessible, but access to their properties requires qualified identifiers (object.property). Note that due to their movability, scopes are now called execution scopes. Execution scopes live as long as the functions that were created within them.

Thinking like the JavaScript engine

Let's revisit the previous example and try to understand how it works in the light of this model:

SafeFactoryPattern/TheScopeChain.png

  1. Global scope is initialized by the scripting engine before anything else. Let's give it the name, S0. In its initialization, an identifier is created for every top-level variable and named function. Variable identifiers are all initialized to the special value of undefined, while named function identifiers are initialized to a new instance of each function.
  2. In this example, three identifiers are created for the variables author, inner1, and inner2, and all are initialized to the value of undefined. Additionally, an identifier Outer is created in S0 and set to a newly created instance of the function Outer, outer1. Its "creator scope" is S0 and that fact is stored with the function in a hidden system property. Execution proceeds, line by line.
  3. The first line is an assignment expression for the author variable; it creates and initializes an object that is set as the value of the identifier author in scope S0.
  4. Execution proceeds in the line var inner1 = Outer(1).
  5. The identifier Outer is evaluated in the current scope, S0, and the result is the function instance outer1. Being a function, the function call expression is valid and, following argument evaluation, the value 1 is supplied as the argument. A new function execution scope is created to support the execution: call it S1. Its parent scope is the "creator scope" of outer1, S0. This fact is stored in S1, again in a hidden system property.
  6. The initialization of scope S1 is not yet finished. For each of the function's declared arguments, an identifier of the same name is created, and is initialized to the corresponding supplied value, if any, or to the value undefined, if none. Then follows initialization of the arguments object, which I will not cover. Finally, identifiers are created and initialized for each local variable and named function.
  7. Scope S1 is initialized as follows: an identifier named value is created in S1 with the initial value 1; another identifier, offset, is created in S1 and initialized to the value undefined. One more identifier, Inner, is created in S1 and its value is set to a newly created instance of the function Inner, inner1.
  8. Execution proceeds to the first line of the function Outer. The current scope is S1. The first line sets the value of offset to 1. The next line of yet unhandled code is the last line of the function Outer, the return statement. It refers to the identifier Inner, which readily evaluates, in scope S1, to the created function inner1. The execution of function inner1 ends and execution resumes at top-level, assigning the returned value to the variable inner1.
  9. The same process is repeated to assign to the variable inner2 the result of the second execution of outer1 - in fast-forward - a function instance inner2 that has a creator scope S2, which contains an identifier named value with value 2, and whose parent scope is S0.
  10. Finally, the line with the expression inner1() === 0 is reached. In the current scope, S0, the identifier inner1 is evaluated to the function instance inner1. The function call expression is evaluated.
  11. A new function execution context is created, S3, to support the execution of inner1. What is its parent scope? It's the "creator scope" of inner1, S1, whose parent, in turn, is S0.
  12. The current scope is S3. The first (and last) statement of function Inner is executed. The return statement refers the identifier value. This identifier is searched for in the current scope, but there's no identifier there with that name, so the search continues in its parent scope, S1, where an identifier with name value is found. It has the value 1, which is then summed with the value of the local identifier offset, -1, and returned.
  13. ...

We've just witnessed the great powers of closure functions, or simply closures - an execution scope (containing local variables, parameters, and named functions) can live beyond the function execution that established it, and travels with (closure) functions created within it.

Where's the privacy in closures?

The state contained in an execution scope that was captured by some function instance is accessible only to its code, when it is executing. Having a reference to the function instance, there is no "dot" that can take you to that state, only the function's code can. Taking the running example, the code inner1.value would evaluate to undefined, not to the value 1, which is accessible only to the code of inner1, while it is executing.

This is the only true private state that the JavaScript language provides, and we shall use it in our favor to provide private state to other JavaScript objects. If you would like to know more about JavaScript closures and their underlying execution model, you may read the article JavaScript Closures.

Known patterns of privacy and inheritance

Several known patterns use closures in different ways to achieve some kind of private state. I also introduce a basic pattern used to create classes in JavaScript.

The Module pattern

This pattern utilizes a function to wrap a group of related code - a module. Only selected objects and functions of a module are made public, by means of exporting them. The objects and functions that are not exported will be visible only to exported functions, and are shared by all of them. The most common way to define a module is using the following idiom:

JavaScript
var Calculator = (function(){
    
    // A special value that must be kept safe
    var _pi = 3.141592653;
 
    function circleArea(radius){
        return _pi * radius * radius;
    }
 
    // export only desired 'things'
    return {
        circleArea: circleArea
    };
})();

Which is really just an anonymous function that is immediately executed. Note, unexported things remain accessible only to exported functions. I personally like to use a helper function like the following to improve the readability of a module definition:

JavaScript
function Module(defineModule){
    var exports = {},
        global = this;
 
    defineModule.call(exports);
 
    // Copy properties in 'exports' to global scope
    for(var p in exports){
       if(exports.hasOwnProperty(p)){
          global[p] = exports[p];
       }
    }
}

The next section shows it in use. The article JavaScript Module Pattern: In-Depth covers the Module pattern "in-depth".

The Prototype-based Inheritance Pattern

This pattern exhibits no privacy whatsoever. Rather, it is JavaScript's intrinsic inheritance pattern, and will serve as a basis for comparisons throughout the rest of the article. I'll adapt the example region object presented before into a JavaScript prototype-based class. It will then be possible to create several instances of it.

JavaScript
// Region1 class
Module(function(){
    // The constructor function of the "Region" class
    function Region(){
        this._inside = 0;
    }
 
    Region.prototype.inside = function(){
        return this._inside;
    };
 
    Region.prototype.enter = function(){
        return ++this._inside;
    };
 
    Region.prototype.leave = function(){
        if(this._inside === 0){
            throw new Error("there's no one inside to leave");
        }
        return --this._inside;
    };
    
    this.Region1 = Region;
});

In JavaScript, a class is represented solely by a function that plays the role of constructor to its instances: that function is called, prepended with the new operator, to initialize every instance of the class. Within the execution of a function that was called this way, the keyword this refers to the instance being created, allowing it to be initialized. The constructor ensures that all instances of a class have similar initial sets of properties.

Every JavaScript function has a special property called prototype containing an object that will become the prototype - model - of objects created using that function as a constructor. Every function may be used as a constructor. Properties that are set in the prototype object are visible, using a get operation on instances of the corresponding class, as long as a property of the same name is not set in the instances themselves, shadowing the inherited property. The prototype inheritance behavior is used to enable methods of a class to be shared by all of its instances.

The Methods Inside the Constructor pattern

This pattern is the only one (that I know of) that enables creating classes with true per-instance private state. This is accomplished by declaring methods of a JavaScript class inside of its constructor function, as properties of the this object. The following example shows how:

JavaScript
// Region2 class
Module(function(){
    // The constructor function of the "Region" class
    function Region(){
        var _inside = 0;
 
        this.inside = function(){
            return _inside;
        };
 
        this.enter = function(){
            return ++_inside;
        };
 
        this.leave = function(){
            if(_inside === 0){
                throw new Error("there's no one inside to leave");
            }
            return --_inside;
        };
    }
 
    this.Region2 = Region;
});

In the example, the _inside variable is accessible only to each instance's methods, which capture the execution context of the constructor call, for each created instance. A region object cannot access the _inside variable of another. Note that this is actually more private than is usual in traditional class-based object models. It is common, useful, and desirable, that an object can access the properties of other instances of the same class.

The downside of this approach is that methods are not shared among instances of the same class, which causes a negative impact on memory size. Also, this design complicates subclassing, which is usually achieved by overriding properties of prototype objects.

Nevertheless, I think that this pattern is perfect for classes intended to be singletons, or ones with only a few instances and no subclasses.

You can read Douglas Crockford's article Private Members in JavaScript for more information on this pattern. He calls methods declared inside a constructor privileged methods and the variables in the constructor privates. (I only disagree with the comment he makes, about the use of his example's "that" variable being due to an error in the JavaScript specification.)

The Safe Factory Pattern

First attempt - a Safe and a Key

My first approach was very simple and intuitive. Not surprisingly, I later found someone had tried it before (see Instance private, class private, package, and friends). In the following code, the createSafe function creates "safe" closure functions, given a key and a secret value:

JavaScript
function createSafe(key, value){
 
    function safe(tryKey){
        if(tryKey !== key){ throw new Error("Access denied"); }
        
        return value;
    }
 
    return safe;
}

A secret value and an opening key are captured by each created safe function. A safe function only returns its secret value if the caller "opens" it with the right key. The following example shows a rewritten Region class that uses this approach:

JavaScript
// Region3 class
Module(function(){
    // An object provides a unique key
    var _moduleKey = {};
 
    function Region(){
        this._safe = createSafe(_moduleKey, {
            inside: 0
        });
    }
 
    Region.prototype.inside = function(){
        return this._safe(_moduleKey).inside;
    };
 
    Region.prototype.enter = function(){
        var fields = this._safe(_moduleKey);
        return ++fields.inside;
    };
 
    Region.prototype.leave = function(){
        var fields = this._safe(_moduleKey);
        if(fields.inside === 0){
            throw new Error("there's no one inside to leave");
        }
 
        return --fields.inside;
    };
 
    this.Region3 = Region;
});

There are several things to note:

  • Safes are created inside the constructor function using the private module key and an instance specific secret value
  • Methods are set in the prototype of the constructor function
  • The usage of a module is required for keeping the safes' key private
  • A given key opens any safe created with it; the same key can be used by many classes within a module
  • Instances of a class have access to the private state of other instances of the same class
  • Safes are set in a (public) property of arbitrary name, on each instance; yet, their enclosed state is private and is accessible only to holders of its associated key
  • The value kept in a safe is read-only: it can only be set when creating the safe; this is a design option, in order to keep the safe's interface as simple as possible - it only has a "get" operation; modifiable private state is achieved by storing an object, whose properties may be changed anytime
  • Access to the private state is done only once per public method, in order to minimize the additional cost of opening the safe

So what's wrong?

The flaw in this approach is that, although a safe is sure to only give away its secret to callers that provided the right key, can the methods that pass the secret key to a safe be sure that the safes are "real", i.e., that they were created by their class?

Since public methods of a class are public, ... well, then they can easily be used to gain access to a module's secret key. Also note that, in general, code is visible to anyone, which makes things even easier to break. Here's how to obtain the secret key of the Region3 module:

JavaScript
function stealSecretKeyOfRegion3(){
    var stolenKey;
 
    function fakeSafe(theKey){
        stolenKey = theKey;
        return {inside: 0};
    }
 
    var impostor = {_safe: fakeSafe};
 
    Region3.prototype.inside.call(impostor);
 
    // Stole the unique "secret" key
    // Use it with any real region
    return stolenKey;
}
 
function breakRegion3(region3){
    var stolenKey = stealSecretKeyOfRegion3();
    return region3._safe(stolenKey);
}
 
var region3 = new Region3();
region3.enter();
 
alert(breakRegion3(region3).inside === 1);

I confess I tried to solve this, but things rapidly got very complicated. The calling method would first test the safe function with a challenge, whose answer could only be given correctly, and in a reasonable amount of time, by a real safe. By receiving a correct response, the calling method would be (almost) sure that the safe function was real, and could then pass it the secret key to obtain the secret value.

This could be done. In fact, it is this kind of technique that Communication Systems use to confirm the identity of each party when a communication channel goes through an insecure medium. The problem with this solution is that even the most trivial challenge would take longer to solve than it is acceptable for the problem at hand. So I quit.

The solution - a Safe, an Opener, and a secure communication channel

The path I took, in the previous approach, actually made me understand what this problem was made of. The solution is to use a secure communication channel. We really shouldn't pass sensitive data through an insecure medium (the safe function arguments and return value). The safe is a closure and holds a secret value. Can it communicate this value by request, and without using the function's return value or arguments for doing so? Of course it can, we've been doing out-of-band communication with closures all along. Here's what I've come up with:

JavaScript
Module(function(){
 
    function createSafeFactory(){
        var _channel;
 
        function create(value){
 
            function safe(){
                _channel = value;
            }
 
            return safe;
        }
 
        function opener(safe){
            if(_channel != null){ throw new Error("Access denied."); }
 
            safe();
 
            var value;
            return value = _channel,
                   _channel = null,
                   value;
        };
 
        opener.create = create;
        return opener;
    }
    
    this.SafeFactory = {
        create: createSafeFactory
    };
});

I note the following things:

  • SafeFactory is not a class, it is just a façade object that exposes a create method (what makes this a Factory is exactly that it creates other objects).
  • The SafeFactory.create method creates a pair of closures that share a private _channel variable, which allows them to communicate out-of-band (without using function arguments or a return value).
  • The create closure assumes the role of the flawed createSafe function. Each time it is called, it creates a safe closure that holds the received secret value.
  • safe closures also have access to the private _channel variable.
  • The opener closure receives a safe and "opens" it. The safe closure is called and places its secret value in the _channel variable. The opener closure reads the value placed in the _channel variable and returns it. Care is taken to clear the _channel variable before returning to ensure that stored secret values are not (memory-)leaked. This process only works if the received safe closure was created with the associated create closure.
  • The choice of returning the opener method as a representation of the safe factory is due to minimizing the cost of the solution, in terms of number of objects created and of speed of execution. If an instance of a SafeFactory class were returned, one more object would be created, and the much frequent access to the opener method would require accessing a property of that object.
  • There's no need for a secret key anymore. Instead, the opener closure takes the role of key. Notice that any code that has access to the opener closure will be able to open the safe closures created by the associated create closure. Because of that, a module is still required to keep the opener closure private.

Mitigating Possible Attacks

If a safe closure is called not by the correct opener closure, but by an "attacker" that is trying to steal the secret value, the worse that could happen would be the safe's secret value being placed in the creating factory's _channel variable. This actually manages to prevent the secret value object from being garbage collected. But what would be the point of such an "attack" if only the corresponding opener closure can access the shared variable's value?

To mitigate an "attack" where a stalled value in the _channel variable could be returned to the caller, in case a fake safe function were passed, the first line of the opener method verifies that the _channel variable has the value of null.

Closures and Scopes in the Safe Factory Pattern

The following diagram shows the closures and their creator scopes that are created by the Safe Factory pattern. Notice the _channel variable, created by executions of the createSafeFactory function. In the diagram, the execution of csf1 that created execution scope S1, created the closures create1 and opener1 that share the private _channel variable. Each time the create1 closure is called, a new safe closure is created and returned. Each safe closure has access to the value parameter, as well as to the private _channel variable.

SafeFactoryPattern/SafeFactoryPatternClosures.png

The Safe Region Class

The following code shows the Region class rewritten to use the Safe Factory pattern:

JavaScript
// Region4 class
Module(function(){
    var _safeOpener = SafeFactory.create();
 
    function Region(){
        this._safe = _safeOpener.create({
            inside: 0
        });
    }
 
    Region.prototype.inside = function(){
        return _safeOpener(this._safe).inside;
    };
 
    Region.prototype.enter = function(){
        var fields = _safeOpener(this._safe);
        return ++fields.inside;
    };
 
    Region.prototype.leave = function(){
       var fields = _safeOpener(this._safe);
       if(fields.inside === 0){
           throw new Error("there's no one inside to leave");
       }
 
       return --fields.inside;
    };
 
    this.Region4 = Region;
});

Notice that now, instead of a secret module key, there is a secret module opener closure. Any code that has access to the opener closure will be able to open the safe closures it created, so a module is still required to keep the opener closure private.

The Safe Property Pattern

One thing that the Safe Factory pattern doesn't address is the choice of names for the properties where the safes are stored. In most cases, you might not even care about the name that is used, as each class will have only one safe property per instance. Yet, what about inheritance? If a class that uses a safe property is subclassed, possibly in a different module, and that subclass also wants to have its own safe property, the names of properties used to store safes become relevant: they must be different. To achieve better debuggability, the property names should also provide some clue as to which class each belongs to.

The Safe Property pattern addresses this issue by providing a guided way for choosing safe property names. It can be used partially, where only a new safe property name is obtained, but creating and opening a safe is still handled the usual way, or fully, where the whole process of obtaining a property name, adding the safe to an instance, and later opening it, is handled by a pair of functions.

JavaScript
// SafeProperty pattern
Module(function(){
    // prefix -> previously used id
    var _map = {};
 
    function createName(prefix){
        if(!prefix) prefix = '';
        
        var id = (prefix in _map) ? (++_map[prefix]) : (_map[prefix] = 1);
 
        return "_safe" + prefix + id;
    }
 
    function createSafeProperty(prefix){
        var _safeProp = createName(prefix),
            _safeOpener = SafeFactory.create();
 
        function add(instance, value){
            instance[_safeProp] = _safeOpener.create(value);
        }
 
        function getter(instance){
            return _safeOpener(instance[_safeProp]);
        }
 
        getter.add = add;
        return getter;
    }
    
    this.SafeProperty = {
        createName: createName,
        create: createSafeProperty
    };
});

Obtaining a Safe Property Name

A version of the region class using the SafeProperty.createName function of the Safe Property pattern to avoid collision of safe property names:

JavaScript
// Region5 class
Module(function(){
    // The safe property prefix is 'Region'
    // Its name will probably be '_safeRegion1'
    var _safeOpener = SafeFactory.create(),
        _safeProp = SafeProperty.createName('Region');
 
    function Region(){
        this[_safeProp] = _safeOpener.create({
            inside: 0
        });
    }
 
    Region.prototype.inside = function(){
        return _safeOpener(this[_safeProp]).inside;
    };
 
    Region.prototype.enter = function(){
        var fields = _safeOpener(this[_safeProp]);
        return ++fields.inside;
    };
 
    Region.prototype.leave = function(){
       var fields = _safeOpener(this[_safeProp]);
       if(fields.inside === 0){
           throw new Error("there's no one inside to leave");
       }
 
       return --fields.inside;
    };
 
    this.Region5 = Region;
});

Complete Abstraction

A version of the region class using the full Safe Property pattern:

JavaScript
// Region6 class
Module(function(){
    var _safePropGet = new SafeProperty('Region').get;
 
    function Region(){
        _safePropGet.add(this, {
            inside: 0
        });
    }
 
    Region.prototype.inside = function(){
        return _safePropGet(this).inside;
    };
 
    Region.prototype.enter = function(){
        var fields = _safePropGet(this);
        return ++fields.inside;
    };
 
    Region.prototype.leave = function(){
       var fields = _safePropGet(this);
       if(fields.inside === 0){
           throw new Error("there's no one inside to leave");
       }
 
       return --fields.inside;
    };
    
    this.Region6 = Region;
});

Cost of the Safe Factory pattern

I have made tests to evaluate the cost of the several presented patterns. I compare the cost of each of the region class versions (except the flawed Region3) to the cost of the Region1 class - the prototype-based inheritance pattern. This class accesses properties through the this keyword, and is the most used inheritance pattern. All cost values are first discounted by the cost of the null region class (not shown here), a class that only does operations that are common to all other classes. The reference Region1 class represents the cost of "instance property access". So, a relative cost of 2 means that a pattern takes twice as long to execute as a single instance property access, or, seen in another way, in that time, two instance properties could be accessed.

Class Pattern Relative cost of execution
Chrome
(v. 8.0.552.224 beta)
Firefox
(v. 4.0 Beta 7)
IE Platform Preview
(v. 9.0.8023.6000)
Safari
(v. 5.0.3)
Region1 Prototype-based inheritance 1 1 1 1
Region2 Methods inside constructor 0.65 1.22 0.28 3.40
Region4 Safe Factory 3.52 5.74 2.76 7.32
Region5 Safe Factory with dynamic property 3.53 6.24 11.60 11.48
Region6 Safe Property 4.43 13.21 12.41 12.59

Results couldn't be more disparate, both across region classes and across browsers. By far, the Chrome browser is the fastest and most uniform browser of the four. It has the smallest variation between the best and worse case relative costs. It is interesting to note how some implementations access instance properties (Region1) faster than they access parent scope variables (Region2), while others behave exactly the opposite. In IE, there's a big difference between using the Safe Factory pattern with a fixed property (this._safe) and using a dynamic property lookup (this[_safePropName]), while in Chrome, there's almost no difference between the two versions.

I should note that these tests measure, as much as possible, the sole effect of choosing one pattern over another. It should not be concluded that the presented costs would be those incurred if this pattern were used in a real application. Real use of this pattern would affect only a small part of a big variety of patterns and instructions in a given code base, so the overall cost of the pattern would be diluted accordingly.

Comparison to the Cost of Common Patterns

I thought I should make a comparison with some widely used pattern and hopefully give evidence on how much speed cost we are accustomed to accept without a blink.

One of the most used "patterns" nowadays is array enumeration with an "each" method that accepts a mapping closure function. This is usually accomplished by using the facilities of known JavaScript frameworks like Prototype.js and jQuery.

The most recent versions of browsers, in the light of ECMAScript 5, already provide a native and faster implementation of an enumeration facility, the Array.forEach method. The Prototype.js framework uses this facility, if present, instead of a slower custom each method implementation. I compare the Array enumeration of these two frameworks with an enumeration using a traditional for loop, and using a mapping closure function.

Pattern Relative cost of execution
Chrome
(v. 8.0.552.224 beta)
Firefox
(v. 4.0 Beta 7)
IE Platform Preview
(v. 9.0.8023.6000)
Safari
(v. 5.0.3)
Basic for loop 1 1 1 1
Prototype each loop
(native forEach)
1.70 1.04 1.49 0.94
jQuery each loop
(not native loop)
1.17 2.92 2.40 1.07
Each closured not native loop 1.24 2.84 2.40 1.12

It's a pity I couldn't test this with IE6. It would probably show very interesting results.

Conclusions

I highlight the following features of the Safe Factory pattern:

  • Is a building block for privacy on any JavaScript object
  • Has an acceptable cost in some of the most recent browser implementations, approaching that of other widely accepted patterns
  • Together with ECMAScript 5's constant properties, enables a completely sane programming model
  • Is a pattern for achieving privacy using closure functions in any dynamic language having a similar execution model

History

  • Version 1 - 2010-12-15 - Initial version.
  • Version 2 - 2010-12-15 - A few corrections on the region object example.
  • Version 3 - 2010-12-16 - A few English wording corrections.
  • Version 4 - 2014-07-18 - Added link to github project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer WebDetails
Portugal Portugal
I am an electrical engineer, but instead I do programming for a living. Currently, I work at WebDetails (a Pentaho company), where among other things, I maintain the CTools' CCC charting library.

"The true sign of intelligence is not knowledge but imagination." [Einstein]

Comments and Discussions

 
QuestionTesting performance on IE8 & an alternative means of privacy Pin
Notre20-Jan-14 18:12
Notre20-Jan-14 18:12 
AnswerRe: Testing performance on IE8 & an alternative means of privacy Pin
Duarte Cunha Leão17-Jul-14 13:20
Duarte Cunha Leão17-Jul-14 13:20 
BugTwo examples of bad work Pin
schreque3-Apr-13 8:35
schreque3-Apr-13 8:35 
GeneralRe: Two examples of bad work Pin
Duarte Cunha Leão17-Jul-14 13:28
Duarte Cunha Leão17-Jul-14 13:28 
GeneralMy vote of 5 Pin
kaushal s15-Mar-12 23:42
kaushal s15-Mar-12 23:42 
GeneralImpressive Pin
David Fernandes (DVF)25-Oct-11 15:39
David Fernandes (DVF)25-Oct-11 15:39 
GeneralRe: Impressive Pin
Duarte Cunha Leão30-Oct-11 9:39
Duarte Cunha Leão30-Oct-11 9:39 
GeneralClarification... Pin
Member 467683329-Mar-11 11:40
Member 467683329-Mar-11 11:40 
GeneralRe: Clarification... Pin
Duarte Cunha Leão31-Mar-11 13:49
Duarte Cunha Leão31-Mar-11 13:49 
GeneralRe: Clarification... Pin
Member 46768331-Apr-11 8:25
Member 46768331-Apr-11 8:25 
GeneralRe: Clarification... Pin
Duarte Cunha Leão1-Apr-11 12:26
Duarte Cunha Leão1-Apr-11 12:26 
GeneralRe: Clarification... Pin
Member 46768331-Apr-11 12:46
Member 46768331-Apr-11 12:46 
GeneralMy vote of 5 Pin
BintEd15-Dec-10 2:35
BintEd15-Dec-10 2:35 
GeneralRe: My vote of 5 Pin
Duarte Cunha Leão15-Dec-10 13:56
Duarte Cunha Leão15-Dec-10 13:56 
GeneralMy vote of 5 Pin
Mel Padden15-Dec-10 0:46
Mel Padden15-Dec-10 0:46 
GeneralStole my thunder Pin
Mel Padden15-Dec-10 0:46
Mel Padden15-Dec-10 0:46 
GeneralRe: Stole my thunder Pin
Duarte Cunha Leão15-Dec-10 13:52
Duarte Cunha Leão15-Dec-10 13:52 
GeneralGreat one Pin
Brij14-Dec-10 19:14
mentorBrij14-Dec-10 19:14 
GeneralMy vote of 5 Pin
maq_rohit14-Dec-10 17:47
professionalmaq_rohit14-Dec-10 17:47 
GeneralMy vote of 5 Pin
DrABELL14-Dec-10 16:19
DrABELL14-Dec-10 16:19 
GeneralRe: My vote of 5 Pin
Duarte Cunha Leão15-Dec-10 13:54
Duarte Cunha Leão15-Dec-10 13:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.