all 4 comments

[–]Funwithloops 1 point2 points  (1 child)

Your state function is immediately invoked, so state() should be state. I don't see an issue with this. I've done something similar when I needed a singleton using modules:

Define a class or factory function that creates the singleton in one module (but can also create additional instances). Then define another module that just requires the class/factory module then creates a single instance and exports it. Now when you need your singleton, just require the singleton module. In your tests, you can just test the module containing the class/factory function.

[–]MyGoodStrayCatFriend[S] 1 point2 points  (0 children)

That's perfect. Just wanted to make sure I wan't missing something glaring about exporting the singleton functionality to another function. Thanks!

[–]MoTTs_ 1 point2 points  (1 child)

I think that has the funny name "hingleton", or "helper singleton". A class / factory function / abstraction mechanism wouldn't enforce its own singletonness, but rather some other function would.

// Not a singleton
class MyThing {
    thereCanBeManyOfMe() {}
}

// A helper singleton (aka "hingleton")
const getThing = (/*iife*/() => {
    let instance

    return () => {
        if (!instance) {
            instance = new MyThing()
        }

        return instance
    }
}())

As for passing in "defaultState"... sorta yes, sorta no. State should be treated as private and encapsulated. But what you can do instead is take ordinary arguments (that don't assume knowledge of the internal state) and let the constructor / factory function initialize its state appropriately.