Simplest/Cleanest way to implement singleton in Ja

2018-12-31 08:48发布

What is the simplest/cleanest way to implement singleton pattern in JavaScript?

30条回答
浮光初槿花落
2楼-- · 2018-12-31 09:34

@CMS and @zzzzBov have both given wonderful answers, but just to add my own interpretation based on my having moved into heavy node.js development from PHP/Zend Framework where singleton patterns were common.

The following, comment-documented code is based on the following requirements:

  • one and only one instance of the function object may be instantiated
  • the instance is not publicly available and may only be accessed through a public method
  • the constructor is not publicly available and may only be instantiated if there is not already an instance available
  • the declaration of the constructor must allow its prototype chain to be modified. This will allow the constructor to inherit from other prototypes, and offer "public" methods for the instance

My code is very similar to @zzzzBov's except I've added a prototype chain to the constructor and more comments that should help those coming from PHP or a similar language translate traditional OOP to Javascripts prototypical nature. It may not be the "simplest" but I believe it is the most proper.

// declare 'Singleton' as the returned value of a self-executing anonymous function
var Singleton = (function () {
    "use strict";
    // 'instance' and 'constructor' should not be availble in a "public" scope
    // here they are "private", thus available only within 
    // the scope of the self-executing anonymous function
    var _instance=null;
    var _constructor = function (name) {
        this.name = name || 'default';
    }

    // prototypes will be "public" methods available from the instance
    _constructor.prototype.getName = function () {
        return this.name;
    }

    // using the module pattern, return a static object
    // which essentially is a list of "public static" methods
    return {
        // because getInstance is defined within the same scope
        // it can access the "private" 'instance' and 'constructor' vars
        getInstance:function (name) {
            if (!_instance) {
                console.log('creating'); // this should only happen once
                _instance = new _constructor(name);
            }
            console.log('returning');
            return _instance;
        }
    }

})(); // self execute

// ensure 'instance' and 'constructor' are unavailable 
// outside the scope in which they were defined
// thus making them "private" and not "public"
console.log(typeof _instance); // undefined
console.log(typeof _constructor); // undefined

// assign instance to two different variables
var a = Singleton.getInstance('first');
var b = Singleton.getInstance('second'); // passing a name here does nothing because the single instance was already instantiated

// ensure 'a' and 'b' are truly equal
console.log(a === b); // true

console.log(a.getName()); // "first"
console.log(b.getName()); // also returns "first" because it's the same instance as 'a'

Note that technically, the self-executing anonymous function is itself a Singleton as demonstrated nicely in the code provided by @CMS. The only catch here is that it is not possible to modify the prototype chain of the constructor when the constructor itself is anonymous.

Keep in mind that to Javascript, the concepts of “public” and “private” do not apply as they do in PHP or Java. But we have achieved the same effect by leveraging Javascript’s rules of functional scope availability.

查看更多
泪湿衣
3楼-- · 2018-12-31 09:35

Not sure why nobody brought this up, but you could just do:

var singleton = new (function() {
  var bar = 123

  this.foo = function() {
    // whatever
  }
})()
查看更多
无与为乐者.
4楼-- · 2018-12-31 09:35

I've found the following to be the easiest Singleton pattern, because using the new operator makes this immediately available within the function, eliminating the need to return an object literal:

var singleton = new (function () {

  var private = "A private value";
  
  this.printSomething = function() {
      console.log(private);
  }
})();

singleton.printSomething();

查看更多
爱死公子算了
5楼-- · 2018-12-31 09:36

I think the cleanest approach is something like:

var SingletonFactory = (function(){
    function SingletonClass() {
        //do stuff
    }
    var instance;
    return {
        getInstance: function(){
            if (instance == null) {
                instance = new SingletonClass();
                // Hide the constructor so the returned object can't be new'd...
                instance.constructor = null;
            }
            return instance;
        }
   };
})();

Afterwards, you can invoke the function as

var test = SingletonFactory.getInstance();
查看更多
唯独是你
6楼-- · 2018-12-31 09:36

Main key is to Undertand Closure importance behind this.So property even inside the inner function will be private with the help of closure.

var Singleton = function () { var instance;

 function init() {

    function privateMethod() {
        console.log("private via closure");
    }

    var privateVariable = "Private Property";

    var privateRandomNumber = Math.random();// this also private

    return {
        getRandomNumber: function () {  // access via getter in init call
            return privateRandomNumber;
        }

    };

};

return {
    getInstance: function () {

        if (!instance) {
            instance = init();
        }

        return instance;
    }

};

};

查看更多
唯独是你
7楼-- · 2018-12-31 09:41

I deprecate my answer, see my other one.

Usually module pattern (see CMS' answer) which is NOT singleton pattern is good enough. However one of the features of singleton is that its initialization is delayed till object is needed. Module pattern lacks this feature.

My proposition (CoffeeScript):

window.singleton = (initializer) ->
  instance = undefined
  () ->
    return instance unless instance is undefined
    instance = initializer()

Which compiled to this in JavaScript:

window.singleton = function(initializer) {
    var instance;
    instance = void 0;
    return function() {
        if (instance !== void 0) {
            return instance;
        }
        return instance = initializer();
    };
};

Then I can do following:

window.iAmSingleton = singleton(function() {
    /* This function should create and initialize singleton. */
    alert("creating");
    return {property1: 'value1', property2: 'value2'};
});


alert(window.iAmSingleton().property2); // "creating" will pop up; then "value2" will pop up
alert(window.iAmSingleton().property2); // "value2" will pop up but "creating" will not
window.iAmSingleton().property2 = 'new value';
alert(window.iAmSingleton().property2); // "new value" will pop up
查看更多
登录 后发表回答