用JavaScript实现单例模式的最简单/最干净的方法是什么?
当前回答
单例模式:
确保一个类只有一个实例,并提供对它的全局访问点。
单例模式将特定对象的实例数量限制为一个。这个单一实例称为单例。
定义返回唯一实例的getInstance()。 负责创建和管理实例对象。
单例对象被实现为一个即时匿名函数。函数立即执行,将它括在括号中,然后再加上两个括号。它被称为匿名,因为它没有名字。
示例程序
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()
其他回答
var singleton = (function () {
var singleton = function(){
// Do stuff
}
var instance = new singleton();
return function(){
return instance;
}
})();
一个没有getInstance方法的解决方案。
这也是一个单例:
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();
下面是我实现单例模式的演练片段。这是我在面试过程中想到的,我觉得我应该在某个地方捕捉到这一点。
/*************************************************
* SINGLETON PATTERN IMPLEMENTATION *
*************************************************/
// Since there aren't any 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 the 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 give 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
*******************************************************************/
同样可以在我的主旨页面上找到。
简短的回答:
由于JavaScript的非阻塞特性,JavaScript中的单例在使用时非常难看。全局变量将在整个应用程序中为您提供一个实例,而不需要所有这些回调,模块模式将内部隐藏在接口后面。参见Christian C. Salvadó的回答。
但是,既然你想要一个单胞胎……
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 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();
});
解释:
单例在整个应用程序中提供了不止一个实例:它们的初始化延迟到第一次使用。当您处理初始化开销很大的对象时,这确实是一个大问题。昂贵通常意味着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.");
我反对我的回答,看看我的另一个。
通常,模块模式(参见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();
};
};
然后我可以做以下事情:
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
推荐文章
- 使用jQuery以像素为整数填充或边距值
- 检查是否选择了jQuery选项,如果没有选择默认值
- Next.js React应用中没有定义Window
- 如何重置笑话模拟函数调用计数之前,每次测试
- 如何强制一个功能React组件渲染?
- 在javascript中从平面数组构建树数组
- 将Dropzone.js与其他字段集成到现有的HTML表单中
- 如何在AngularJS中观察路由变化?
- JavaScript DOM删除元素
- 将dd-mm-yyyy字符串转换为日期
- Javascript复选框onChange
- Javascript函数前导bang !语法
- 如何在页面上遍历所有DOM元素?
- 在JS/jQuery中触发按键/按键/按键事件?
- 如何每5秒重新加载页面?