在JavaScript中实现单例的最简单/最简洁的方法?


300

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


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

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

Answers:


315

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

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

46
+1在具有全局变量的语言中寻找“ Singleton模式”有点奇怪吗???
维克多

4
使用模块模式,公共成员将如何访问另一个公共成员?即,怎么publicMethod1称呼publicMethod2
typeof

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

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

55
这不应是公认的答案。这根本不是单例!这只是一个全局变量。两者之间有天壤之别。
mlibby

172

我认为最干净的方法是:

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

4
备注:可以使用delete instance.constructor以下方法再次读取原始构造函数:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W

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

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

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

小提琴被打破了
HoffZ '18

103

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

模块模式:

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

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

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

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

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

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

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


18

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

1
如果Singleton是其他类的包装并且只有该instance字段,那么冻结将是有意义的。由于目前(instance设置为this),此类可能还包含其他字段,因此冻结没有意义。
thisismydesign '19

10

以下在节点v6中起作用

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

8

在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

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

7

剥皮猫的方法不止一种:)根据您的口味或特定需求,您可以应用任何建议的解决方案。我个人尽可能地寻求CMS的第一个解决方案(当您不需要隐私时)。由于问题是关于最简单,最干净的,因此是赢家。甚至:

var myInstance = {}; // done!

这(从我的博客引用)...

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

没什么意义(我的博客示例也没有),因为它不需要任何私有变量,因此与以下内容几乎相同:

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

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

7

我不赞成我的答案,请参阅另一个

通常不是单例模式的模块模式(请参阅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

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

6

简短答案:

由于JavaScript具有非阻塞性,因此JavaScript中的Singleton确实很难使用。全局变量也将在整个应用程序中为您提供一个实例,而无需所有这些回调,模块模式将内部构件轻轻地隐藏在接口后面。请参阅@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.");

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

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

回调堆栈是我真正需要的东西!谢谢!!
Tim Ogilvy 2014年

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

6

我是从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
        // i.e.: 
        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演示。


5

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

除了使用new关键字,您还可以创建一个这样的类:

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

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


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

1
我不这么认为,您甚至不能不通过getInstance来访问该类。你能详细说明吗?
大卫(David

David Maes抱歉,但是我没有注意到第二个示例中的验证。我道歉。
nicolascolman

4

@CMS和@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 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'

请注意,从技术上讲,自执行匿名函数本身就是Singleton,如@CMS提供的代码中很好地演示的那样。这里唯一要注意的是,当构造函数本身是匿名的时,不可能修改构造函数的原型链。

请记住,对于Javascript,“公共”和“私有”的概念并不像在PHP或Java中那样适用。但是,我们通过利用Javascript的功能范围可用性规则达到了相同的效果。


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

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

4

不知道为什么没人提出来,但是您可以这样做:

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

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

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

4

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


3

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

export default class Singleton {

  static instance;

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

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

}

源代码来自:adam-bien.com


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

2

这怎么了

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

Test = Klass; t1 = new Test(); t2 = new Test();-没有机会重命名类或选择其他名称空间。
zzzzBov 2012年

2

我可以放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)

2

我发现下面是最容易Singleton模式,因为采用新的运营商,使立即在函数中使用,不再需要返回一个对象字面:

var singleton = new (function () {

  var private = "A private value";
  
  this.printSomething = function() {
      console.log(private);
  }
})();

singleton.printSomething();


2

这是解释javascript中单例模式的简单示例。

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

1

我需要几个单身人士:

  • 延迟初始化
  • 初始参数

所以这就是我想出的:

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才能起作用,因此,如果您有空变量,只需传入[]

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

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


1

这样,只是确保该类不再是新的。

这样,您可以使用instanceofop,也可以使用原型链继承该类,它是一个常规类,但是不能新建它,如果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成员,只需将其放入闭包即可。


1

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

/*************************************************
   *     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
  *******************************************************************/

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


1
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

1

这不是一个单身人士吗?

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

1

您可以像下面的示例中那样使用装饰器来完成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等编译器。

还要注意,单例实例是“惰性”创建的,即仅在您首次使用它时才创建。


1

模块模式:“更具可读性”。您可以轻松查看哪些方法是公共方法,哪些是私有方法

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(); //->我正在通过公共方法alert(“ hi im a private method”)调用私有方法

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


1

单身人士:

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

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

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

Singleton对象被实现为直接匿名函数。该函数立即执行,方法是将其包装在方括号中,然后再加上两个括号。之所以称为匿名,是因为它没有名称。

示例程序

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


1

对我来说,最简单/最干净的意思也就是简单地理解并且没有任何麻烦,就像在Java版本的讨论中所讨论的那样:

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

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

https://stackoverflow.com/a/70824/1497139

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

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

但是,鉴于最新的ECMA语法,可能更接近:

Singleton模式作为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
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

1

如果要使用类:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.