Singleton pattern with Browserify/CommonJS

2019-02-04 15:39发布

Trying to implement the singleton pattern within CommonJS modules, using Browserify. So far:

// foo.js

var instance = null;

var Foo = function(){
    if(instance){
        return instance;
    }
    this.num = 0;
    return instance = new Foo();
}

Foo.prototype.adder = function(){
    this.num++;
};

module.exports = Foo();

// main.js

var foo = require('./foo.js');
console.log(foo.num); // should be 0
foo.adder(); // should be 1
var bar = require('./foo.js');
console.log(bar.num); // like to think it'd be 1, not 0

First problem is that I get a maximum call stack exceeded error when I load the built JS file in the browser, but secondly, am I approaching this correctly? Is this possible?

2条回答
别忘想泡老子
2楼-- · 2019-02-04 15:55

First problem is that I get a maximum call stack exceeded error

Well, that comes from your Foo function recursively calling new Foo

but secondly, am I approaching this correctly?

No. For singletons, you don't need a "class" with a constructor and a prototype - there will only ever be one instance. Simply create one object, most easily with a literal, and return that:

module.exports = {
    num: 0,
    adder: function(){
        this.num++;
    }
};
查看更多
啃猪蹄的小仙女
3楼-- · 2019-02-04 16:20

The result of any require call is a singleton -- whether a singleton instance or a singleton function or a singleton factory function. Furthermore, a require call should be idempotent -- poorly written CommonJS modules may violate this, so if a CommonJS module has a side effect, that side effect should happen only once no matter how many times that require is called.

The code snippet you have

if(instance){
    return instance;
}
// ...
return instance = new Foo();

is a legacy of the kinds of hoops you'd have to jump through if you were using plain old JavaScript to create singletons. It's totally unnecessary when using CommonJS and besides that, it leads to your maximum call stack exceeded issue.

Your code could be rewritten like this:

var Foo = function(){
   this.num = 0;
}

Foo.prototype.adder = function(){
   this.num++;
};

module.exports = new Foo();

or even more succinctly:

module.exports = {
   num: 0,
   adder: function(){ this.num++; }
}

because putting the adder method on the prototype doesn't gain you any real efficiency if you're only creating a single instance of Foo, and because you don't need to hide anything in the closure.

查看更多
登录 后发表回答