`
cleverpig
  • 浏览: 150740 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

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

阅读更多

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开发手册

分享到:
评论
10 楼 zkj_beyond 2006-12-29  
我认为楼主的代码,有些坏味道。
从面向对象编程角度,两个对象交叉引用,并且 重复方法:与另一个方法、函数、过程十分相似的方法、函数、过程。
http://wiki.matrix.org.cn/Wiki.jsp?page=CodeSmell

从函数式编程角度,有副作用。
从mvc角度,putRequest,showXMLResponse 这两个方法,绝对应该属于两个类。和webwork action一样,职责太多。

归根是prototype.js这段代码封装得不完美,请求服务器,完全可以当作函数调用,干吗搞个对象出来,看看YUI connection,dojo.io.bind  代码,都是一个单例对象,用poll来解决并发问题。
9 楼 cleverpig 2006-12-29  
jianfeng008cn说的没错,调用堆栈的问题,往往出现在XMLHTTPRequest对象的function中call其它类对象的方法时,这发生在XMLHTTPRequest对象中,而此时this指向了XMLHTTPRequest对象。
8 楼 jianfeng008cn 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了,没有则可以。
7 楼 jianfeng008cn 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。
6 楼 qiezi 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"。
5 楼 cleverpig 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对象,而并不是外部的类对象引用。
4 楼 jianfeng008cn 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了,也没测试。
3 楼 jianfeng008cn 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变量就是用来指向当前类实例的引用的。
2 楼 qiezi 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时是经常这么用的。
1 楼 jianfeng008cn 2006-12-28  
给每个对象增加一个属性(这里是$)来指向对象的引用,通过arguments.callee来指向当前function的父funcion的对象引用,再通过$属性来取得该父function对象的引用,实在是个绝秒的办法啊.
和我的实现oo树的思想类似,不过先前我不知道arguments.callee这个东西的用处
http://www.iteye.com/topic/40798

相关推荐

    Ext.Ajax.request跨域

    标题"Ext.Ajax.request跨域"指出我们将探讨如何使用ExtJS库中的Ajax模块进行跨域请求。Ext.Ajax.request是ExtJS提供的一种发起Ajax请求的方式,它允许开发者向服务器发送异步HTTP请求。然而,由于浏览器的同源策略...

    Ext.Ajax.request2.x实现同步请求

    在EXTJS库中,`Ext.Ajax.request`是用于发送Ajax请求的核心方法,它支持异步和同步操作。本文将详细解析如何利用`Ext.Ajax.request`实现同步请求,并探讨其背后的原理和注意事项。 首先,我们需要理解Ajax的本质,...

    Ext.Ajax.request 小问题收集

    这篇文章将深入探讨`Ext.Ajax.request`的使用,以及在实际应用中可能遇到的小问题。 首先,`Ext.Ajax.request`的基本语法如下: ```javascript Ext.Ajax.request({ url: 'your-url', method: 'GET', // 可选值有...

    prototype.js简介

    3. **Ajax** - prototype.js 改进了原始的XMLHttpRequest对象,提供了`Ajax`模块,包含`Ajax.Request`和`Ajax.Updater`等类,使异步数据交换更加简单和灵活。 4. **事件处理** - 库中的`Event.observe()`和`Event....

    prototype.js

    2. **Ajax**:Prototype.js提供了一套完善的Ajax解决方案,包括`Ajax.Request`和`Ajax.Updater`,使得异步数据交互更为简单,同时支持JSON、XML等多种数据格式。 3. **事件处理**:Prototype.js通过`Event.observe...

    prototype.js文件使用和讲解

    - **Ajax支持**:`prototype.js`提供了一套完整的Ajax解决方案,包括`Ajax.Request`、`Ajax.Updater`和`Ajax.PeriodicalUpdater`等类,使得异步通信更为简便。 - **事件处理**:它增强了事件处理机制,允许绑定和...

    prototype.js中文手册

    2. **Ajax功能**:Prototype.js 强化了AJAX操作,通过`Ajax.Request` 和 `Ajax.Updater` 类,开发者可以轻松实现异步数据交换,无需关心浏览器兼容性问题。 3. **对象扩展**:Prototype.js 基于JavaScript原型链...

    prototype.js 1.6中文手册、prototype.js 1.6英文手册、

    `Ajax.Request`和`Ajax.Updater`是核心组件,前者用于发送请求,后者用于更新页面的一部分。它们都支持多种HTTP方法(GET、POST等),并能处理JSON、XML等多种数据格式。 ### 4. 动画效果 Prototype.js 提供了强大...

    prototype.js 1.4-1.6[全]

    Prototype.js 提供了强大的Ajax功能,包括`Ajax.Request`和`Ajax.Updater`等,它们简化了异步请求和页面局部更新的代码。通过这些工具,开发者可以轻松构建富交互性的Web应用。 6. **动画效果** 库中还包含了一些...

    prototype_1.7.3.js 最新版本

    `Ajax.Request`和`Ajax.Updater`是其中两个关键的类,它们分别用于发起完整的HTTP请求和替换页面的部分内容。 Prototype.js的另一个显著特点是对JavaScript对象的增强。1.7.3版本继续沿用了基于类的模拟实现,通过`...

    prototype.js 1.6

    Prototype.js 的 AJAX 支持在 1.6 版本中得到增强,`Ajax.Request` 和 `Ajax.Updater` 类提供了一种优雅的方式来处理异步数据交换。新版本增加了错误处理机制,同时支持 JSONP 和 CORS,适应了跨域请求的需求。 ###...

    prototype.js javaScript插件

    `Ajax.Request`和`Ajax.Updater`可以轻松发起GET或POST请求,并处理响应。此外,还有`Ajax.PeriodicalUpdater`用于定期更新页面内容。 - **JSON支持**:Prototype.js提供了`toJSON`方法,可以将JavaScript对象转换...

    prototype.js开发笔记--让你精通prototype开发

    1.4.1. 使用 Ajax.Request类 1.4.2. 使用 Ajax.Updater 类 2. prototype.js参考 2.1. JavaScript 类的扩展 2.2. 对 Object 类的扩展 2.3. 对 Number 类的扩展 2.4. 对 Function 类的扩展 2.5. 对 String 类的扩展 ...

    prototype.js 1.4版开发者手册(强烈推荐)

    3. **Ajax支持**:Prototype.js提供了强大的Ajax功能,包括`Ajax.Request`和`Ajax.Updater`等,它们允许开发者轻松实现页面的异步更新,无需刷新整个页面,从而提高用户体验。 4. **事件处理**:库中包含了事件绑定...

    prototype.js开发笔记.pdf

    Ajax.Request类用于发送Ajax请求。 1.4.2.使用Ajax.Updater类 Ajax.Updater类用于更新页面中的某个元素。 二、Prototype.js参考 Prototype.js提供了许多有用的函数和方法,以帮助开发者快速构建Web应用程序。...

    prototype-AJAX案例

    1. **Ajax.Request**: 这是Prototype的核心AJAX方法,用于发起HTTP请求。例如: ```javascript new Ajax.Request('url', { method: 'get' || 'post', parameters: {key: value}, // 发送的数据 onSuccess: ...

    prototype.js.cn.doc.rar

    `Ajax.Request`和`Ajax.Updater`类用于发送HTTP请求,处理响应数据,实现了页面局部更新。 2. **Selectors API**:Prototype.js兼容了CSS选择器,提供了`$$()`函数,可以像操作CSS一样选取DOM元素,增强了...

    ext-basex.js 进行Ext.Ajax.request 同步请求 FF无法正常

    标题提到的"ext-basex.js进行Ext.Ajax.request同步请求 FF无法正常"问题,涉及到浏览器兼容性和异步/同步请求的理解。 `Ext.Ajax.request`是ExtJS中的一个方法,用于发起Ajax(异步JavaScript和XML)请求。它可以...

    prototype.js_v1.6_含中英文手册

     prototype.js是一个非常优雅的javascript基础类库,对javascript做了大量的扩展,而且很好的支持Ajax,国外有多个基于此类库实现的效果库,也做得很棒。  prototype.js不仅是一个有很大实用价值的js库,而且有很...

Global site tag (gtag.js) - Google Analytics