JavaScript'类'和单例问题

时间:2010-04-19 12:25:58

标签: javascript singleton closures

我有一个使用另一个对象(不是单例)的单例对象,需要一些信息到服务器:

var singleton = (function(){

  /*_private properties*/
  var myRequestManager = new RequestManager(params,
    //callbacks
    function(){
        previewRender(response);
    },
    function(){
        previewError();
    }
  );

  /*_public methods*/
  return{

    /*make a request*/
    previewRequest: function(request){
       myRequestManager.require(request);  //err:myRequestManager.require is not a func
    },

    previewRender: function(response){
      //do something
    },

    previewError: function(){
      //manage error
    }
  };
}());

这是向服务器发出请求的“类”

function RequestManager(params, success, error){
  //create an ajax manager
  this.param = params;
  this._success = success;  //callbacks
  this._error = error;
}

RequestManager.prototype = {

  require: function(text){
    //make an ajax request
  },
  otherFunc: function(){
     //do other things
  }

}

问题是我无法从单例对象中调用myRequestManager.require。 Firebug consolle说:“myRequestManager.require不是函数”,但我不明白问题出在哪里。 有没有更好的解决方案来实现这种情况?

1 个答案:

答案 0 :(得分:6)

您的代码按照您引用的顺序排列,不是吗?单身人士出现在来源的RequestManager上方?

如果是这样,那就是你的问题。这是相当微妙的(!),但假设您的两位引用代码按照您显示的顺序排列,这是事情发生的顺序(我将在下面详细解释):

  1. 定义了函数RequestManager
  2. 您创建单身人士的匿名函数,包括实例化RequestManager的实例。
  3. RequestManager原型被替换为新原型。
  4. 由于{<1}}实例在之前实例化原型已更改,因此它没有您在该(新)原型上定义的功能。它继续使用实例化时原型对象。

    您可以通过重新排序代码或通过向myRequestManager的原型添加属性而不是替换它来轻松解决此问题,例如:

    RequestManager

    这是有效的,因为你没有替换原型对象,你刚刚添加它。 RequestManager.prototype.require = function(text){ //make an ajax request }; RequestManager.prototype.otherFunc = function(){ //do other things }; 看到了添加内容,因为您已将它们添加到它正在使用的对象中(而不是在构造函数的myRequestManager属性上设置新对象)。

    为什么会发生这种情况有点技术性,我将主要遵从规范。当解释器输入新的“执行上下文”(例如,函数或全局 - 例如,页面级 - 上下文)时,它执行操作的顺序不是严格的自上而下的源顺序,存在阶段。第一阶段之一是实例化上下文中定义的所有函数;在之前发生任何逐步执行的代码。在the spec的10.4.1节(全局代码),10.4.3(功能代码)和10.5(声明绑定)中详细介绍了它们的所有内容,但基本上,这些函数是在第一行的第一行之前创建的 - 步骤代码。 : - )

    使用隔离的测试示例最容易看到:

    prototype

    正如您所看到的,如果您运行它,<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <title>Test Page</title> <style type='text/css'> body { font-family: sans-serif; } </style> <script type='text/javascript'> // Uses Thing1 var User1 = (function() { var thing1 = new Thing1(); function useIt() { alert(thing1.foo()); } return useIt; })(); // Uses Thing2 var User2 = (function() { var thing2 = new Thing2(); function useIt() { alert(thing2.foo()); } return useIt; })(); // Thing1 gets its prototype *replaced* function Thing1() { this.name = "Thing1"; } Thing1.prototype = { foo: function() { return this.name; } }; // Thing2 gets its prototype *augmented* function Thing2() { this.name = "Thing2"; } Thing2.prototype.foo = function() { return this.name; }; // Set up to use them window.onload = function() { document.getElementById('btnGo').onclick = go; } // Test! function go() { alert("About to use User1"); try { User1(); } catch (e) { alert("Error with User1: " + (e.message ? e.message : String(e))); } alert("About to use User2"); try { User2(); } catch (e) { alert("Error with User2: " + (e.message ? e.message : String(e))); } } </script> </head> <body><div> <div id='log'></div> <input type='button' id='btnGo' value='Go'> </div></body> </html> 会失败,因为它使用的User1实例没有Thing1属性(因为原型已被替换),但foo 1}}起作用,因为它使用* User2实例(因为原型是增强的,而不是替换的)。