Шаблон Singleton с Browserify/CommonJS
Попытка реализовать шаблон singleton в модулях CommonJS, используя Browserify. До сих пор:
//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
Первая проблема заключается в том, что я получаю ошибку maximum call stack exceeded
при загрузке встроенного JS файла в браузере, но, во-вторых, я правильно подошел? Возможно ли это?
Ответы
Ответ 1
Первая проблема заключается в том, что я получаю максимальный стек вызовов превышен error
Ну, это происходит из вашей функции Foo
, которая рекурсивно вызывает new Foo
...
но во-вторых, правильно ли я подошел?
Нет. Для синглетов вам не нужен "класс" с конструктором и прототипом - будет только один экземпляр. Просто создайте один объект, наиболее легко с литералом, и верните его:
module.exports = {
num: 0,
adder: function(){
this.num++;
}
};
Ответ 2
Результатом любого вызова require является singleton - будь то одиночный экземпляр или одноэлементная функция или одиночная функция factory. Кроме того, требуемый вызов должен быть идемпотентным - плохо написанные модули CommonJS могут нарушать это, поэтому, если модуль CommonJS имеет побочный эффект, этот побочный эффект должен произойти только один раз, независимо от того, сколько раз потребуются вызовы.
У фрагмента кода, который у вас есть
if(instance){
return instance;
}
// ...
return instance = new Foo();
- это наследие видов обручей, с которыми вам пришлось бы перепрыгивать, если бы вы использовали простой старый JavaScript для создания синглетов. Это совершенно необязательно при использовании CommonJS, и, кроме того, это приводит к проблеме maximum call stack exceeded
.
Ваш код можно переписать следующим образом:
var Foo = function(){
this.num = 0;
}
Foo.prototype.adder = function(){
this.num++;
};
module.exports = new Foo();
или даже более лаконично:
module.exports = {
num: 0,
adder: function(){ this.num++; }
}
потому что использование метода adder
на прототипе не дает вам реальной эффективности, если вы создаете только один экземпляр Foo и потому, что вам не нужно ничего скрывать в закрытии.