【问题标题】:Javascript: best Singleton pattern [duplicate]Javascript:最佳单例模式
【发布时间】:2010-12-10 18:49:48
【问题描述】:

可能重复:
Simplest/Cleanest way to implement singleton in JavaScript?

我将这种模式用于单例,在示例中单例是 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!
    };

}();

由于 PlanetEarth 的构造函数是私有的,所以只能有一个。

现在,有件事告诉我,这种自制的东西并不是最好的,主要是因为我没有受过学术教育,而且我倾向于以愚蠢的方式解决问题。您会提出什么作为我的方法的更好替代方法,其中更好被定义为风格上更好和/或更强大?

【问题讨论】:

    标签: javascript singleton design-patterns


    【解决方案1】:

    (1) 2019 年更新:ES7 版本

    class Singleton {
        static instance;
    
        constructor() {
            if (instance) {
                return instance;
            }
    
            this.instance = this;
        }
    
        foo() {
            // ...
        }
    }
    
    console.log(new Singleton() === new Singleton());
    

    (2) ES6 版本

    class Singleton {
        constructor() {
            const instance = this.constructor.instance;
            if (instance) {
                return instance;
            }
    
            this.constructor.instance = this;
        }
    
        foo() {
            // ...
        }
    }
    
    console.log(new Singleton() === new Singleton());
    

    找到的最佳解决方案: 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
    

    对于那些想要严格版本的人:

    (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);
    

    【讨论】:

    • 已更新为严格版本,先生。请撤销投票。
    • 我投了赞成票,因为我正在寻找一个简单哈希映射的过度烘焙解决方案的示例。 :)
    • 有没有办法在不使用 new 的情况下做到这一点?在您的示例中,您执行了 = new Singleton,这有点违反直觉。不应该是 a = Singleton.getInstance() 然后 b= Singleton.getInstance() 吗??
    • 如果我打电话给MySingletonClass()然后new MySingletonClass()怎么办???它将产生一个 Window 对象。
    • @endavid Chrome 71 还不支持 ES7 静态属性。只有 ES6。请参阅下一个解决方案。
    【解决方案2】:

    Extending Tom 的上述帖子,如果您需要类类型声明并使用变量访问单例实例,下面的代码可能会有所帮助。我喜欢这种表示法,因为代码很少自我指导。

    function SingletonClass(){
        if ( arguments.callee.instance )
            return arguments.callee.instance;
        arguments.callee.instance = this;
    }
    
    
    SingletonClass.getInstance = function() {
        var singletonClass = new SingletonClass();
        return singletonClass;
    };
    

    要访问单例,您可以

    var singleTon = SingletonClass.getInstance();
    

    【讨论】:

    • 真的,应该尽量避免使用“被调用者”——来自 MDN JavaScript 严格模式参考(解释了为什么在严格模式下不支持被调用者)——“在普通代码中,被调用者指的是封闭函数。这个用例很弱:简单地命名封闭函数!此外,arguments.callee 实质上阻碍了内联函数等优化,因为如果访问 arguments.callee,则必须提供对未内联函数的引用。
    【解决方案3】:
    function SingletonClass() 
    {
        // demo variable
        var names = [];
    
        // instance of the singleton
        this.singletonInstance = null;
    
        // Get the instance of the SingletonClass
        // If there is no instance in this.singletonInstance, instanciate one
        var getInstance = function() {
            if (!this.singletonInstance) {
                // create a instance
                this.singletonInstance = createInstance();
            }
    
            // return the instance of the singletonClass
            return this.singletonInstance;
        }
    
        // function for the creation of the SingletonClass class
        var createInstance = function() {
    
            // public methodes
            return {
                add : function(name) {
                    names.push(name);
                },
                names : function() {
                    return names;
                }
            }
        }
    
        // wen constructed the getInstance is automaticly called and return the SingletonClass instance 
        return getInstance();
    }
    
    var obj1 = new SingletonClass();
    obj1.add("Jim");
    console.log(obj1.names());
    // prints: ["Jim"]
    
    var obj2 = new SingletonClass();
    obj2.add("Ralph");
    console.log(obj1.names());
    // Ralph is added to the singleton instance and there for also acceseble by obj1
    // prints: ["Jim", "Ralph"]
    console.log(obj2.names());
    // prints: ["Jim", "Ralph"]
    
    obj1.add("Bart");
    console.log(obj2.names());
    // prints: ["Jim", "Ralph", "Bart"]
    

    【讨论】:

    • 单例的意义不是你没有明确地实例化它,而是(a)只有一个实例(这就是为什么它被称为单例)和(b)它是自动声明的在全局命名空间中?在我看来,Lecleuse 先生的模式是针对“类”而不是单例的。
    • 这段代码实际上是相当危险的误导。 getInstance 函数内部的this 实际上是指全局对象(浏览器中的window)。基本上,singletonInstance 会泄漏到全局范围内——如果你有不止一个使用名称 singletonInstance 的类,那么祝你好运。在严格模式下,你会得到TypeError: this is undefined,这是正确的!
    【解决方案4】:

    为什么要为单个对象使用构造函数和原型设计?

    以上等价于:

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

    【讨论】:

    • 如果在一个页面中包含两次,最新的对象将覆盖较早的对象。在实践中,它会发生——有时会带来意想不到的后果。单例闭包模式可以防止这种情况。因此,如果您分发脚本/库,它将优雅地处理使用它的其他人的滥用。出于这个原因,大多数 Google 的 API 库都是这样做的。假设我在一个网站上使用谷歌地图,但想安装一些第三方的小部件,其中也明确包含了地图库。我应该更改第三方代码,还是应该由库本身优雅地处理这个?
    • 如果你使用它不会覆盖:var earth = earth || { .... };
    • 如果你使用合适的文件管理器,比如 requirejs,它也不会覆盖它。
    • “它不会”...拜托,任何人仍然可以通过例如覆盖单例在不使用闭包模式时通过小书签/favlet 注入 javascript。这不仅关乎您正确使用自己的代码,还关乎避免恶意攻击。
    • ?你不可能做任何事情来防御“攻击”。如果有人将代码注入到您的 JS 源中,您已经完全迷失了。 JavaScript 的设计初衷不是为了在源内提供安全边界。
    猜你喜欢
    • 2012-06-20
    • 2017-10-16
    • 1970-01-01
    • 2013-07-27
    • 2013-12-02
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 2012-01-10
    相关资源
    最近更新 更多