论坛首页 Web前端技术论坛

Prototype.AjaxRequest的调用堆栈重写问题

浏览 7202 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-12-27  

Prototype.AjaxRequest的调用堆栈重写问题<o:p></o:p>

作者:cleverpig<o:p></o:p>

<o:p> </o:p>

由于调用AjaxRequest类进行XMLHTTPRequest操作时,this引用(指向当前function所在的对象)会出现了call stack问题,从而指向当前的对象:<o:p></o:p>

错误演示:<o:p></o:p>

var OverWritingDemonstrate=Class.create();<o:p></o:p>

OverWritingDemonstrate.prototype={<o:p></o:p>

    xml_source:'',<o:p></o:p>

    initialize:function(){<o:p></o:p>

    },<o:p></o:p>

    putRequest:function(url,params,callBackFunction){<o:p></o:p>

       var funcHolder=arguments.callee.$;<o:p></o:p>

        var xmlHttp = new Ajax.Request(url,<o:p></o:p>

           {<o:p></o:p>

              method: 'get', <o:p></o:p>

                   parameters: params, <o:p></o:p>

              requestHeaders:['my-header-encoding','utf-8'],<o:p></o:p>

                   onFailure: function(){<o:p></o:p>

                  alert('对不起,网络通讯失败,请重新刷新!');<o:p></o:p>

              },<o:p></o:p>

              onSuccess: function(transport){<o:p></o:p>

              },<o:p></o:p>

              onComplete: function(transport){<o:p></o:p>

                  this.xml_source=transport.responseText;<o:p></o:p>

                  this.showXMLResponse();<o:p></o:p>

              }<o:p></o:p>

           });<o:p></o:p>

    },<o:p></o:p>

    //显示xml信息<o:p></o:p>

    showXMLResponse:function(){<o:p></o:p>

       alert(this.xml_source);<o:p></o:p>

    },<o:p></o:p>

    …<o:p></o:p>

}<o:p></o:p>

这样使用必定找不到showXMLResponse方法,因为在AjaxRequestonComplete函数中的this指向了当前的function所在的对象xmlHttp,而不是我们的OverWritingDemonstrate类对象。<o:p></o:p>

<o:p> </o:p>

Fix方法:<o:p></o:p>

我们可以借鉴一下《解开JavaScript生命的达芬奇密码》Joshua Gertzen的方法,实现一个ClassUtils类:<o:p></o:p>

//类工具<o:p></o:p>

var ClassUtils=Class.create();<o:p></o:p>

ClassUtils.prototype={<o:p></o:p>

    _ClassUtilsName:'ClassUtils',<o:p></o:p>

    initialize:function(){<o:p></o:p>

    },<o:p></o:p>

    /**<o:p></o:p>

     * 给类的每个方法注册一个对类对象的自我引用<o:p></o:p>

     * @param reference 对类对象的引用<o:p></o:p>

     */<o:p></o:p>

    registerFuncSelfLink:function(reference){<o:p></o:p>

       for (var n in reference) {<o:p></o:p>

        var item = reference[n];                        <o:p></o:p>

        if (item instanceof Function) <o:p></o:p>

              item.$ = reference;<o:p></o:p>

    }<o:p></o:p>

    }<o:p></o:p>

}<o:p></o:p>

<o:p> </o:p>

然后修改一下前面的OverWritingDemonstrate,这里为了达到区分效果的目的,类名取为AjaxWrapper<o:p></o:p>

//Ajax操作封装类:<o:p></o:p>

//由于调用AjaxRequest类进行XMLHTTPRequest操作时,this引用(指向当前的对象)会出现了call stack问题,从而指向当前的对象。<o:p></o:p>

//所以,对putRequestcallBackHandler、以及callback方法都要使用arguments.callee.$来获得正确的类对象引用<o:p></o:p>

var AjaxWrapper=Class.create();<o:p></o:p>

AjaxWrapper.prototype={<o:p></o:p>

    xml_source:'',<o:p></o:p>

    /**<o:p></o:p>

     * 初始化<o:p></o:p>

     * @param isDebug 是否显示调试信息<o:p></o:p>

     */<o:p></o:p>

    initialize:function(isDebug){<o:p></o:p>

       new ClassUtils().registerFuncSelfLink(this);<o:p></o:p>

    },<o:p></o:p>

    putRequest:function(url,params,callBackFunction){<o:p></o:p>

       var funcHolder=arguments.callee.$;<o:p></o:p>

        var xmlHttp = new Ajax.Request(url,<o:p></o:p>

           {<o:p></o:p>

              method: 'get', <o:p></o:p>

                parameters: params, <o:p></o:p>

              requestHeaders:['my-header-encoding','utf-8'],<o:p></o:p>

                onFailure: function(){<o:p></o:p>

                  alert('对不起,网络通讯失败,请重新刷新!');<o:p></o:p>

              },<o:p></o:p>

              onSuccess: function(transport){<o:p></o:p>

              },<o:p></o:p>

              onComplete: function(transport){<o:p></o:p>

                  funcHolder.xml_source=transport.responseText;<o:p></o:p>

                  funcHolder.showXMLResponse();<o:p></o:p>

              }<o:p></o:p>

           });<o:p></o:p>

    },<o:p></o:p>

    //显示xml信息<o:p></o:p>

    showXMLResponse:function(){<o:p></o:p>

       alert(funcHolder.xml_source);<o:p></o:p>

    },<o:p></o:p>

    …<o:p></o:p>

}<o:p></o:p>

这样就避免了发生在调用堆栈中的this重写问题了。<o:p></o:p>

<o:p> </o:p>

代码下载:

    demonstrate.rar

相关资源:<o:p></o:p>

   解开JavaScript生命的达芬奇密码<o:p></o:p>

   Prototype JavaScript framework website

   Prototype 快速教学

   Prototype开发手册

   发表时间:2006-12-28  
给每个对象增加一个属性(这里是$)来指向对象的引用,通过arguments.callee来指向当前function的父funcion的对象引用,再通过$属性来取得该父function对象的引用,实在是个绝秒的办法啊.
和我的实现oo树的思想类似,不过先前我不知道arguments.callee这个东西的用处
http://www.iteye.com/topic/40798
0 请登录后投票
   发表时间:2006-12-28  
好像在方法后面调用一个bind(this)就可以了,很多这种用法。

new Ajax.Request(url,
{
  method: 'get',
  parameters: params,
  requestHeaders:['my-header-encoding','utf-8'],
  onFailure: function(){
    alert('对不起,网络通讯失败,请重新刷新!');
  }.bind(this),
  onSuccess: function(transport){
  }.bind(this),
  onComplete: function(transport){
    this.xml_source=transport.responseText;
    this.showXMLResponse();
  }.bind(this)
});

上面代码没测试,不过我在使用prototype时是经常这么用的。
0 请登录后投票
   发表时间:2006-12-28  
qiezi 写道
好像在方法后面调用一个bind(this)就可以了,很多这种用法。

new Ajax.Request(url,
{
  method: 'get',
  parameters: params,
  requestHeaders:['my-header-encoding','utf-8'],
  onFailure: function(){
    alert('对不起,网络通讯失败,请重新刷新!');
  }.bind(this),
  onSuccess: function(transport){
  }.bind(this),
  onComplete: function(transport){
    this.xml_source=transport.responseText;
    this.showXMLResponse();
  }.bind(this)
});

上面代码没测试,不过我在使用prototype时是经常这么用的。


请仔细观察该贴的例子,prototype中的bind(this)方法,上针对本class的实例的,而这里的例子中即使bind(this)了,也只是调用xmlhttp类的实例的该方法,所以在这里是肯定不行的,仔细分析一下bind方法,其实他的思想和我上面说的是一样的,其中的-method变量就是用来指向当前类实例的引用的。
0 请登录后投票
   发表时间:2006-12-28  
new Ajax.Request(url,  
{  
  method: 'get',  
  parameters: params,  
  requestHeaders:['my-header-encoding','utf-8'],  
  onFailure: function(){  
    alert(this.url);  
  }.bind(this),  
  onSuccess: function(transport){  
    alert(this.url);  
  }.bind(this),  
  onComplete: function(transport){  
    alert(this.url);  
  }.bind(this)  
}); 

这样使用bind的结果应该就是上面的每个经过bind的方法,其this.url能被alert了,也没测试。
0 请登录后投票
   发表时间:2006-12-28  
Prototype1.4中bind方法代码:
Function.prototype.bind = function() {
  var __method = this, 
  args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

在AjaxRequest中使用bind(this),只是绑定了AjaxRequest对象,而并不是外部的类对象引用。
0 请登录后投票
   发表时间:2006-12-28  
简单点:
function Test(){
}

Test.prototype.foo = function(){
  new Ajax.Request("/",  {  
    onFailure: function(){  
      alert(this.url);  
    }.bind(this)
  }); 
}

Test.prototype.url = "http://www.iteye.com";

上面这个我已经测试过了,可以显示出"http://www.iteye.com"。
0 请登录后投票
   发表时间:2006-12-29  
qiezi 写道
简单点:
function Test(){
}

Test.prototype.foo = function(){
  new Ajax.Request("/",  {  
    onFailure: function(){  
      alert(this.url);  
    }.bind(this)
  }); 
}

Test.prototype.url = "http://www.iteye.com";

上面这个我已经测试过了,可以显示出"http://www.iteye.com"。


你的bind是对的,因为例子中并没有存在内部类,而是在类A的内部定义一个对象new Ajax.Request(),
onFailure: function(){ 
      alert(this.url); 
    }.bind(this)
像上面这样的方法也不是写在Ajax.Request的内部的,而是写在其类函数的参数对象里面,就是说到这里我们的this还没有逃出外面的父类的实例A,用一下bind还是能绑定的,但是其方法this.showMessage却不行了,因为前面的this会在运行时成为xmhttp对象,即使bind了,也导致了一个问题,showMessage不是Ajax.Request类里定义的,会找不到这个方法,如果Ajax.Request里刚好有showMessage这个方法,那么方法里如果有this,运行时也指向了Ajax.Request的实例了而不是A。
0 请登录后投票
   发表时间:2006-12-29  
jianfeng008cn 写道

new Ajax.Request(url,  
{  
  method: 'get',  
  parameters: params,  
  requestHeaders:['my-header-encoding','utf-8'],  
  onFailure: function(){  
    alert(this.url);  
  }.bind(this),  
  onSuccess: function(transport){  
    alert(this.url);  
  }.bind(this),  
  onComplete: function(transport){  
    alert(this.url);  
  }.bind(this)  
}); 

这样使用bind的结果应该就是上面的每个经过bind的方法,其this.url能被alert了,也没测试。


这个例子是错的,大家擦亮眼睛哦,“js是解释型的弱类型语言” 啊门。
这里如果有了bind应该就不能alert了,没有则可以。
0 请登录后投票
   发表时间:2006-12-29  
jianfeng008cn说的没错,调用堆栈的问题,往往出现在XMLHTTPRequest对象的function中call其它类对象的方法时,这发生在XMLHTTPRequest对象中,而此时this指向了XMLHTTPRequest对象。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics