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


当前回答

这个知识是基于我正在学习Java,虽然Java和Javascript是不同的,但单例的概念和Java如何做到这一点是一样的。在我看来,JS的类样式本身是干净的,而不是var初始化。

class Singleton {
    // use hashtag which entails that the variable can only be accessed from self scope
    static #instance = null;
    static getInstance() {
        if (this.#instance === null) this.#instance = new Singleton();
        return this.#instance;
    }

    // some class property
    hello = 'world';

    // or initialize the variable in the constructor, depend on your preference
    constructor() {
        // this.hello = 'world';
    }

    /* you can also add parameters on the constructor & getInstance
     * e.g. 
     * static getInstance(param1, param2) {...new Singleton(param1, param2)}
     * constructor(param1, param2) {...}
     */

}




// this is the same code for java and normal way for singleton for class
// just use static so you can get instance


// testing the singleton
var s1,s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();

// you cannot access the property, immediately
if (Singleton.hello === undefined) console.log('getInstance so you can access this');

console.log(s1.hello);
// result: "world"

console.log(s2.hello);
// result: "world"


// set the value of Singleton object
s2.hello = "hi";
    console.log(s1.hello);
    // result: "hi"

    console.log(s2.hello);
    // result: "hi"
// this is just an evidence which means that they are the same even in property level

if (s1 === s2) console.log("S1 & S2 is the same object");
// result: "S1 & S2 is the same object"

// don't use something like `var s1 = new Singleton();`
// this will defeat your purpose of just (1 object), one instance of class

其他回答

最明确的答案应该是Addy Osmani所著的《学习JavaScript设计模式》一书。

var mySingleton = (function () { // Instance stores a reference to the singleton var instance; function init() { // Singleton // Private methods and variables function privateMethod(){ console.log( "I am private" ); } var privateVariable = "I'm also private"; var privateRandomNumber = Math.random(); return { // Public methods and variables publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public", getRandomNumber: function() { return privateRandomNumber; } }; }; return { // Get the singleton instance if one exists // or create one if it doesn't getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })();

我喜欢将单例模式和模块模式结合起来使用,并将初始化时分支与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 ) );

对我来说,最干净的方法是:

const singleton = new class {
    name = "foo"
    constructor() {
        console.log(`Singleton ${this.name} constructed`)
    }
}

使用这种语法,您可以确定您的单例是并且将保持惟一的。您还可以享受类语法的甜蜜,并按预期使用它。

(注意,类字段需要节点v12+或现代浏览器。)

我认为最简单的方法是声明一个简单的对象文字:

var myInstance = {
  method1: function () {
    // ...
  },
  method2: function () {
    // ...
  }
};

如果你想在你的单例实例上有私有成员,你可以这样做:

var myInstance = (function() {
  var privateVar = '';

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // All private members are accessible here
    },
    publicMethod2: function () {
    }
  };
})();

这被称为模块模式,它基本上允许您通过使用闭包来封装对象上的私有成员。

如果你想阻止对单例对象的修改,你可以使用ES5 object .freeze方法冻结它。

这将使对象不可变,防止对其结构和值进行任何修改。

如果你正在使用ES6,你可以很容易地使用ES模块来表示一个单例,你甚至可以通过在模块范围内声明变量来保持私有状态:

// my-singleton.js
const somePrivateState = []

function privateFn () {
  // ...
}

export default {
  method1() {
    // ...
  },
  method2() {
    // ...
  }
}

然后你可以简单地导入单例对象来使用它:

import myInstance from './my-singleton.js'
// ...

你没说"在浏览器里"否则,你可以使用Node.js模块。这些对于每个require调用都是相同的。基本的例子:

foo.js的内容: Const circle = require('./circle.js'); console.log('半径为4的圆的面积为${circle.area(4)} '); circle.js的内容: const PI = Math.PI; 出口。area = (r) => PI * r * r; 出口。周长= (r) => 2 * PI * r;

注意,您不能访问圆。PI,因为它没有导出。

虽然这在浏览器中不起作用,但它简单而干净。