在javascript中有条件地实例化模块模式的一个实例

时间:2012-11-14 21:34:51

标签: javascript module-pattern

我正在尝试公开一个模块。我想只向所有调用者公开它的一个实例,我想等到调用模块来实例化它。我试着这样做:

var obj = {};

var foobar = function(){
 var id=22;
 function GetId(){ return ++id; }
 return{ GetId: GetId };
};

obj.foobar = (function(){
    if (obj.foobar instanceof foobar) {
         return obj.foobar;
    }
    return new foobar();
})();

console.log(obj.foobar.GetId());//23
console.log(obj.foobar.GetId());//24

但实际上这只是对

的混淆
obj.foobar = new foobar();

我原本打算在第一次调用obj.foobar = new foobar()时实例化obj.foobar.GetId(),第二次调用obj.foobar.GetId()时使用已经实例化的版本。虽然此处不存在,但仍存在需要等待实例化new foobar();的依赖项,因此无法立即执行。

我怎样才能做到这一点,我错过了什么?

2 个答案:

答案 0 :(得分:4)

每次访问foobar时都可以使用函数调用:

obj.foobar = (function() {
  var inst;
  return function() {
    return inst || (inst = foobar());
  };
})();

console.log(obj.foobar().GetId()); // 23

如果目标执行环境支持ECMAScript 5的命名访问器属性,也可以使用它:

Object.defineProperty(obj, "foobar", {
  get: (function() {
    var inst;
    return function() {
      return inst || (inst = foobar());
    };
  })()
});

console.log(obj.foobar.GetId()); // 23

或者,只要您知道可以在foobar上调用的方法列表,就可以使用更复杂的解决方案:

obj.foobar = (function() {
  var inst, res = {}, methods = ["GetId"];
  function createLazyMethod(method) {
    return function() {
      if (!inst) {
        obj.foobar = inst = foobar();
      }

      return inst[method].apply(inst, methods.slice.call(arguments, 0));
    };
  }

  for (var i = 0; i < methods.length; ++i) {
    res[methods[i]] = createLazyMethod(methods[i]);
  }

  return res;
})();

console.log(obj.foobar.GetId()); // 23

使用此解决方案,一旦foobar被实例化,对其方法的调用将为零成本。

答案 1 :(得分:2)

  

我打算在第一次调用obj.foobar = new foobar()时实例化obj.foobar.GetId()

不,这不起作用。如果调用getId方法,则必须已存在现有对象。您可以为foobar的{​​{1}}属性定义一个getter函数,该属性在访问时创建实例,或者您之前只是实例化它(正如您在IEFE中所做的那样,并且可以更短的完成赋值,如你所说)。

通常,您将使用每次调用的函数(也可以是构造函数),如果已经创建了它,则返回单例,否则创建一个并存储它:

obj