我最近遇到了关于如何在Node.js中编写单例的this article。我知道require
states的文档是:
模块在第一次加载后被缓存。对
require('foo')
的多次调用可能不会导致模块代码多次执行。
因此,似乎每个所需的模块都可以很容易地作为一个单例使用,而不需要单例样板代码。
问题:
上面的文章提供了创建单例的解决方案吗?
发布于 2016-08-10 21:31:37
这基本上与nodejs缓存有关。简单明了。
https://nodejs.org/api/modules.html#modules_caching
(v 6.3.1)
缓存
模块在第一次加载后被缓存。这意味着(在其他事情中),每次对require('foo')的调用都将得到完全相同的对象返回,如果它将解析到相同的文件。
对require('foo')的多次调用可能不会导致模块代码多次执行。这是一个重要的特性。有了它,可以返回“部分完成”的对象,从而允许加载传递依赖,即使它们会导致循环。
如果您想让一个模块多次执行代码,那么导出一个函数,然后调用该函数。
模块缓存注意事项
模块根据其解析的文件名进行缓存。由于模块可能会根据调用模块的位置解析为不同的文件名(从node_modules文件夹加载),因此不能保证require('foo')将始终返回完全相同的对象,如果它将解析为不同的文件。
此外,在不区分大小写的文件系统或操作系统上,不同的解析文件名可以指向相同的文件,但缓存仍会将它们视为不同的模块,并会多次重新加载文件。例如,require(' ./foo ')和require(' ./FOO ')返回两个不同的对象,而不管./FOO和./FOO是否是同一个文件。
所以简而言之。
如果需要单例;,请导出对象。
如果您不想要单例;导出一个函数(并在该函数中执行填充/返回填充/其他操作)。
非常清楚的是,如果你正确地这样做,它应该可以工作,看看 (Allen Luce的答案)。它在代码中解释了当缓存由于不同解析的文件名而失败时会发生什么。但是,如果您总是解析到相同的文件名,它应该可以工作。
更新2016
creating a true singleton in node.js with es6 symbols Another solution:in this link
更新2020
这个答案指的是CommonJS (Node.js自己的导入/导出模块的方式)。Node.js很可能会切换到ECMAScript模块:https://nodejs.org/api/esm.html (如果你不知道,ECMAScript是JavaScript的真实名称)
现在迁移到ECMAScript时,请阅读以下内容:https://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards
发布于 2012-11-01 22:35:58
以上所有这些都过于复杂了。有一种思想流派认为,设计模式显示了实际语言的缺陷。
具有基于原型的OOP (无类)的语言根本不需要单例模式。您只需动态创建一个(Ton)对象,然后使用它。
至于节点中的模块,是的,默认情况下它们是缓存的,但它可以进行调整,例如,如果您想要热加载模块更改。
但是,是的,如果你想在所有地方使用共享对象,把它放在一个模块导出中是可以的。只是不要用“单例模式”来复杂化它,在JavaScript中不需要它。
发布于 2015-11-17 07:45:43
No。当节点的模块缓存失败时,单例模式也会失败。我修改了示例,以便在OSX上有意义地运行:
var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");
console.log(sg.getSocketList(), sg2.getSocketList());
这给出了作者预期的输出:
{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }
但是一个小小的修改就能击败缓存。在OSX上,执行以下操作:
var sg = require("./singleton.js");
var sg2 = require("./SINGLETON.js");
sg.add(1, "test");
sg2.add(2, "test2");
console.log(sg.getSocketList(), sg2.getSocketList());
或者,在Linux上:
% ln singleton.js singleton2.js
然后将sg2
require行更改为:
var sg2 = require("./singleton2.js");
然后砰的一声,独生子被击败了:
{ '1': 'test' } { '2': 'test2' }
我不知道有什么可以接受的方法来解决这个问题。如果您确实觉得有必要制作类似于单例的东西,并且可以接受污染全局名称空间(以及可能导致的许多问题),那么可以将作者的getInstance()
和exports
行更改为:
singleton.getInstance = function(){
if(global.singleton_instance === undefined)
global.singleton_instance = new singleton();
return global.singleton_instance;
}
module.exports = singleton.getInstance();
也就是说,我从来没有在生产系统中遇到过需要这样做的情况。我也从来没有觉得有必要在Javascript中使用单例模式。
https://stackoverflow.com/questions/13179109
复制相似问题