理解了singleton创建共享全局状态的事实,我可能会在某些情况下需要singleton,比如redux store/state对象。
如果我使用ES模块,我可以使用如下简单代码来创建单例:
// a.js (singleton module)
class A {}
// Create singleton
export const a = new A();
现在我可以在其他模块中的任何地方使用这个示例化的对象:
// b.js
// Import singleton
import { a } from './a.js';
console.log(a);
// c.js - some other nested file
import { a } from '../../a.js';
console.log(a);
理论上,应该可以按照上面的描述来管理单例创建。但截至目前,我们使用模块捆绑器,如Webpack.js或Rollup.js来为浏览器提供JavaScript。如果这些捆绑器意外/故意多次包含某些模块,该怎么办?我能想象的最简单的事情是,如果我有某种符号链接通过不同的路径解析到同一个模块。或者它可能只是模块解析过程中的一个错误。
我的问题是-这些模块捆绑器是否总是确保创建单例对象的模块在任何情况下都保持单例?
有一个主题我还没有完全研究过。我知道ES Symbols是全局唯一的,它们被用来创建私有对象成员。这是我的下一个问题-符号的这些特性是否可以帮助我创建一个真正的单例?(我相信如果捆绑过程不健全,符号也会遇到同样的问题。)
最后一个问题:是否有可能在JavaScript中可靠地创建真正的单例?
- 注意:我不是在防范Module bundler中的bug,Bug类比只是一种修辞。*
2条答案
按热度按时间ifmq2ha21#
我经常在rollup中使用这种模式,从来没有遇到过问题。或者你可以像这样定义一个单例:
vaqhlq812#
答案是否定的!模块捆绑器不能确保在捆绑包之间保留单例!
这个问题已经有4年的历史了,所以我希望OP能找到他们想要的答案,但是有一点值得一提的是,在很多情况下,以这种方式创建的单例会不止一次地生成,这破坏了程序员的意图。考虑以下场景:
在本例中,bundle A和bundle B中的代码都将被下载,而您的模块SingletonClass中的代码将被执行两次。这两个bundle之间没有内存共享。
在 modern webpack中,有一个模块联合的概念,可以用来绕过这个问题。
除此之外,Webpack还公开了一个名为
runtimeChunk
的配置,在此问题中提供了有关该解决方案的更多有用信息:How to share singletons instances between chunks with webpack?