Javascript:最佳单例模式

Posted

技术标签:

【中文标题】Javascript:最佳单例模式【英文标题】:Javascript: best Singleton pattern [duplicate] 【发布时间】: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 的构造函数是私有的,所以只能有一个。

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

【问题讨论】:

【参考方案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 的设计初衷不是为了在源内提供安全边界。

以上是关于Javascript:最佳单例模式的主要内容,如果未能解决你的问题,请参考以下文章

Unity单例模式最佳实践(附代码)

单例模式的最佳实现(Java)

单例模式的最佳实现(Java)

「源码分析」— 为什么枚举是单例模式的最佳方法

「源码分析」— 为什么枚举是单例模式的最佳方法

JavaScript设计模式(单例模式)