在 JavaScript 中实现单例模式的最简单/最干净的方法是什么?
在 JavaScript 中实现单例的最简单/最干净的方法
IT技术
javascript
function
design-patterns
singleton
2021-02-07 20:57:20
6个回答
我认为最简单的方法是声明一个简单的对象字面量:
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 () {
}
};
})();
这就是所谓的module模式,它基本上可以让你来封装对象私有成员,通过采取利用的优势关闭。
如果要防止修改单例对象,可以使用 ES5方法冻结它Object.freeze
。
这将使对象不可变,防止对其结构和值进行任何修改。
如果你使用的是 ES6,你可以很容易地使用ES module来表示单例,你甚至可以通过在module范围内声明变量来保持私有状态:
// 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;
}
};
})();
之后,您可以调用该函数作为
var test = SingletonFactory.getInstance();
我不确定我是否同意将module模式用作单例模式的替代品。我经常看到在完全不需要单例的地方使用和滥用单例,而且我确信module模式填补了程序员否则会使用单例的许多空白。然而,module模式不是单例。
module模式:
var foo = (function () {
"use strict";
function aPrivateFunction() {}
return { aPublicFunction: function () {...}, ... };
}());
module模式中初始化的所有内容都在Foo
声明时发生。此外,module模式可用于初始化构造函数,然后可以多次实例化该构造函数。虽然module模式是许多工作的正确工具,但它并不等同于单例。
单例模式:
简写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();
}
长格式,使用module模式
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
在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"
在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