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:
var obj = {};
obj.prop1 = 1;
obj.prop2 = 2;
delete obj.prop1;
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:
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:
var author = {
firstName: "Duarte",
lastName: "Leão"
};
function Outer(value){
var offset = 1;
function Inner(){
var offset = -1;
return value + offset;
}
return Inner;
}
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:
- 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. - 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. - 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. - Execution proceeds in the line
var inner1 = Outer(1)
. - 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. - 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. - 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. - 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
. - 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. - 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. - 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.
- 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. - ...
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:
var Calculator = (function(){
var _pi = 3.141592653;
function circleArea(radius){
return _pi * radius * radius;
}
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:
function Module(defineModule){
var exports = {},
global = this;
defineModule.call(exports);
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.
Module(function(){
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:
Module(function(){
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:
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:
Module(function(){
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:
function stealSecretKeyOfRegion3(){
var stolenKey;
function fakeSafe(theKey){
stolenKey = theKey;
return {inside: 0};
}
var impostor = {_safe: fakeSafe};
Region3.prototype.inside.call(impostor);
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:
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.
The Safe Region Class
The following code shows the Region
class rewritten to use the Safe Factory pattern:
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.
Module(function(){
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:
Module(function(){
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:
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.