17

Understanding the fact that singleton creates a shared global state, there will be situations where I might want singleton such as redux store/state object.

If I am using ES modules, I can use the simple code as follows to create singleton:

// a.js (singleton module)
class A {}

// Create singleton
export const a = new A();

Now I can use this instantiated object anywhere in another modules:

// 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);

In theory, it should be possible to manage singleton creation as described above. But as of today, we use module bundlers like Webpack.js or Rollup.js to serve JavaScript to the browser. What if these bundlers accidentally/intentionally include some module more than once. The simplest possible thing I can imagine is if I have some sort of symlinks that resolves to the same module via different paths. Or it could simply be a bug in module resolution process.

My question is - do these module bundlers always ensure that a module creating a singleton object stays singleton under every circumstance?

There is one topic which I have not fully looked into. I know ES Symbols are globally unique and they are used to create private object members. That is my next question - Is there a way this characteristics of the symbol can help me create a true singleton? (I believe that symbols will suffer the same problem if bundling process is not sound.)

Finally, last question: Is it ever possible to reliably create a true singleton in JavaScript?

Note: I am not trying to guard against a bug in Module bundler. Bug analogy just is a figure of speech to illustrate the idea.

Harshal Patil
  • 17,838
  • 14
  • 60
  • 126
  • 1
    I do know that if Webpack applies code splitting, the different chunks get their own context, and singletons are not preserved. You can resolve this by introducing a commons chunk: https://webpack.js.org/plugins/commons-chunk-plugin/ – bakkerjoeri Jun 28 '20 at 13:52

2 Answers2

1

The answer is NO! Module bundlers do not ensure singletons are preserved between bundles!

This question is 4 years old so I hope the OP found the answer they were looking for, but it bears saying that there are many cases where singletons created in this manner are generated more than once, destroying the intention of the programmer. Consider the following scenario:

  • you have a project where webpack is configured to place all content from node_modules into a vendor bundle
  • all of the other bundles are generated with files NOT included in node_modules
  • you create 2 modules with entry files ModuleA and ModuleB
  • ModuleA and ModuleB both require SingletonClass
  • THEREFORE! SingletonClass will be bundled into both ModuleA's bundle as well as ModuleB's bundle
  • Both ModuleA's bundle and ModuleB's bundle are included as script tags in the same web page.

What happens in this case is the code from bundle A and B will both be downloaded, the code from your module SingletonClass will be executed twice. There's no memory sharing between the 2 bundles.

In modern webpack there's a concept of module federation which can be used to skirt the issue.

In addition to that, Webpack exposes a configuration called runtimeChunk, more good info on that solution in this question : How to share singletons instances between chunks with webpack?

nameofname
  • 91
  • 5
0

I use that pattern quite often with rollup and never had a problem. Alternatively you can define a singleton like this:

//singleton.js

let instance;

class Singleton{
    constructor(){
        if(instance){
          return instance; 
        }
        instance = this;
   }
}

var a = new Singleton();
var b = new Singleton();

a === b // true
Isidrok
  • 2,015
  • 10
  • 15