最简单/最干净的方式来实现JavaScript中的单身人士?

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

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

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

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

 var myInstance = (function() { var privateVar = ''; function privateMethod () { // ... } return { // public interface publicMethod1: function () { // all private members are accesible here }, publicMethod2: function () { } }; })(); 

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

我认为最干净的方法是这样的:

 var SingletonClass = (function(){ function SingletonClass() { //do stuff } var instance; return { getInstance: function(){ if (instance == null) { instance = new SingletonClass(); // Hide the constructor so the returned objected can't be new'd... instance.constructor = null; } return instance; } }; })(); 

之后,您可以调用该函数

 var test = SingletonClass.getInstance(); 

我不确定我是否同意将模块模式用作单例模式的替代。 我经常看到单身使用和滥用的地方,他们是完全不必要的,我敢肯定,模块模式填补了许多空白,程序员会使用单身,但模块模式不是一个单身。

模块模式:

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

在我提供的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 

我还应该提到singleton模式很适合隐含的构造函数模式:

 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 

有一种以上的方法来剥皮猫:)根据您的口味或特定的需要,您可以应用任何建议的解决方案。 我亲自去找CMS的第一个解决方案(当你不需要隐私时)。 既然问题是最简单最干净的,那就是赢家。 甚至:

 var myInstance = {}; // done! 

这(引用我的博客)…

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

没有什么意义(我的博客的例子不是),因为它不需要任何私人变量,所以它几乎是相同的:

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

我贬低我的答案,看到我的另一个 。

通常模块模式(见CMS的答案)是不是单身模式是够好的。 然而单身人士的特点之一是它的初始化被延迟,直到需要对象。 模块模式缺少这个功能。

我的建议(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 

我从JavaScript模式中得到了这个例子使用编码和设计模式构建更好的应用程序作者:Stoyan Stefanov的书,如果你需要一些简单的实现类,如singltone对象,你可以使用立即函数如下:

 var ClassName; (function() { var instance; ClassName = function ClassName() { //If private instance variable already initialized return reference if(instance) { return instance; } //If instance does not created save pointer of original reference //to private instance variable. instance = this; //All constructor initialization will be here // ie: this.someProperty = 0; this.someMethod = function() { //Some action here }; }; }()); 

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

 //Extending defined class like Singltone object using new prototype property ClassName.prototype.nothing = true; var obj_1 = new ClassName(); //Extending defined class like Singltone object using new prototype property ClassName.prototype.everything = true; var obj_2 = new ClassName(); //Testing does this two object pointing to same instance console.log(obj_1 === obj_2); //Result is true, it points to same instance object //All prototype properites 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 is defined inside of constructor console.log(obj_1.someProperty);// output 0 console.log(obj_2.someProperty);// output 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演示。

我想我已经找到了用JavaScript编程的最简洁的方法,但是你需要一些想象力。 我从“javascript好的部分”一书中的工作技巧中得到了这个想法。

而不是使用新的关键字,你可以创建一个这样的类:

 function Class() { var obj = {}; // Could also be used for inheritence 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(); 

我认为这是最完整的“阶级”甚至不可访问的最好的方式。

不知道为什么没有人提起这件事,但你可以这样做:

 var singleton = new (function() { var bar = 123 this.foo = function() { // whatever } })() 

简短的回答:

由于JavaScript的非阻塞性质,JavaScript中的Singletons在使用上真的很难看。 全局变量也会通过整个应用程序给你一个实例,没有所有这些回调,模块模式轻轻地隐藏了界面后面的内部。 看@CMS的答案。

但是,既然你想要一个单身人士

 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 singleton that instance is ready to use instanceReady(preparedObject); } var s = singleton(singletonInitializer); // get 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."); 

@CMS和@zzzzBov都给出了很好的答案,只是为了增加我自己的解释,基于我已经从PHP / Zend框架开始重新开发node.js,其中单例模式是常见的。

以下备注文件的代码基于以下要求:

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

我的代码与@ 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 availble 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' 

请注意,从技术上讲,自执行的匿名函数本身就是一个单例,正如@CMS提供的代码所展示的那样。 唯一的问题在于,当构造函数本身是匿名的时候,不可能修改构造函数的原型链。

请记住,对于Javascript来说,“public”和“private”这些概念并不适用于PHP或Java。 但是,通过利用Javascript的功能范围可用性规则,我们已经取得了同样的效果。

我可以把我的5个硬币。 我有一个构造函数,例如。

 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) 

我需要几个单身人士:

  • 懒惰的初始化
  • 初始参数

所以这就是我想到的:

 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; } 
  • 参数必须是数组才能工作,所以如果你有空变量,只需传入[]

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

  • 名称和构造参数只是为了使window []工作,但是通过一些简单的类型检查,window.name和window.construct也是可能的。

这有什么问题?

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

这样怎么样,只要保证班级不会再新的。

通过这个,你可以使用instanceof操作,也可以使用原型链来继承类,它是一个普通的类,但不能新的它,如果yuu想要得到的实例只是使用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');} } // initilize the instance new CA(); // test here var c = CA.getInstance() c.func(); console.assert(c instanceof CA) // this will failed var b = new CA(); 

如果你不想公开这个instance成员,就把它放到一个闭包中。

以下是我的步行通过实施单身模式的片段。 这是在面试过程中发生的,我觉得我应该在某个地方抓到这个。

 /************************************************* * SINGLETON PATTERN IMPLEMENTATION * *************************************************/ //since there are no 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 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 gave 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 *******************************************************************/ 

我的要点页面上可以找到相同的内容

 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, then pad zeroes to the codepoint 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 

这不是一个单身吗?

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

您可以使用类似下面这个示例中的装饰器来处理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这样的编译器。

另外请注意,单例实例被创建为“懒惰”,即只在第一次使用时创建。

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

 var module = (function(_name){ /*Local Methods & Values*/ var _local = { name : _name, flags : { init : false } } function init(){ _local.flags.init = true; } function imaprivatemethod(){ alert("hi im a private method"); } /*Public Methods & variables*/ var $r = {}; //this object will hold all public methods. $r.methdo1 = function(){ console.log("method1 call 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 init method return $r; //returning all publics methods })("module"); 

现在你可以使用公开的方法

module.method2(); / />我打电话通过公共方法警报私人方法(“你好我私人方法”)

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

最明智的答案应该是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 = "Im 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; } }; })(); 

我喜欢使用Singleton和模块模式的组合,init-time使用全局NS检查分支,并封装在闭包中。 在单例初始化后环境不会改变的情况下, 使用立即调用的对象字面量来返回一个模块,这个模块将会持续一段持续时间应该没问题。 我没有传递任何依赖关系,只是在自己的小世界中调用单例 – 唯一的目标是:创建事件绑定/解除绑定的实用程序模块(在这种情况下,设备方向/方向更改也可以)。

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

你没有说“在浏览器中”。 否则,您可以使用NodeJS模块 。 每个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 ,因为它未被导出。

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

主要关键在于这个背后的重要性。即使在内部函数内部的属性也会在关闭的帮助下被隐藏。

var Singleton = function(){var instance;

  function init() { function privateMethod() { console.log("private via closure"); } var privateVariable = "Private Property"; var privateRandomNumber = Math.random();// this also private return { getRandomNumber: function () { // access via getter in init call return privateRandomNumber; } }; }; return { getInstance: function () { if (!instance) { instance = init(); } return instance; } }; 

};

javascript中的单例是使用Module模式和闭包实现的。 下面是几乎不言自明的代码 –

 // 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(); cnosole.log(obj1===obj2); //check for type and value. 

辛格尔顿:

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

The Singleton Pattern limits the number of instances of a particular object to just one. This single instance is called the singleton.

  • defines getInstance() which returns the unique instance.
  • responsible for creating and managing the instance object.

The Singleton object is implemented as an immediate anonymous function. The function executes immediately by wrapping it in brackets followed by two additional brackets. It is called anonymous because it doesn't have a name.

Sample Program,

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

I believe this is the simplest/cleanest and most intuitive way though it requires ES7:

 export default class Singleton { static instance; constructor(){ if(instance){ return instance; } this.state = "duke"; this.instance = this; } } 

The source code is from: adam-bien.com

In 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 

Here is the simple example to explain singleton pattern in java script.

  var Singleton=(function(){ var instance; var init=function(){ return { display:function(){ alert("This is a Singleton patern 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 patern demo"); // It means one object is created var inst=Singleton.getInstance(); inst.display(); // In this call only display alert("This is a Singleton patern demo") // it means second time new object is not created, // it uses the already created object var inst1=Singleton.getInstance(); inst1.display();