How / why does this JS singleton pattern work?

I came across this rather interesting way to create a singleton JavaScript, which can be created using the new keyword, for example var x = new SingletonClass() . I'm pretty good at variables and closures, etc., but it's hard for me to understand why this block of code works the way it does.

 // EDIT: DO NOT USE this code; see the answers below function SingletonClass() { this.instance = null; var things = []; function getInstance() { if (!this.instance) { this.instance = { add: function(thing) { things.push(thing); }, list: function() { console.log(things.toString()); } }; } return this.instance; } return getInstance(); } var obj1 = new SingletonClass(); obj1.add("apple"); obj1.list(); //"apple" var obj2 = new SingletonClass(); obj2.add("banana"); obj1.list(); //"apple,banana" obj2.list(); //"apple,banana" obj1.add("carrot"); obj1.list(); //"apple,banana,carrot" obj2.list(); //"apple,banana,carrot" 

My intuition says that every time a new SingletonClass is created, this refers to this new new object, but after the constructor completely returns a separate object, I would think that this just gets discarded. But he hangs around. How? Why?

There are some tiniest details that I miss. Can anyone shed light on him?

EDIT: Turns out this code is bad. The reason it is "magically" seems to contain a reference to an instance is because it actually silently stores it in a global object. This is bad practice at best, and certainly error-prone.

+7
source share
5 answers

Do not confuse this inside the getInstance function that this is a global window object, so you create an object and assign it to the window object, and the next time you call the constructor, you check to see if window.instance .

Code this.instance = null; is pointless, just confusing you. Delete this will not change anything.

Following is the MDN .

When new foo(...) code is executed, the following events occur:

  • A new object is created that inherits from foo.prototype.
  • The constructor function foo is called with the specified arguments, and this is due to the newly created object. new foo is equivalent to new foo() , that is, if no argument list is specified, foo is called without arguments.
  • The object returned by the constructor function becomes the result of an entire new expression. If the constructor function does not explicitly return the object, the object created in step 1 instead is used. (Typically, constructors do not return values, but can select this if they want to override the creation of a regular Process object.)

Pay attention to step 3, when you have a return statement in the constructor, the return result will be the result of a new expression.

+3
source

When the constructor calls getInstance() , the this pointer inside getInstance() set to window , so this.instance inside it has a global variable window.instance .

This code simply uses the global window.instance variable to track a single instance and may be LOT easier than it is.

An easier way to implement this would be. One global instance is stored as a property of the function itself, not a top-level global variable.

  function SingletonClass() { var things = []; // if there is a previous instance, return it if (SingletonClass.inst) { return SingletonClass.inst; } // if not called with 'new', force it if (!this instanceof SingletonClass) { return new SingletonClass(); } // remember the first created instance SingletonClass.inst = this; // add methods that can see our private `things` variable this.add = function(thing) { things.push(thing); } this.list = function() { console.log(things.toString()); } } 
+2
source

Using window - getInstance this does not match SingletonClass this:

 function Singleton() { console.log("This is:", this); this.instance = null; function _get() { console.log("_get this is:", this); if (!this.instance) { console.log("Instance is null"); this.instance = { test: 1 }; } return this.instance; } return _get(); } var x = new Singleton(); // Output is: This is: Singleton _get this is: Window Instance is null 

The best way to implement a Singleton template in JavaScript might be:

 function Singleton() { // Handle not being called with `new` if (!this instanceof Singleton) { return new Singleton(); } var instantiating = !!Singleton.instance, things = []; Singleton.instance = instantiating ? this : Singleton.instance; if (instantiating) { this.list = function() { console.log(things.toString()); } this.add = function(item) { things.push(item); } } return Singleton.instance; } 

It still suffers from limitations. For example, if someone replaces Singleton.instance with another object, then tests between different instances of Singleton may be interrupted - for example:

 var x = new Singleton(); // Har, har, we be breaking things! Singleton.instance = {"not": "the", "same": "at", "all": true}; var y = new Singleton(); console.log(x === y); // false 
+1
source

The this in the getInstance function indicates a Window object. In the SingletonClass function, it points to a SingletonClass object.

With a lexical scope, the add and list functions always refer to the same things array.

+1
source

This is apparently a good reference to singletones:

The simplest / cleanest way to implement singleton in JavaScript?

Here is an example that relates to what you do above:

 var myInstance = { method1: function () { return "Apple"; }, method2: function () { return "Orange"; }, banana: "Banana" }; myInstance.grape = "Grape"; console.log(myInstance.method1(), myInstance.method2(), myInstance.banana, myInstance.grape); 

Here is a working link:

http://www.quirkscode.com/flat/forumPosts/singleton/singleton.html

0
source

All Articles