使用Browserify/CommonJS的单例模式

ben*_*e89 13 javascript commonjs browserify

尝试使用Browserify在CommonJS模块中实现单例模式.至今:

// 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();
Run Code Online (Sandbox Code Playgroud)

// 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
Run Code Online (Sandbox Code Playgroud)

第一个问题是,maximum call stack exceeded当我在浏览器中加载构建的JS文件时出现错误,但其次,我是否正确地接近了这个?这可能吗?

Ber*_*rgi 28

第一个问题是我得到一个超出最大调用堆栈的错误

好吧,这来自你的Foo函数递归调用new Foo...

但其次,我正确接近这个吗?

不.对于单身人士,你不需要一个带有构造函数和原型的"类" - 只会有一个实例.只需创建一个对象,最容易用文字创建,然后返回:

module.exports = {
    num: 0,
    adder: function(){
        this.num++;
    }
};
Run Code Online (Sandbox Code Playgroud)

  • @ benhowdle89不,它没有,导出的值被缓存. (12认同)
  • 目前正在吃我的话.:) (9认同)

I-L*_*Kuo 22

任何require调用的结果都是单例 - 无论是单例实例还是单例函数还是单例工厂函数.此外,require调用应该是幂等的 - 编写得不好的CommonJS模块可能会违反这一点,因此如果CommonJS模块有副作用,那么无论调用多少次,副作用都应该只发生一次.

你有的代码片段

if(instance){
    return instance;
}
// ...
return instance = new Foo();
Run Code Online (Sandbox Code Playgroud)

如果您使用普通的旧JavaScript来创建单例,那么您必须跳过各种箍的遗产.使用CommonJS时完全没必要,除此之外,它会导致您的maximum call stack exceeded问题.

您的代码可以像这样重写:

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

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

module.exports = new Foo();
Run Code Online (Sandbox Code Playgroud)

甚至更简洁:

module.exports = {
   num: 0,
   adder: function(){ this.num++; }
}
Run Code Online (Sandbox Code Playgroud)

因为adder如果你只创建一个Foo实例,并且因为你不需要在闭包中隐藏任何东西,那么将该方法放在原型上并不能获得任何真正的效率.

  • 我甚至不知道什么是真实的.这是王牌. (8认同)
  • @BigDong在这种情况下,你总是可以在导出的对象上包含一个"init"方法并以这种方式传递选项. (2认同)