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

评论

拒绝接受的答案根本不是单例。这只是一个全局变量。

这是大量信息,但实际上列出了不同JS设计模式之间的差异。它对我有很大帮助:addyosmani.com/resources/essentialjsdesignpatterns/book

#1 楼

我认为最简单的方法是声明一个简单的对象常量:
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'
// ...


评论


+1在具有全局变量的语言中寻找“ Singleton模式”不是很奇怪吗???

–维克多
09年10月28日在9:18

使用模块模式,公共成员将如何访问另一个公共成员?即,publicMethod1将如何调用publicMethod2?

– typeof
11年4月26日在21:50

@Tom,是的,该模式诞生于基于类的OOP语言-我记得很多实现都涉及静态getInstance方法和私有构造函数-但是IMO,这是构建单例的最“简单”方式Javascript中的对象,最后达到了相同的目的-单个对象,您无法再次初始化(没有构造函数,它只是一个对象)-。关于链接的代码,它存在一些问题,交换a和b变量声明并测试a ===窗口。干杯。

–基督徒C.Salvadó
2011年7月18日在15:40

@Victor –以这种语言查找“单例模式”并不奇怪。许多面向对象的语言都使用全局变量,并且仍在使用单例。单例不仅保证给定类只有一个对象。 Singleton具有更多功能:1)应该在首次使用时进行初始化(这不仅意味着延迟初始化,而且还确保对象确实可以使用)2)它应该是线程安全的。模块模式可以代替单例模式,但只能在浏览器中(并非总是如此)。

–skalee
11-11-3在10:03



这不应是公认的答案。这根本不是单身人士!这只是一个全局变量。两者之间存在天壤之别。

– mlibby
13年2月25日在12:52

#2 楼

我认为最干净的方法是:

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


之后,您可以将函数调用为

var test = SingletonFactory.getInstance();


评论


备注:可以使用delete instance.constructor再次读取原始构造函数。x = SingletonClass.getInstance();删除x.constructor; new x.constructor;

– Rob W
2012年12月12日下午13:42

var test = SingletonClass.getInstance()-看起来不太干净或类似JS。其他以a = new Foo()结尾的灵魂; b =新的Foo(); a === b //是

–马特西亚斯
2013年11月7日13:24



整个“ getInstance”部分对我来说更像工厂。

– Lajos Meszaros
2015年5月5日14:06

这不是单例,因为您可以使用Object.create创建它的多个实例。

– AndroidDev
17年1月13日,0:28

小提琴坏了

– HoffZ
18年2月16日在13:37

#3 楼

我不确定我是否同意将模块模式用作单例模式的替代品。我经常看到在完全不需要的地方使用和滥用单例,并且我敢肯定,模块模式会填补程序员在其他情况下会使用单例的空白。但是,模块模式不是单例。
模块模式:
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


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

评论


我从没说过关于单例是一个坏主意还是好主意。我说过,单例的实现要复杂得多,因为您将Java的局限性与该模式混淆了,就好像根本不了解它一样。这就像通过仅在Javascript中使用匿名函数来构造构造函数和方法来实现策略模式一样。

– Esailija
13年11月17日在11:25



@Esailija,对我来说听起来像您不了解单例模式。单例模式是一种设计模式,用于将类的实例化限制为一个对象。 var singleton = {}不符合该定义。

–zzzzBov
13年11月17日在18:03

由于其局限性,这仅适用于Java之类的语言。 var singleton = {}是如何在Javascript中实现单例。

– Esailija
13年11月17日在18:14

@Esailija,“在基于原型的编程语言中,使用对象而不使用类...” JavaScript具有类的概念,因此不适用。

–zzzzBov
13年11月17日在19:22

顺便说说。 Singleton是一种反模式,无论如何也绝不能在托管沙盒环境(如JavaScript)中使用它。 misko.hevery.com/2008/08/17/singletons-are-pathologic-liars-youtube.com/watch?v=G32acYVd9LY blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx -jalf.dk/blog/2010/03/…-kore-nordmann.de/blog/0103_static_considered_harmful.html-phparch.com/2010/03/static-methods-vs-singletons-choose-noth

–本杰明·格伦鲍姆(Benjamin Gruenbaum)
13年11月17日在19:35

#4 楼

在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" 




评论


嗨,您能帮我知道_instance和instance之间的区别,因为我正在使用实例并且代码无法正常工作

– Abhinav bhardwaj
2月13日下午7:54

实例和_instance没有技术上的区别。这只是编程语言中的命名约定,我们用下划线命名私有变量。我怀疑您的代码无法正常工作的原因是您使用的是this.instance而不是MyClass.instance

– UtkarshPramodGupta
2月13日8:17

这可以通过使用将保存实例的静态私有变量来改进,例如静态#instance = null;。否则,只需修改_instance属性(将其设置为null)即可创建第二个实例。

–akinuri
12月4日8:35

#5 楼

在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


评论


如果Singleton是其他类的包装并且仅包含实例字段,则冻结是有意义的。到目前为止,此类(实例已设置为该类)可能还会包含其他字段,并且冻结对imo没有意义。

–thisismydesign
19年5月14日在15:07

#6 楼

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


#7 楼

剥皮猫的方法不止一种:)根据您的口味或特定需求,您可以应用任何建议的解决方案。我个人会尽可能寻求Christian C.Salvadó的第一个解决方案(当您不需要隐私时)。
由于问题是关于最简单,最简洁的问题,所以这就是赢家。甚至:
var myInstance = {}; // Done!

(引用我的博客)...
var SingletonClass = new function() {
    this.myFunction() {
        // Do stuff
    }
    this.instance = 1;
}

没什么意义(我的博客示例也没有),因为它没有t不需要任何私有变量,因此与以下内容几乎相同:
var SingletonClass = {
    myFunction: function () {
        // Do stuff
    },
    instance: 1
}


评论


代码段2包含语法错误。您不能写this.f(){}

–xoxox
16年11月6日在21:19



#8 楼

我不赞成使用我的答案,请参阅其他答案。
通常,单例模式不是很好的模块模式(请参见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();
    };
};


评论


如果不需要模块,为什么还要加载该模块?而当您需要加载模块时,则加载模块,并对其进行初始化。

– Esailija
2013年11月17日19:27



#9 楼

简短答案:
由于JavaScript具有非阻塞性,因此JavaScript中的单例确实很难使用。全局变量也将在整个应用程序中为您提供一个实例,而无需所有这些回调,并且模块模式将内部构件轻轻地隐藏在接口后面。请参阅克里斯蒂安·C·萨尔瓦多(Christian C.Salvadó)的回答。整个应用程序中只有一个实例:它们的初始化被延迟到第一次使用。当您处理初始化成本很高的对象时,这确实是一件大事。昂贵的方法通常意味着I / O,而在JavaScript中的I / O始终意味着回调。
不要相信像instance = singleton.getInstance()这样的界面,它们都会错过重点。
如果他们不进行回调,要在实例准备就绪时运行,那么在初始化程序进行I / O时它们将无法工作。
是的,回调总是比立即返回对象实例的函数调用难看。再说一遍:执行I / O时,必须执行回调。如果您不想执行任何I / O,则实例化足以在程序启动时进行。
示例1,廉价的初始化方法:
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);
    }
  };

};

示例2,使用I进行初始化/ O:
在此示例中,setTimeout伪造了一些昂贵的I / O操作。这说明了为什么JavaScript中的单例确实需要回调。
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();
});


评论


通过对其他单例答案的试探和磨难,我发现了与此类似的结果代码。

– mheyman
2014年8月22日在18:21

出于某种原因,这是唯一对我有意义的答案。其他答案都让我想起了愚蠢的表演集,其中三名男子试图通过递归彼此的肩膀爬上四个人高的墙。

–蒂姆·奥美(Tim Ogilvy)
2014年11月25日13:42

回调堆栈是我真正需要的东西!谢谢!!

–蒂姆·奥美(Tim Ogilvy)
2014年11月25日14:08

这种方法实际上不会给您一个单例,例如:singleton(singletonInitializer)!== singleton(singletonInitializer)它们是两个不同的实例。您返回的结果函数可用于将更多回调附加到该实例,但未严格指定只能创建此类型的一个实例。这就是单例的重点。

–欧文
2015年5月12日14:47



#10 楼

我从* JavaScript Patterns
通过编码和设计模式构建更好的应用程序一书(由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

此方法通过了所有测试用例,而使用原型扩展时私有的静态实现将失败(可以固定,但不会很简单),并且由于实例暴露给公众的静态实现不建议public。
js完美演示。

#11 楼

Christian C.Salvadó和zzzzBov的答案都给出了很好的答案,但是只是基于我从单例模式很常见的PHP / Zend Framework转移到沉重的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 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提供的代码很好地展示了萨尔瓦多这里唯一要注意的是,当构造函数本身是匿名的时,不可能修改构造函数的原型链。
请记住,对于JavaScript,“ public”和“ private”的概念不适用于他们使用PHP或Java。但是我们通过利用JavaScript的功能范围可用性规则达到了相同的效果。

评论


可以从您的代码创建多个实例:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');

–zzzzBov
13年11月21日在14:42

@zzzzBov:在我的小提琴中尝试时遇到了错误:jsfiddle.net/rxMu8

–cincodenada
2014年5月23日18:47



#12 楼

我想我已经找到了使用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();

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

评论


但是使用这种技术,您可以拥有多个实例。那是不对的。

–nicolascolman
17年6月23日在16:18

我不这么认为,您甚至不通过getInstance就无法访问该类。你能详细说明吗?

–大卫
17年6月24日在17:11

大卫·梅斯(David Maes)抱歉,在第二个示例中,我没有注意到该验证。我道歉。

–nicolascolman
17年6月26日在10:07

#13 楼

您可以这样做:
var singleton = new (function() {
  var bar = 123

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


评论


这似乎是跳过getInstance方法并获得更简单解决方案的一种好方法。但是请记住,单例将在文件解析后立即执行,这意味着DOM侦听器必须包装在$(document).ready函数中。

– HoffZ
18年2月16日在14:21

#14 楼

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

  };

})(); 




#15 楼

这应该起作用:
function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}


评论


测试=克拉斯; t1 = new Test(); t2 = new Test(); -没有机会重命名类或选择其他名称空间。

–zzzzBov
2012年12月3日在21:37

@zzzzBov我不太明白您要说的话。您是说不允许其他功能引用“ Singleton”功能吗?该解决方案有什么问题?

– Sudhansu Choudhary
8月6日19:15

@SudhansuChoudhary,坦率地说,八年前写这本书时,我无法准确说出自己的想法。如果我不得不猜测的话,这可能是因为该解决方案在全球名称空间中使用时的脆弱性,这在2012年是很普遍的。采用现代脚本实践,这种抱怨似乎不再相关,但是我要补充一点,这并不是2020年在JS中使用singleton的好主意(2012年也不是好主意)。

–zzzzBov
8月6日20:36

@zzzzBov我也不是一个Singleton粉丝,现在我知道当您发表评论时您来自何处,全局命名空间中解决方案的脆弱性应该引起人们的极大关注。感谢您的澄清。

– Sudhansu Choudhary
8月11日9:31



#16 楼

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

export default class Singleton {

  static instance;

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

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

}


源代码来自:adam-bien.com

评论


这是完全错误的,并且在调用新的Singleton()时会引发错误。

– UtkarshPramodGupta
1月20日13:02



#17 楼

我的两分钱:我有一个构造函数(CF),例如,
var A = function(arg1){
  this.arg1 = arg1
};

我只需要此CF创建的每个对象都相同。
var X = function(){
  var instance = {};
  return function(){ return instance; }
}();

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


#18 楼

这是一个简单的示例,用于解释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();


#19 楼

我发现以下是最简单的单例模式,因为使用new运算符使它可以立即在函数中使用,而无需返回对象文字:



 var singleton = new (function () {

  var private = "A private value";

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

singleton.printSomething(); 




#20 楼

对我来说,最简单/最简洁的方法也意味着简单地理解,并且不会像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


评论


我查看了广为接受的答案,但我认为这个答案没有得到足够的重视。 +1 btw

– Davood Falahati
10月17日23:33

我是来自Java的JavaScript的新手,必须问,这样做如何停止执行新的Singleton(),因此Singleton模式被破坏了

– Erik
10月26日8:01

@Erik-不是按照惯例,您调用getInstance

– Wolfgang Fahl
10月26日8:45

#21 楼

对我而言,最干净的方法是:
const singleton = new class {
    name = "foo"
    constructor() {
        console.log(`Singleton ${this.name} constructed`)
    }
}

使用此语法,您可以确定自己的单身身份并将保持唯一。您还可以享受类语法的甜味,并按预期使用this
(请注意,类字段需要节点v12 +或现代浏览器。)

#22 楼

如果要使用类:
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

使用函数编写单例的另一种方法
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


#23 楼

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


#24 楼

我需要以下几个单例:

惰性初始化
初始参数

所以这就是我想出的: br />
args必须是Array才能起作用,因此,如果您有空变量,只需传入[]


我在函数中使用了window对象可以传入一个参数来创建我自己的作用域


名称,并且构造参数只是String才能使window []正常工作,但是需要进行一些简单的类型检查,window.name和window .construct也可以。



#25 楼

模块模式:“更具可读性”。您可以轻松查看哪些方法是公共方法,哪些是私有方法
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();之类的公共方法。 //->我通过公共方法alert(“嗨,我是私有方法”)调用私有方法
http://jsfiddle.net/ncubica/xMwS9/

#26 楼

另一种方法-确保该类不再是新的。
这样,您可以使用instanceof op。另外,您可以使用原型链继承类。这是一门普通的课,但是你不能补习。如果要获取实例,请使用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成员,只需将其放入闭包中。

#27 楼

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


#28 楼

以下是我实现单例模式的摘录。这是我在面试过程中发生的,我觉得应该把它捕捉到某个地方。

#29 楼

这也是一个单例:
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();


#30 楼

您可以像下面的示例中那样使用装饰器来完成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之类的编译器。
还请注意,单例实例是“惰性”创建的,即仅在第一个实例中使用时才创建时间。

评论


TypeScript不是编译器。它是一种编程语言。您可以澄清(通过编辑答案)吗?

– Peter Mortensen
12月19日23:09