用JavaScript实现单例最简单/干净的方法

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

296378 次浏览

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

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'
// ...

我认为最干净的方法是:

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

之后,可以调用函数as

var test = SingletonFactory.getInstance();

剥猫皮的方法不止一种:)根据你的口味或特定需求,你可以应用任何一种建议的解决方案。我个人在任何可能的情况下都会选择Christian C. Salvadó的第一个解决方案(当你不需要隐私的时候)。

因为这个问题是关于最简单和最干净的,所以这个问题是赢家。甚至:

var myInstance = {}; // Done!

这(引用自我的博客)……

var SingletonClass = new function() {
this.myFunction() {
// Do stuff
}
this.instance = 1;
}

没有太大的意义(我的博客例子也没有),因为它不需要任何私有变量,所以它几乎与:

var SingletonClass = {
myFunction: function () {
// Do stuff
},
instance: 1
}

我反对我的回答,见另一个

通常模块模式(参见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

我不确定我是否同意用模块模式来代替单例模式。我经常看到单例对象在完全没有必要的地方被使用和滥用,我确信模块模式填补了程序员使用单例对象的许多空白。然而,模块模式是一个单例。

模块模式:

var foo = (function () {
"use strict";
function aPrivateFunction() {}
return { aPublicFunction: function () {...}, ... };
}());

在模块模式中初始化的所有内容都发生在声明Foo时。此外,模块模式可用于初始化构造函数,然后可以多次实例化构造函数。虽然模块模式是许多工作的正确工具,但它并不等同于单例模式。

单例模式:

简式
var Foo = function () {
"use strict";
if (Foo._instance) {
// This allows the constructor to be called multiple times
// and refer to the same instance. Another option is to
// throw an error.
return Foo._instance;
}
Foo._instance = this;
// Foo initialization code
};
Foo.getInstance = function () {
"use strict";
return Foo._instance || new Foo();
}
长格式,使用模块模式
var Foo = (function () {
"use strict";
var instance; //prevent modification of "instance" variable
function Singleton() {
if (instance) {
return instance;
}
instance = this;
//Singleton initialization code
}
// Instance accessor
Singleton.getInstance = function () {
return instance || new Singleton();
}
return Singleton;
}());

在我提供的两个版本的单例模式中,构造函数本身都可以用作访问器:

var a,
b;
a = new Foo(); // Constructor initialization happens here
b = new Foo();
console.log(a === b); //true

如果你不习惯这样使用构造函数,你可以在if (instance)语句中抛出一个错误,并坚持使用长形式:

var a,
b;
a = Foo.getInstance(); // Constructor initialization happens here
b = Foo.getInstance();
console.log(a === b); // true

我还应该提到,单例模式很适合隐式构造函数模式:

function Foo() {
if (Foo._instance) {
return Foo._instance;
}
// If the function wasn't called as a constructor,
// call it as a constructor and return the result
if (!(this instanceof Foo)) {
return new Foo();
}
Foo._instance = this;
}
var f = new Foo(); // Calls Foo as a constructor
-or-
var f = Foo(); // Also calls Foo as a constructor

我需要几个单人间:

  • 延迟初始化
  • 初始参数

这就是我想到的:

createSingleton ('a', 'add', [1, 2]);
console.log(a);


function createSingleton (name, construct, args) {
window[name] = {};
window[construct].apply(window[name], args);
window[construct] = null;
}


function add (a, b) {
this.a = a;
this.b = b;
this.sum = a + b;
}
  • args必须是Array,所以如果你有空变量,只需要传入[]

  • 我在函数中使用了窗口对象,但我本可以传入一个参数来创建自己的作用域

  • 名称和构造参数只有String,以便window[]工作,但通过一些简单的类型检查,window.namewindow.construct也是可能的。

这应该可以工作:

function Klass() {
var instance = this;
Klass = function () { return instance; }
}

Christian C. Salvadó的zzzzBov的回答都给出了很好的答案,但只是添加我自己的解释,基于我已经从PHP/Zend框架转向了沉重的Node.js开发,其中单例模式很常见。

以下注释记录的代码基于以下需求:

  • 函数对象的一个且只有一个实例可以被实例化
  • 实例不是公开可用的,只能通过公共方法访问
  • 构造函数不是公开可用的,只有在没有可用实例的情况下才可以实例化
  • 构造函数的声明必须允许修改其原型链。这将允许构造函数从其他原型继承,并提供“;public"实例的方法

我的代码与zzzzBov的答案非常相似,除了我在构造函数中添加了一个原型链和更多的注释,这些注释应该有助于那些来自PHP或类似语言的人将传统的OOP转换为JavaScript的原型性质。它可能不是“最简单的”;但我相信这是最合适的。

// Declare 'Singleton' as the returned value of a self-executing anonymous function
var Singleton = (function () {
"use strict";
// 'instance' and 'constructor' should not be available 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'

请注意,从技术上讲,自执行匿名函数本身就是一个单例函数,Christian C. Salvadó提供的代码很好地演示了这一点。这里唯一的问题是,当构造函数本身是匿名的时,不可能修改构造函数的原型链。

请记住,在JavaScript中,“公共”和“私有”的概念并不像在PHP或Java中那样适用。但是,通过利用JavaScript的函数作用域可用性规则,我们也达到了同样的效果。

模块模式:以“更易读的风格”。您可以很容易地看到哪些方法是公共的,哪些是私有的

var module = (function(_name){
/* Local Methods & Values */
var _local = {
name : _name,
flags : {
init : false
}
}


function init(){
_local.flags.init = true;
}


function imaprivatemethod(){
alert("Hi, I'm a private method");
}


/* Public Methods & variables */


var $r = {}; // This object will hold all public methods.


$r.methdo1 = function(){
console.log("method1 calls it");
}


$r.method2 = function(){
imaprivatemethod(); // Calling private method
}


$r.init = function(){
inti(); // Making 'init' public in case you want to init manually and not automatically
}


init(); // Automatically calling the init method


return $r; // Returning all public methods


})("module");

现在你可以使用公共方法,比如

module.method2 ();/ /→我正在调用一个私有方法而不是一个公共方法警报(“嗨,我是一个私有方法”;)

http://jsfiddle.net/ncubica/xMwS9/

你可以这样做:

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


this.foo = function() {
// Whatever
}
})()

另一种方法-只是确保类不能再新的。

这样,你就可以使用instanceof操作。同样,你也可以使用原型链来继承类。它是一个常规类,但是你不能它。如果你想获取实例,只需使用getInstance:

function CA()
{
if(CA.instance)
{
throw new Error('can not new this class');
}
else
{
CA.instance = this;
}
}




/**
* @protected
* @static
* @type {CA}
*/
CA.instance = null;


/* @static */
CA.getInstance = function()
{
return CA.instance;
}




CA.prototype =
/** @lends CA# */
{
func: function(){console.log('the func');}
}


// Initialise the instance
new CA();


// Test here
var c = CA.getInstance()
c.func();
console.assert(c instanceof CA)


// This will fail
var b = new CA();

如果不想公开instance成员,只需将其放入闭包中。

function Unicode()
{
var i = 0, unicode = {}, zero_padding = "0000", max = 9999;


// Loop through code points
while (i < max) {
// Convert decimal to hex value, find the character,
// and then pad zeroes to the code point
unicode[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);
i = i + 1;
}


// Replace this function with the resulting lookup table
Unicode = unicode;
}


// Usage
Unicode();


// Lookup
Unicode["%"]; // Returns 0025

下面是我实现单例模式的演练片段。这是我在面试过程中想到的,我觉得我应该在某个地方捕捉到这一点。

/*************************************************
*     SINGLETON PATTERN IMPLEMENTATION          *
*************************************************/


// Since there aren't any classes in JavaScript, every object
// is technically a singleton if you don't inherit from it
// or copy from it.
var single = {};




// Singleton Implementations
//
// Declaring as a global object...you are being judged!


var Logger = function() {
// global_log is/will be defined in the GLOBAL scope here
if(typeof global_log === 'undefined'){
global_log = this;
}
return global_log;
};




// The below 'fix' solves the GLOABL variable problem, but
// the log_instance is publicly available and thus can be
// tampered with.
function Logger() {
if(typeof Logger.log_instance === 'undefined') {
Logger.log_instance = this;
}


return Logger.log_instance;
};




// The correct way to do it to give it a closure!


function logFactory() {
var log_instance; // Private instance
var _initLog = function() { // Private init method
log_instance = 'initialized';
console.log("logger initialized!")
}
return {
getLog : function(){ // The 'privileged' method
if(typeof log_instance === 'undefined') {
_initLog();
}
return log_instance;
}
};
}




/***** TEST CODE ************************************************


// Using the Logger singleton
var logger = logFactory(); // Did I just give LogFactory a closure?


// Create an instance of the logger
var a = logger.getLog();


// Do some work
// Get another instance of the logger
var b = logger.getLog();


// Check if the two logger instances are same
console.log(a === b); // true
*******************************************************************/

同样可以在我的主旨页面上找到。

简短的回答:

由于JavaScript的非阻塞特性,JavaScript中的单例在使用时非常难看。全局变量将在整个应用程序中为您提供一个实例,而不需要所有这些回调,模块模式将内部隐藏在接口后面。看到Christian C. Salvadó的回答

但是,既然你想要一个单胞胎……

var singleton = function(initializer) {


var state = 'initial';
var instance;
var queue = [];


var instanceReady = function(createdInstance) {
state = 'ready';
instance = createdInstance;
while (callback = queue.shift()) {
callback(instance);
}
};


return function(callback) {
if (state === 'initial') {
state = 'waiting';
queue.push(callback);
initializer(instanceReady);
} else if (state === 'waiting') {
queue.push(callback);
} else {
callback(instance);
}
};


};

用法:

var singletonInitializer = function(instanceReady) {
var preparedObject = {property: 'value'};
// Calling instanceReady notifies the singleton that the instance is ready to use
instanceReady(preparedObject);
}
var s = singleton(singletonInitializer);


// Get the instance and use it
s(function(instance) {
instance.doSomething();
});

解释:

单例在整个应用程序中提供了不止一个实例:它们的初始化延迟到第一次使用。当您处理初始化开销很大的对象时,这确实是一个大问题。昂贵通常意味着I/O,在JavaScript中I/O总是意味着回调。

不要相信那些给你instance = singleton.getInstance()这样的界面的答案,他们都没有抓住重点。

如果它们没有在实例就绪时运行回调,那么当初始化器执行I/O时,它们将不起作用。

是的,回调看起来总是比函数调用更丑,函数调用会立即返回一个对象实例。但是同样:当你执行I/O时,回调是必须的。如果你不想做任何I/O,那么在程序启动时实例化就足够便宜了。

例1,简单的初始化器:

var simpleInitializer = function(instanceReady) {
console.log("Initializer started");
instanceReady({property: "initial value"});
}


var simple = singleton(simpleInitializer);


console.log("Tests started. Singleton instance should not be initalized yet.");


simple(function(inst) {
console.log("Access 1");
console.log("Current property value: " + inst.property);
console.log("Let's reassign this property");
inst.property = "new value";
});
simple(function(inst) {
console.log("Access 2");
console.log("Current property value: " + inst.property);
});

例2,初始化I/O:

在这个例子中,setTimeout伪造了一些昂贵的I/O操作。这说明了为什么JavaScript中的单例真的需要回调。

var heavyInitializer = function(instanceReady) {
console.log("Initializer started");
var onTimeout = function() {
console.log("Initializer did his heavy work");
instanceReady({property: "initial value"});
};
setTimeout(onTimeout, 500);
};


var heavy = singleton(heavyInitializer);


console.log("In this example we will be trying");
console.log("to access singleton twice before it finishes initialization.");


heavy(function(inst) {
console.log("Access 1");
console.log("Current property value: " + inst.property);
console.log("Let's reassign this property");
inst.property = "new value";
});


heavy(function(inst) {
console.log("Access 2. You can see callbacks order is preserved.");
console.log("Current property value: " + inst.property);
});


console.log("We made it to the end of the file. Instance is not ready yet.");

我喜欢将单例模式和模块模式结合起来使用,并将初始化时分支与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 ) );
我从*JavaScript模式中得到了这个例子 使用编码和设计模式构建更好的应用程序一书(作者Stoyan Stefanov)。如果你需要一些简单的实现类,比如单例对象,你可以使用一个直接函数,如下所示
var ClassName;


(function() {
var instance;
ClassName = function ClassName() {
// If the private instance variable is already initialized, return a reference
if(instance) {
return instance;
}
// If the instance is not created, save a pointer of the original reference
// to the private instance variable.
instance = this;


// All constructor initialization will be here
// i.e.:
this.someProperty = 0;
this.someMethod = function() {
// Some action here
};
};
}());

你可以通过下面的测试用例来检查这个例子:

// Extending defined class like singleton object using the new prototype property
ClassName.prototype.nothing = true;
var obj_1 = new ClassName();


// Extending the defined class like a singleton object using the new prototype property
ClassName.prototype.everything = true;
var obj_2 = new ClassName();


// Testing makes these two objects point to the same instance
console.log(obj_1 === obj_2); // Result is true, and it points to the same instance object


// All prototype properties work
// no matter when they were defined
console.log(obj_1.nothing && obj_1.everything
&& obj_2.nothing && obj_2.everything); // Result true


// Values of properties which are defined inside of the constructor
console.log(obj_1.someProperty); // Outputs 0
console.log(obj_2.someProperty); // Outputs 0


// Changing property value
obj_1.someProperty = 1;


console.log(obj_1.someProperty); // Output 1
console.log(obj_2.someProperty); // Output 1


console.log(obj_1.constructor === ClassName); // Output true

这种方法通过了所有测试用例,而当使用原型扩展时,私有静态实现将失败(它可以被修复,但并不简单),而公共静态实现则不太可取,因为实例是公开给公众的。

jsFiddly demo.

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

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

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

我认为这是完整的“类”中最简洁的一种方式。甚至无法访问。

这也是一个单例:

function Singleton() {
var i = 0;
var self = this;


this.doStuff = function () {
i = i + 1;
console.log('do stuff', i);
};


Singleton = function () { return self };
return this;
}


s = Singleton();
s.doStuff();

我的意见:我有一个构造函数(CF),例如,

var A = function(arg1){
this.arg1 = arg1
};

我只需要这个CF创建的每个对象都是相同的。

var X = function(){
var instance = {};
return function(){ return instance; }
}();

测试

var x1 = new X();
var x2 = new X();
console.log(x1 === x2)

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

foo.js的内容:

const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);

circle.js的内容:

const PI = Math.PI;


exports.area = (r) => PI * r * r;


exports.circumference = (r) => 2 * PI * r;

注意你不能访问circle.PI,因为它没有被导出。

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

你可以用下面这个例子中的TypeScript装饰器来做:

class YourClass {


@Singleton static singleton() {}


}


function Singleton(target, name, descriptor) {
var instance;
descriptor.value = () => {
if(!instance) instance = new target;
return instance;
};
}

然后你像这样使用你的单例:

var myInstance = YourClass.singleton();

在撰写本文时,JavaScript引擎中还没有装饰器。你需要确保你的JavaScript运行时实际上启用了装饰器,或者使用像Babel和TypeScript这样的编译器。

还要注意,单例实例是“lazy”创建的,也就是说,它只在你第一次使用它时才创建。

主要的关键是理解背后的关闭的的重要性。因此,在闭包的帮助下,即使在内部函数内部的属性也将是私有的。

var Singleton = function () {
var instance;


function init() {


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


var privateVariable = "Private Property";


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


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


return {
getInstance: function () {


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

JavaScript中的单例是使用模块的模式和闭包实现的。

下面的代码是不言自明的

// Singleton example.
var singleton = (function() {
var instance;


function init() {
var privateVar1 = "this is a private variable";
var privateVar2 = "another var";


function pubMethod() {
// Accessing private variables from inside.
console.log(this.privateVar1);
console.log(this.privateVar2);
console.log("inside of a public method");
};
}


function getInstance() {
if (!instance) {
instance = init();
}
return instance;
};


return {
getInstance: getInstance
}
})();


var obj1 = singleton.getInstance();
var obj2 = singleton.getInstance();


console.log(obj1 === obj2); // Check for type and value.

单例模式:

确保一个类只有一个实例,并提供对它的全局访问点。

单例模式将特定对象的实例数量限制为一个。这个单一实例称为单例。

  • 定义返回唯一实例的getInstance()。
  • 负责创建和管理实例对象。

单例对象被实现为一个即时匿名函数。函数立即执行,将它括在括号中,然后再加上两个括号。它被称为匿名,因为它没有名字。

示例程序

var Singleton = (function () {
var instance;


function createInstance() {
var object = new Object("I am the instance");
return object;
}


return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();


function run() {


var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();


alert("Same instance? " + (instance1 === instance2));
}


run()

我相信这是最简单/最干净和最直观的方式,尽管它需要ECMAScript 2016 (ES7):

export default class Singleton {


static instance;


constructor(){
if(instance){
return instance;
}


this.state = "duke";
this.instance = this;
}


}

源代码来自:adam-bien.com

ECMAScript 2015 (ES6)中:

class Singleton {
constructor () {
if (!Singleton.instance) {
Singleton.instance = this
}
// Initialize object
return Singleton.instance
}
// Properties & Methods
}


const instance = new Singleton()
Object.freeze(instance)


export default instance

下面是一个简单的例子来解释JavaScript中的单例模式。

var Singleton = (function() {
var instance;
var init = function() {
return {
display:function() {
alert("This is a singleton pattern demo");
}
};
};
return {
getInstance:function(){
if(!instance){
alert("Singleton check");
instance = init();
}
return instance;
}
};
})();


// In this call first display alert("Singleton check")
// and then alert("This is a singleton pattern demo");
// It means one object is created


var inst = Singleton.getInstance();
inst.display();


// In this call only display alert("This is a singleton pattern demo")
// it means second time new object is not created,
// it uses the already created object


var inst1 = Singleton.getInstance();
inst1.display();

最清楚的答案应该是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;
}


};


})();

在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' }

我发现以下是最简单的单例模式,因为使用操作符可以使立即在函数中可用,从而消除了返回对象字面量的需要:

var singleton = new (function () {


var private = "A private value";


this.printSomething = function() {
console.log(private);
}
})();


singleton.printSomething();

你可以在每次new执行中返回相同的实例

function Singleton() {
// lazy
if (Singleton.prototype.myInstance == undefined) {
Singleton.prototype.myInstance = { description: "I am the instance"};
}
return Singleton.prototype.myInstance;
}


a = new Singleton();
b = new Singleton();
console.log(a); // { description: "I am the instance"};
console.log(b); // { description: "I am the instance"};
console.log(a==b); // true
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

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

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

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

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

ES6中正确的方法是:

class MyClass {
constructor() {
if (MyClass._instance) {
throw new Error("Singleton classes can't be instantiated more than once.")
}
MyClass._instance = this;


// ... Your rest of the constructor code goes after this
}
}


var instanceOne = new MyClass() // Executes succesfully
var instanceTwo = new MyClass() // Throws error

或者,如果你不想在创建第二个实例时抛出错误,你可以只返回最后一个实例,如下所示:

class MyClass {
constructor() {
if (MyClass._instance) {
return MyClass._instance
}
MyClass._instance = this;


// ... Your rest of the constructor code goes after this
}
}


var instanceOne = new MyClass()
var instanceTwo = new MyClass()


console.log(instanceOne === instanceTwo) // Logs "true"

如果你想使用类:

class Singleton {
constructor(name, age) {
this.name = name;
this.age = age;
if(this.constructor.instance)
return this.constructor.instance;
this.constructor.instance = this;
}
}
let x = new Singleton('s', 1);
let y = new Singleton('k', 2);

以上的输出将是:

console.log(x.name, x.age, y.name, y.age) // s 1 s 1

另一种使用函数编写Singleton的方法

function AnotherSingleton (name,age) {
this.name = name;
this.age = age;
if(this.constructor.instance)
return this.constructor.instance;
this.constructor.instance = this;
}


let a = new AnotherSingleton('s', 1);
let b = new AnotherSingleton('k', 2);

以上的输出将是:

console.log(a.name, a.age, b.name, b.age) // s 1 s 1

简单的例子

class Settings {


constructor() {
if (Settings.instance instanceof Settings) {
return Settings.instance;
}
this.settings = {
id: Math.floor(Math.random() * 4000),
name: "background",
};
Object.freeze(this.settings);
Object.freeze(this);
Settings.instance = this;
}


}


var o1 = new Settings();
var o2 = new Settings();


console.dir(o1);
console.dir(o2);


if (o1 === o2) {
console.log("Matched");
}
var singleton = (function () {


var singleton = function(){
// Do stuff
}
var instance = new singleton();
return function(){
return instance;
}
})();

一个没有getInstance方法的解决方案。

let MySingleton = (function () {
var _instance
function init() {
if(!_instance) {
_instance = { $knew: 1 }
}
return _instance
}
let publicAPIs = {
getInstance: function() {
return init()
}
}
// this prevents customize the MySingleton, like MySingleton.x = 1
Object.freeze(publicAPIs)
// this prevents customize the MySingleton.getInstance(), like MySingleton.getInstance().x = 1
Object.freeze(publicAPIs.getInstance())
return publicAPIs
})();

如果你正在使用node.JS,那么你可以利用node.JS缓存机制,你的Singleton将像下面这样简单:

class Singleton {
constructor() {
this.message = 'I am an instance';
}
}
module.exports = new Singleton();

请注意,我们导出的 Singleton实例 Singleton()

Node.JS将在每次需要时缓存和重用相同的对象。

更多详情请查看:Node.JS和单例模式

class Singelton {
static #instance;


#testValue;


constructor() {
if (Singelton.#instance instanceof Singelton) {
return Singelton.#instance;
}


Singelton.#instance = this;
return Singelton.#instance;
}


get testValue() {
return this.#testValue;
}


set testValue(value) {
this.#testValue = value;
}
}

测试:

let x = new Singelton();
x.testValue = 123;


let y = new Singelton();


console.log({isSingelton: x === y, testValueFromY: y.testValue});

所以公平地说,最简单的答案通常是最好的。一个对象字面值总是一个实例。没有什么理由去做更复杂的事情,除了按需分配内存。

话虽如此,这里是一个使用ES6的经典单例实现。

  • 实例“字段”;是“private"。这实际上意味着我们将实例隐藏为构造函数的属性。某个不是构造函数的地方。原型,它将通过原型继承对实例可用。
  • 构造函数是“private”。当调用者不是静态getInstance方法时,我们实际上只是抛出一个错误。

同样值得注意。理解这个关键词在不同语境下的含义是很重要的。

在构造函数中,this指向创建的实例。

在静态getInstance方法中,this指向dot的左边,宇宙构造函数,它是一个对象,像JS中的大多数东西一样,可以保存属性。

class Universe {
constructor() {
if (!((new Error).stack.indexOf("getInstance") > -1)) {
throw new Error("Constructor is private. Use static method getInstance.");
}
this.constructor.instance = this;
this.size = 1;
}
static getInstance() {
if (this.instance) {
return this.instance;
}
return new this;
}
expand() {
this.size *= 2;
return this.size;
}
}




console.log(Universe.getInstance())
console.log(Universe.getInstance().expand())
console.log(Universe.getInstance())
console.log(new Universe())

使用ES6类和私有静态字段。调用Singleton类的新实例将返回相同的实例。实例变量也是私有的,不能在类外部访问。

class Singleton {
// # is a new Javascript feature that denotes private
static #instance;


constructor() {
if (!Singleton.#instance) {
Singleton.#instance = this
}
return Singleton.#instance
}


get() {
return Singleton.#instance;
}
}


const a = new Singleton();
const b = new Singleton();
console.log(a.get() === b.get()) // true
console.log(Singleton.instance === undefined) // true

这个知识是基于我正在学习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

简单地使用类表达式:

const singleton = new (class {
hello() { return 'world'; }
})();


console.log(singleton.hello()); //=> world

这就是我如何使用ES6特性实现单例模式。是的,我知道这看起来不像面向对象的方法,但我发现这个方法很容易实现,而且是一种干净的实现方式。

const Singleton = (() => {
var _instance = !_instance && new Object('Object created....');
return () => _instance;
})();


//************************************************************************


var instance1 = Singleton();
var instance2 = Singleton();
console.log(instance1 === instance2); // true