There are a number of different approaches that can be taken to creating singleton patterns in JavaScript – this one is both simple and effective and supports lazy initialisation.
The following code shows typical usage of constructor functions:
function Vehicle() { // #1 this.objectString = "I am a vehicle"; this.serialNo = 0; } function Car() { // #3 return new Vehicle(); } var vehicle1 = new Vehicle(); // #2 vehicle1.serialNo = 1; var vehicle2 = new Vehicle(); vehicle2.serialNo = 2; var car = new Car(); // #4 console.log("vehicle1:"); // #5 console.log(vehicle1); console.log("\n\nvehicle2:"); console.log(vehicle2); console.log("\n\ncar:"); console.log(car);
#1: We define a constructor for the Vehicle object.
#2: We use this constructor to instantiate two new Vehicle objects. The javascript new operator will create an empty object and pass this to the constructor, which can then reference it with the this keyword. This is used in the Vehicle constructor to establish two properties of the new object. One of these properties is then updated by each instance of the Vehicle object.
#3: However, the constructor function is not obliged to make use of the object created by new, it can return a completely different object. This is what the Car constructor does. It ignores the object created by new and instead returns an instance of the Vehicle object.
#4: The car variable here is calling the Car constructor, but it will end up referencing a Vehicle object.
#5: The output of these statements will demonstrate that all three objects are indeed Vehicle objects:
Our singleton pattern can make use of this ability of a constructor to return an alternative object by returning an existing instance, if there is one. So how does the constructor know whether there is an existing instance and how can it return it?
To achieve this we will make use of a little-known and little-used feature of JavaScript functions – their ability to have properties. Remember that functions in JavaScript are objects, which means not only can we can do useful things like passing them around as arguments to other functions or having other functions return them, we can also attach properties to them. Consider the code below:
function Vehicle() { // #1 this.objectString = "I am a vehicle"; this.serialNo = 0; Vehicle.funcprop = "This is a property of the function, not the object"; } var vehicle = new Vehicle(); // #2 console.log("vehicle object:"); // #3 console.log(vehicle); console.log("\n\nVehicle function property:"); console.log(Vehicle.funcprop);
#1: This is same constructor function as before, except that it now has an additional line that creates a property of the function.
#2: Here we instantiate a Vehicle object. When the constructor runs, the new object will be populated with the objectString and serialNo properties. The funcprop property will not be part of the object, but will be a property of the constructor function.
#3: The output from these statements will show that the Vehicle object has no access to the funcprop property, which is accessed through the function name:
So, now we can combine these two features to create an effective JavaScript singleton pattern that supports lazy initialisation:
function Singleton() { // #1 if (Singleton.instance) { return Singleton.instance; } else { Singleton.instance = this; } } var instance1 = new Singleton(); // #2 instance1.prop1 = true; var instance2 = new Singleton(); // #3 instance2.prop2 = "This is the same object"; console.log("instance1:"); // #4 console.log(instance1); console.log("\n\ninstance2"); console.log(instance2); console.log("\n\ninstance1 === instance2 is " + (instance1 === instance2));
#1: The constructor for our Singleton class first checks for the presence of its own property called instance. If this property does not exist (which will be the case the first time the constructor is called), then the constructor simply sets this property to the object created by new. On any subsequent invocation of the constructor, the property will exist so the constructor ignores the object created by new and returns the existing object instead.
#2: Here we call the constructor for the first time and add a new property to the object. For this invocation, a new object will be created and this object will be set in the constructor property called instance.
#3: Here we call the constructor for the second time and add a second property. For this invocation, the existing instance will be returned.
#4: The output of these statements demonstrate that both instance1 and instance2 reference the same object:
Be First to Comment