Throughout this article, I will use Java's Object-Oriented Programming terminologies such as static, private and public.
This pattern exploits scoping to allow for private and public fields and methods. It is named as Self-construction because the class function (or constructor) has to assign instance methods one by one.
Employee = (function(){
// private static field
var numEmployees = 0;
// class function a.k.a. constructor
function cls()
{
// private instance fields
var name = "";
var self = this;
// public instance field
this.age = 10;
numEmployees++;
// private instance method
function incrementInternal()
{
// must use self instead of this
self.age ++;
}
// public instance method
this.getName = function(){
return name;
};
this.setName = function(newName){
name = cls.capitalize(newName);
};
this.increment = function(){
incrementInternal();
};
this.getAge = function(){
return this.age;
};
}
// public static field
cls.staticVar = 0;
// public static method
cls.capitalize = function(name){
return name.substring(0, 1).toUpperCase() +
name.substring(1).toLowerCase();
};
// private static method
function createWithName(name)
{
var obj = new cls();
obj.setName(cls.capitalize(name));
return obj;
}
return cls;
})();
john = new Employee();
john.setName("john");
mary = new Employee();
mary.setName("mary");
mary.increment();
alert("John's name: " + john.getName() + ", age==10: "+john.getAge());
alert("Mary's name: " + mary.getName() + ", age==11: "+mary.getAge());
Note that Employee
is actually the cls
function that is returned.
A function defines a new environment and so each instance of Employee
has its own copies of name
and self
.
The fields name
and self
are private because they are local to Employee
's scope, and hence inaccessible externally.
The method incrementInternal
is also private for the same reason.
The only downside is that the instance methods have to be assigned one by one for each new instance,
instead of using Employee.prototype
to auto-assign.
Manager = (function(parentCls){
function cls(arg1)
{
// call parent's constructor to initialize instance methods and fields
parentCls.call(this, arg1);
// Save a copy of parent's method before overriding.
var oldGetName = this.getName;
this.getName = function(){
// call parent's method
return oldGetName.call(this).toUpperCase();
};
}
// copy parentCls's static fields and methods to cls
// If using jQuery, can be achieved alternatively using:
// $.extend(cls, parentCls);
cls.createWithName = parentCls.createWithName;
cls.staticVar = parentCls.staticVar;
return cls;
})(Employee);
mgr = new Manager();
mgr.setName('john');
alert("John's name in uppercase: "+mgr.getName());
Note that this is not true inheritance since cls.staticVar
and parentCls.staticVar
are separate copies. If you update
one, the other won't change. To illustrate:
Manager.staticVar ++;
alert(Employee.staticVar); // returns 0
This behavior can be explained by the fact that a "class" in JavaScript is actually an object. Subclasses of a class are merely copies of the parent class object with possibly additional fields and methods.
There is no way to make both staticVar
's to be the same copy. The workaround is to change staticVar
to be a private static field in the parent class and encapsulate
(or protect) the field so that changes to the value of staticVar
have to go through a method.
On the other hand, if staticVar
references an object, then cls.staticVar
and parentCls.staticVar
would
reference the same
object, and changes to the properties of that object will be visible through both staticVar
's.
A singleton class is one that has only a single instance of it, logically. What I mean by logically is that in JavaScript, we can achieve singleton-ness by not using the traditional method of instantiating a single copy and preventing the class from being instantiated further. In JavaScript, we don't even have to define a class and instantiate it. The singleton "instance" can simply be an object, not a true instance of a class, as illustrated below:
Singleton = (function(parentCls){
// private static field
var field;
var cls = {};
// TODO : copy fields and methods from parentCls to cls
// If using jQuery:
// var cls = $.extend({}, parentCls);
// private static method
function method2(){
cls.method1();
}
// public static field
cls.field = 0;
// public static method
cls.method1 = function(){};
cls.method2 = function(){
cls.method1();
};
return cls;
})(parentCls);
If there is no parent class to inherit from, simply use {}
for parentCls
.
The benefit of using an object as a singleton is that new Singleton()
will result in an error since Singleton
is not a function, thereby preventing multiple instances.
Same as Pattern 1, but without using the this
and new
keywords.
Employee = (function(){
function cls()
{
var name = '';
var age = '';
var obj = {
publicField : 0,
getName: function(){return name;},
increment: function(){obj.publicField++;}
};
return obj;
}
return cls;
})();
john = Employee(); // no new needed
mary = new Employee(); // doesn't hurt, but less efficient
If we do not use new
, this
in Employee
will refer to something else, for example, the Window
object if the script
is executed in a browser. But in general, what this
is, depends on the surrounding context.
The new
keyword assigns this
to a copy of Employee.prototype
upon entrance to the Employee
function,
and causes the function to return this copy by default if the function doesn't return anything. If we don't use this
, then
this copy is wasted.
Manager = (function(parentCls){
function cls()
{
var obj = parentCls();
var oldGetName = obj.getName;
obj.getName = function(){
return oldGetName().toUpperCase();
};
return obj;
}
return cls;
})(Employee);
This pattern relies on a function's prototype
object.
Employee = (function(){
// private static var
var staticVar;
// private static method
function staticMethod(){}
function cls()
{
// public instance field
this.name = "";
}
// public static method
cls.staticMethod = function(){};
cls.prototype = {
// public instance method
getName: function(){ return this.name; }
// ...
};
return cls;
})();
The main problem is that there seems to be no way to define private instance fields and methods.
The reason is that the functions in the prototype
object has to access the instance fields and methods through this
.
But defining fields as properties of this
has a side effect of exposing the fields as public.
Manager = (function(parentCls){
function cls()
{
this.type = 'Manager';
}
cls.prototype = new parentCls();
var proto = cls.prototype;
// override parent's getName
var oldGetName = proto.getName;
proto.getName = function(){
return oldGetName.call(this).toUpperCase();
};
// alternatively, since name is public, we can just use this.name
proto.getName = function(){
return this.name.toUpperCase();
};
return cls;
})(Employee);
Pattern 1 and 2 are very similar and equally powerful in allowing private fields and methods to be defined. The obj
variable in Pattern 2 is in fact analogous to this
in Pattern 1. The downside is that instance methods have to be
manually assigned one by one but the performance impact should be minimal. For Pattern 2, inheritance is slightly less
efficient since parentCls
returns a new object each time it is called. This downside is absent in Pattern 1 since
parentCls
simply assigns instance methods and fields to an existing object.
On the other hand, for Pattern 3, there seems no way to define private fields and methods, thus exposing every instance fields to be public and leading to a violation of the data encapsulation principle.