用JavaScript实现单例模式的最简单/最干净的方法是什么?
当前回答
对我来说,最简单/最干净的也意味着简单地理解,没有花哨的东西,就像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
其他回答
我从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.
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
我认为最简单的方法是声明一个简单的对象文字:
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'
// ...
如果你正在使用node.JS,那么你可以利用node.JS的缓存机制,你的单例将像这样简单:
class Singleton {
constructor() {
this.message = 'I am an instance';
}
}
module.exports = new Singleton();
请注意,我们导出的不是类Singleton,而是实例Singleton()。
Node.JS将在每次需要时缓存和重用相同的对象。
更多细节请查看:Node.JS和单例模式
最明确的答案应该是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; } }; })();
推荐文章
- 我如何使用Jest模拟JavaScript的“窗口”对象?
- 我如何等待一个承诺完成之前返回一个函数的变量?
- 在JavaScript中根据键值查找和删除数组中的对象
- 使嵌套JavaScript对象平放/不平放的最快方法
- 如何以及为什么'a'['toUpperCase']()在JavaScript工作?
- 有Grunt生成index.html不同的设置
- 文档之间的区别。addEventListener和window。addEventListener?
- 如何检查动态附加的事件监听器是否存在?
- 如何从Python函数中返回两个值?
- 如何写setTimeout与参数Coffeescript
- 将JavaScript字符串中的多个空格替换为单个空格
- JavaScript: override alert()
- 重置setTimeout
- 如何确保<select>表单字段被禁用时提交?
- jQuery有不聚焦的方法吗?