97

Possible Duplicate:
Simplest/Cleanest way to implement singleton in JavaScript?

I'm using this pattern for singletons, in the example the singleton is PlanetEarth:

var NAMESPACE = function () {

    var privateFunction1 = function () {
        privateFunction2();
    };

    var privateFunction2 = function () {
        alert('I\'m private!');
    };

    var Constructors = {};

    Constructors.PlanetEarth = function () {
        privateFunction1();
        privateFunction2();
    };

    Constructors.PlanetEarth.prototype = {
        someMethod: function () {
            if (console && console.log) {
                console.log('some method');             
            }
        }
    };

    Constructors.Person = function (name, address) {
        this.name = name;
        this.address = address;
    };

    Constructors.Person.prototype = {
        walk: function () {
            alert('STOMP!');
        }
    };

    return {
        Person: Constructors.Person, // there can be many
        PlanetEarth: new Constructors.PlanetEarth() // there can only be one!
    };

}();

Since PlanetEarth's constructor remains private, there can only be one.

Now, something tells me that this self-cooked thing isn't the best one can do, mostly because I don't have an academic education and I tend to solve problems in stupid ways. What would you propose as a better alternative my method, where better is defined as stylistically better and/or more powerful?

0

3 Answers 3

181

(1) UPDATE 2019: ES7 Version

class Singleton {
    static instance;

    constructor() {
        if (instance) {
            return instance;
        }

        this.instance = this;
    }

    foo() {
        // ...
    }
}

console.log(new Singleton() === new Singleton());

(2) ES6 Version

class Singleton {
    constructor() {
        const instance = this.constructor.instance;
        if (instance) {
            return instance;
        }

        this.constructor.instance = this;
    }

    foo() {
        // ...
    }
}

console.log(new Singleton() === new Singleton());

Best solution found: http://code.google.com/p/jslibs/wiki/JavascriptTips#Singleton_pattern

function MySingletonClass () {

  if (arguments.callee._singletonInstance) {
    return arguments.callee._singletonInstance;
  }

  arguments.callee._singletonInstance = this;

  this.Foo = function () {
    // ...
  };
}

var a = new MySingletonClass();
var b = MySingletonClass();
console.log( a === b ); // prints: true

For those who want the strict version:

(function (global) {
  "use strict";
  var MySingletonClass = function () {

    if (MySingletonClass.prototype._singletonInstance) {
      return MySingletonClass.prototype._singletonInstance;
    }

    MySingletonClass.prototype._singletonInstance = this;

    this.Foo = function() {
      // ...
    };
  };

var a = new MySingletonClass();
var b = MySingletonClass();
global.result = a === b;

} (window));

console.log(result);
Sign up to request clarification or add additional context in comments.

21 Comments

@shahalpk please refer to the strict mode option, where no arguments.callee (not valid in strict mode) is used. this is never the prototype. That's basically why.
This strict mode version does not work if you instantiate 'b' before 'a'.
Is there a way to do this without using new? In your example you do a = new Singleton which is kind of counter-intuitive. shouldn't it be something like a = Singleton.getInstance() and then b= Singleton.getInstance()??
What if I called MySingletonClass() and then new MySingletonClass()??? It will produce a Window object.
@endavid Chrome 71 does not yet support ES7 static properties. Only ES6. See next solution.
|
24

Why use a constructor and prototyping for a single object?

The above is equivalent to:

var earth= {
    someMethod: function () {
        if (console && console.log)
            console.log('some method');                             
    }
};
privateFunction1();
privateFunction2();

return {
    Person: Constructors.Person,
    PlanetEarth: earth
};

5 Comments

If this is included on a page twice, the latest object will overwrite the earlier. In practice, it happens - sometimes with unexpected consequences. Singleton closure pattern prevents this. So, if you distribute a script/library it will gracefully handle misuse by others who use it. Most of Google's API libraries do this for just that reason. Say I use Google Maps on a site, but want to install a widget by some third party which also includes the maps library explicitly. Should I have to change third party code, or should the library itself gracefully handle this?
it won't overwrite if you use: var earth = earth || { .... };
It also won't overwrite it if you're using a proper file manager, like requirejs.
"It won't if"... please, anyone still could overwrite the singleton by e.g. injecting javascript trough a bookmarklet/favlet when not using the closure pattern. It's not just about you using your own code properly, but also about avoiding malicious attacks.
? You cannot possibly do anything to defend against "attacks". If someone has injected code into your JS origin you have already totally lost. JavaScript was not designed to offer security boundaries inside an origin.
18

Extending the above post by Tom, if you need a class type declaration and access the singleton instance using a variable, the code below might be of help. I like this notation as the code is little self guiding.

function SingletonClass(){
    if ( arguments.callee.instance )
        return arguments.callee.instance;
    arguments.callee.instance = this;
}


SingletonClass.getInstance = function() {
    var singletonClass = new SingletonClass();
    return singletonClass;
};

To access the singleton, you would

var singleTon = SingletonClass.getInstance();

1 Comment

Really, one should try to avoid using 'callee' - from the MDN JavaScript strict mode reference (explaining why callee is not supported in strict mode) - "In normal code arguments.callee refers to the enclosing function. This use case is weak: simply name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed."

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.