In this post, I will introduce another pattern to add to
my previous post about my JavaScript patterns. In fact, this is an extension to those patterns and combines both patterns together! This is a singleton pattern that creates a constructor!
In this post I will also make use of some aspects of JSquared, so please refer to
the JSquared website for more information on those.
So, private members shared between object instances. What does that mean? What it means is that I want to have some private variables and functions which are accessible to multiple instances of a constructor but without them being part of the constructor or the objects the constructor will create. A fairly well known way of achieving this is by making the prototype of my constructor an object built using something similar to my singleton pattern. This would allow public and private members on the prototype of my object. But that will not give me all I wish to achieve this time and it can be a clumsy syntax.
The code I will use to help explain this concept is designed to be a simple panel object. The object will manage a series of panels on a webpage which are linked. Only one panel can be open at a time. Typically one would construct this with a singleton object which finds all instances of the panels in the DOM and adds some handling to each DOM node accordingly. I will do this in a different way. This code example is of course merely a skeleton.
var Panel = new (function() {
//add a DOM load event
addLoadEvent( function() {
//get all DIV elements with a class of "panel"
document.getElementsByClassName( {cssClass: "panel", tags: "div", callback: function() {
//create a new instance of the Panel constructor for each panel
new Panel(this);
} } );
} );
var panels = [];
function closeAll() {
for (var i = panels.length-1; i>=0; i--) {
panels[i].close();
}
}
//return the Panel constructor
return function(panelNode) {
this.open = function() {
closeAll();
//perform the open logic
}
this.close = function() {
//perform the close logic
}
//add this instance to the all instances array inside the closure
panels.push(this);
}
});
Lets step through each part of this example and see what it does:
var Panel = new (function() {
Create a new variable called
Panel using the singleton pattern.
//add a DOM load event
addLoadEvent( function() {
//get all DIV elements with a class of "panel"
document.getElementsByClassName( {cssClass: "panel", tags: "div", callback: function() {
//create a new instance of the Panel constructor for each panel
new Panel(this);
} } );
} );
Using JSquared methods add an event handler for when the documents loads. The handler will use another JSquared method to find all elements in the document which are
DIVs with a class of
panel and for each one run the supplied function which will create a new instance of
Panel passing in the
DIV node with the class
panel that was found. (see
the JSquared docs for more info on how
getElementsByClassName is used)
var panels = [];
function closeAll() {
for (var i = panels.length-1; i>=0; i--) {
panels[i].close();
}
}
These are the private members which each instance of
Panel will have access to. We have an array of panels which will get filled with each instance of
Panel that is created and we have a
closeAll method that loops through each instance of
Panel and calls its
close method.
//return the Panel constructor
return function(panelNode) {
We are going to return a constructor (using the standard constructor pattern). The variable
Panel that we created at the top of the code example will now take the value of this constructor. In other words,
Panel becomes a constructor which we can create instances of using the
new keyword.
this.open = function() {
closeAll();
//perform the open logic
}
this.close = function() {
//perform the close logic
}
Create
open and
close methods which will perform those actions. In the open method, we first want to close all the panels ensuring only one can be open at any time. To do that we call the private
closeAll method which is available through the closure around
Panel.
//add this instance to the all instances array inside the closure
panels.push(this);
Add this new instance (this line of code is still part of the
Panel constructor) to the private
panels array also available through the closure we have created.
To recap, we use the singleton pattern to execute some logic before returning a constructor which is then available to us later on in the page execution. We can use the closure this creates to make private members, declared inside the self executing function which is the singleton pattern. These private members are available to each instance of the constructor but the members are not available anywhere else within any JavaScript - as is usual for a closure of this type.
This can be a very powerful and useful pattern. When building a large application, I believe it is good to keep public members of all objects to a minimum and I also prefer not to use the prototype of an object unless I am using inheritance. This pattern achieves both of these aims in an elegant and encapsulated way.