98

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?

Community
  • 1
  • 1
Kaze no Koe
  • 3,254
  • 1
  • 21
  • 22

3 Answers3

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);
Tom Roggero
  • 5,777
  • 1
  • 32
  • 39
  • Couldn't this be just MySingletonClass.prototype._singletonInstance = 1; ? – sivann Jul 10 '12 at 19:59
  • @sivann and then how would you return the first instance? – Tom Roggero Sep 12 '12 at 00:48
  • why use `arguments.callee` rather than `this`. Any performance issues? – shahalpk Dec 30 '12 at 09:52
  • 1
    @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. – Tom Roggero Dec 30 '12 at 15:55
  • @mwilcox the point of singleton is not just an object or stdClass, the point it's a class that cannot be instantiated more than once :) – Tom Roggero Sep 26 '13 at 15:25
  • @TomRoggero, it doesn't make sense because you are essentially turning a constructor function into a factory. Just use a factory. – mwilcox Oct 19 '13 at 01:46
  • @mwilcox Please refer to Addy Osmani explanation on the topic available at http://addyosmani.com/resources/essentialjsdesignpatterns/book/#singletonpatternjavascript compared to the Factory conception within the same book chapter :) – Tom Roggero Oct 20 '13 at 22:45
  • 1
    This strict mode version does not work if you instantiate 'b' before 'a'. – spirit walker Apr 07 '14 at 15:43
  • 6
    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()?? – Diego Apr 08 '14 at 23:30
  • 1
    @Diego the `new` keyword is used only for demonstration purposes. Feel free to never use it :) - although theoretically in JS a class is instantiated using new. – Tom Roggero Apr 09 '14 at 04:20
  • 4
    What if I called `MySingletonClass()` and then `new MySingletonClass()`??? It will produce a Window object. – N K Jun 27 '14 at 08:11
  • Is there any significance to the fact that, in contrast to the non-strict version, the strict version uses a function expression instead of a function declaration, and puts _singletonInstance on the prototype instead of the function itself? Why not just replace "arguments.callee" with "MySingletonClass" in the original and be done with it? – Hans Sep 07 '14 at 00:36
  • @Hans I believe fn.something (not shared across instances) is not the same as fn.prototype.something (shared) - I should summon someone else here. Proof: http://cl.ly/image/3M391P2m2Z23 – Tom Roggero Sep 22 '14 at 19:47
  • 1
    There is no theoretical difference between using `fn._instance` and `fn.prototype._instance` for this case. Both `fn` and `fn.prototype` are objects in JS, so it doesn't matter much. The only practical difference is that when using `fn.prototype._instance` you'll get `this === this._instance` inside every method (a bit leaky). – juandopazo Sep 22 '14 at 20:02
  • without using the new keyword it will not work on strict mode – albanx Sep 17 '15 at 20:19
  • @albanx why would you not use `new`? Singleton is a Class! – Tom Roggero Sep 18 '15 at 16:54
  • Yes you re right but you say that this would work without new – albanx Sep 18 '15 at 17:19
  • @albanx it does work without new. Try copy-pasting what I just put in the strict mode. Works ;) – Tom Roggero Sep 21 '15 at 23:35
  • 1
    @TomRoggero if you delete `new SingletonClass()` line then it will fail (apart from not having `a` defined) because without `new` `this` is undefined. I've added on the first line `if (typeof this === 'undefined') { return new SingletonClass(); }` and it helped. – Kangur Nov 14 '16 at 13:46
  • 1
    In the ES7 version I get a `Unexpected token ;` error in this line: `static instance;`. Tested on Chrome 71.0.3578.98 Win-64bit. – endavid Jan 04 '19 at 11:51
  • 2
    @endavid Chrome 71 does not yet support ES7 static properties. Only ES6. See next solution. – Tom Roggero Jan 09 '19 at 18:45
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
};
bobince
  • 528,062
  • 107
  • 651
  • 834
  • 13
    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? – defines Apr 17 '13 at 14:36
  • 11
    it won't overwrite if you use: var earth = earth || { .... }; – chees Sep 13 '13 at 13:01
  • 3
    It also won't overwrite it if you're using a proper file manager, like requirejs. – mwilcox Sep 25 '13 at 22:19
  • 3
    "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. – Jos Jun 18 '14 at 09:46
  • 10
    ? 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. – bobince Jun 18 '14 at 22:59
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();
Community
  • 1
  • 1
arunpon
  • 382
  • 2
  • 6
  • 6
    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." – defines Apr 15 '13 at 20:04