用JavaScript实现单例模式的最简单/最干净的方法是什么?


当前回答

在Node.js版本6中工作:

class Foo {
  constructor(msg) {

    if (Foo.singleton) {
      return Foo.singleton;
    }

    this.msg = msg;
    Foo.singleton = this;
    return Foo.singleton;
  }
}

我们测试:

const f = new Foo('blah');
const d = new Foo('nope');
console.log(f); // => Foo { msg: 'blah' }
console.log(d); // => Foo { msg: 'blah' }

其他回答

我认为我已经找到了用JavaScript编程的最简洁的方法,但您需要一些想象力。我是从《JavaScript: the Good Parts》一书中的一个工作技巧中得到这个想法的。

不用new关键字,你可以这样创建一个类:

function Class()
{
    var obj = {}; // Could also be used for inheritance if you don't start with an empty object.

    var privateVar;
    obj.publicVar;

    obj.publicMethod = publicMethod;
    function publicMethod(){}

    function privateMethod(){}

    return obj;
}

你可以这样实例化上面的对象:

var objInst = Class(); // !!! NO NEW KEYWORD

现在有了这个工作方法,你可以创建一个像这样的单例:

ClassSingleton = function()
{
    var instance = null;

    function Class() // This is the class like the above one
    {
        var obj = {};
        return obj;
    }

    function getInstance()
    {
        if( !instance )
            instance = Class(); // Again no 'new' keyword;

        return instance;
    }

    return { getInstance : getInstance };
}();

现在可以通过调用来获取实例

var obj = ClassSingleton.getInstance();

我认为这是最简洁的方法,因为完整的“类”甚至无法访问。

对我来说,最简单/最干净的也意味着简单地理解,没有花哨的东西,就像Java版本的讨论中讨论的那样:

在Java中实现单例模式的有效方法是什么?

从我的角度来看,最简单/最干净的答案是:

Jonathan的回答是:在Java中实现单例模式的有效方法是什么?

它只能部分地翻译成JavaScript。JavaScript中的一些区别是:

构造函数不能是私有的 类不能有声明的字段

但考虑到最新的ECMA语法,有可能接近:

作为JavaScript类示例的单例模式

 class Singleton {

  constructor(field1,field2) {
    this.field1=field1;
    this.field2=field2;
    Singleton.instance=this;
  }

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance=new Singleton('DefaultField1','DefaultField2');
    }
    return Singleton.instance;
  }
}

示例使用

console.log(Singleton.getInstance().field1);
console.log(Singleton.getInstance().field2);

例子的结果

DefaultField1
DefaultField2

我喜欢将单例模式和模块模式结合起来使用,并将初始化时分支与Global NS检查结合起来使用,并将它们包装在一个闭包中。

在初始化单例后环境不会改变的情况下,使用立即调用的object-literal来返回一个充满实用程序的模块,该模块将持续一段时间。

我没有传递任何依赖项,只是在它们自己的小世界中调用单例对象——唯一的目标是:为事件绑定/解绑定创建一个实用程序模块(设备方向/方向变化也可以在这种情况下工作)。

window.onload = ( function( _w ) {
    console.log.apply( console, ['it', 'is', 'on'] );
    ( {
        globalNS : function() {
            var nameSpaces = ["utils", "eventUtils"],
                nsLength = nameSpaces.length,
                possibleNS = null;

            outerLoop:
            for ( var i = 0; i < nsLength; i++ ) {
                if ( !window[nameSpaces[i]] ) {
                    window[nameSpaces[i]] = this.utils;
                    break outerLoop;
                };
            };
        },
        utils : {
            addListener : null,
            removeListener : null
        },
        listenerTypes : {
            addEvent : function( el, type, fn ) {
                el.addEventListener( type, fn, false );
            },
            removeEvent : function( el, type, fn ) {
                el.removeEventListener( type, fn, false );
            },
            attachEvent : function( el, type, fn ) {
                el.attachEvent( 'on'+type, fn );
            },
            detatchEvent : function( el, type, fn ) {
                el.detachEvent( 'on'+type, fn );
            }
        },
        buildUtils : function() {
            if ( typeof window.addEventListener === 'function' ) {
                this.utils.addListener = this.listenerTypes.addEvent;
                this.utils.removeListener = this.listenerTypes.removeEvent;
            } else {
                this.utils.attachEvent = this.listenerTypes.attachEvent;
                this.utils.removeListener = this.listenerTypes.detatchEvent;
            };
            this.globalNS();
        },
        init : function() {
            this.buildUtils();
        }
    } ).init();
} ( window ) );
function Once() {
    return this.constructor.instance || (this.constructor.instance = this);
}

function Application(name) {
    let app = Once.call(this);

    app.name = name;

    return app;
}

如果你在上课:

class Once {
    constructor() {
        return this.constructor.instance || (this.constructor.instance = this);
    }
}

class Application extends Once {
    constructor(name) {
        super();

        this.name = name;
    }
}

测试:

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

let app1 = new Application('Foobar');
let app2 = new Application('Barfoo');

console.log(app1 === app2);
console.log(app1.name); // Barfoo

我反对我的回答,看看我的另一个。

通常,模块模式(参见Christian C. Salvadó的答案)不是单例模式就足够好了。然而,单例的一个特点是它的初始化会延迟到需要对象时。模块模式缺乏这个特性。

我的命题(CoffeeScript):

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

在JavaScript中编译为:

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

然后我可以做以下事情:

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