用JavaScript实现单例模式的最简单/最干净的方法是什么?
当前回答
关键是要理解闭包背后的重要性。因此,在闭包的帮助下,即使在内部函数内部的属性也将是私有的。
var Singleton = function () {
var instance;
function init() {
function privateMethod() {
console.log("private via closure");
}
var privateVariable = "Private Property";
var privateRandomNumber = Math.random(); // This is also private
return {
getRandomNumber: function () { // Access via getter in init call
return privateRandomNumber;
}
};
};
return {
getInstance: function () {
if (!instance) {
instance = init();
}
return instance;
}
};
};
其他回答
简单的例子
class Settings {
constructor() {
if (Settings.instance instanceof Settings) {
return Settings.instance;
}
this.settings = {
id: Math.floor(Math.random() * 4000),
name: "background",
};
Object.freeze(this.settings);
Object.freeze(this);
Settings.instance = this;
}
}
var o1 = new Settings();
var o2 = new Settings();
console.dir(o1);
console.dir(o2);
if (o1 === o2) {
console.log("Matched");
}
你可以这样做:
var singleton = new (function() {
var bar = 123
this.foo = function() {
// Whatever
}
})()
Christian C. Salvadó和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 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. Salvadó提供的代码很好地演示了这一点。这里唯一的问题是,当构造函数本身是匿名的时,不可能修改构造函数的原型链。
请记住,在JavaScript中,“公共”和“私有”的概念并不像在PHP或Java中那样适用。但是,通过利用JavaScript的函数作用域可用性规则,我们也达到了同样的效果。
let MySingleton = (function () {
var _instance
function init() {
if(!_instance) {
_instance = { $knew: 1 }
}
return _instance
}
let publicAPIs = {
getInstance: function() {
return init()
}
}
// this prevents customize the MySingleton, like MySingleton.x = 1
Object.freeze(publicAPIs)
// this prevents customize the MySingleton.getInstance(), like MySingleton.getInstance().x = 1
Object.freeze(publicAPIs.getInstance())
return publicAPIs
})();
在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' }
推荐文章
- Babel 6改变了它导出默认值的方式
- 如何配置历史记录?
- ES6模板文字可以在运行时被替换(或重用)吗?
- [Vue警告]:找不到元素
- 可以在setInterval()内部调用clearInterval()吗?
- AngularJS控制器的生命周期是什么?
- 无法读取未定义的属性“msie”- jQuery工具
- 我的蛋蛋怎么不见了?
- JavaScript中的排列?
- JavaScript中有睡眠/暂停/等待功能吗?
- 如何禁用文本选择使用jQuery?
- 如何停止事件冒泡复选框点击
- 如何在PHP中截断字符串最接近于一定数量的字符?
- 向对象数组添加属性
- 如何在Redux应用程序中动态加载代码分割的减速器?