jQuery AJAX回调函数this指向问题 |
本文标签:jQuery,AJAX,回调函数 如在全局作用域调用一个含this的对象,此时当前对象的this指向的是window 。为了让this的指向符合自己的意愿,JavaScript提供了两个方法用以改变this的指向,它们是call和apply,当然也有利用闭包来实现的方法 。本文通过一个例子来说明这些问题 。 先看一段演示代码,这代码只供演示用,没有实际意义 。 复制代码 代码如下: //一个没有实际意义的socket连接对象 var socket = { connect: function(host, port) { alert(Connecting socket server,host: + host + ,port: + port); } }; //一个即时通讯类,其中connect方法还将作为AJAX回调函数被调用 function classIm() { this.host = 192.168.1.28; this.port = 8080; this.connect = function(data) { socket.connect(this.host, this.port); }; } //实例化即时通讯类 var IM = new classIm(); //AJAX请求,这里假设要打开socket连接首先要通过WEB得知用户WEB登录成功 $.get(CheckWebLogin.aspx, IM.connect); 运行上面的例子,你将看到弹出的host与port都是undefined,那是因为回调函数的this不是指向IM对象,而是jQuery的AJAX配置对象ajaxSettings 。在jQuery内部是用s.success代替传入的回调函数去执行的,而success的调用对象就是s,即下面ajaxSettings对象的缩写 。 ajaxSettings: { url: location.href, global: true, type: "GET", contentType: "application/x-www-form-urlencoded", processData: true, async: true } 为了证明这一点,你可以这样修改代码测试一下,你将看到是url、global、type、contentType等对象的属性名称: 复制代码 代码如下: this.connect = function(data) { for (var key in this) { alert(key); } } 现在了解了问题所在,接下来想办法解决这个问题 。其实我们的目的是希望AJAX回调函数代码socket.connect(this.host, this.port)中的this指向类classIm的实例对象IM,或者说是想socket.connect()方法能得到正确的参数值吧 。为了得到预期的AJAX回调函数执行结果,我分析了大致有下面几种方法: 方法一 直接传对象的正确引用而非this指针,或叫对象实传 。这是最常见的做法,即在类实例化时用一个变量存储对当前对象的引用,在后面的方法中直接使用此变量代替this的使用 。注意:这种方法并没有真正改变this的指向 。演示代码如下,注意对比前后两次代码的区别,我也特别高亮显示差异部分代码 。 复制代码 代码如下: var socket = { connect: function(host, port) { alert(Connecting socket server,host: + host + ,port: + port); } }; function classIm() { var self = this; this.host = 192.168.1.28; this.port = 8080; this.connect = function(data) { socket.connect(self.host, self.port); }; } var IM = new classIm(); $.get(CheckWebLogin.aspx, IM.connect); 方法二 使用apply加闭包实现真正改变this的指向 。下面方法把函数调用时的this对象存到一个临时变量_method,然后又利用闭包把它传给返回的function对象,在这个返回的function中使用apply把调用时对象的this替换为目标对象thisObj 。这种方法是很多JavaScript框架的做法,而且下面这个Function原型方法正是我从prototype框架精简而来 。注意我是先给Function原型加了Apply方法,这个Apply不是脚本内置的apply,是我自定义的,如果你喜欢可以定个别的名字 。 复制代码 代码如下: /** * 改变jQuery AJAX回调函数this指针指向 * @param {Object} thisObj 要替换当前this指针的对象 * @return {Function} function(data){} */ Function.prototype.Apply = function(thisObj) { var _method = this; return function(data) { return _method.apply(thisObj,[data]); }; } var socket = { connect: function(host, port) { alert(Connecting socket server,host: + host + ,port: + port); } }; function classIm() { this.host = 192.168.1.28; this.port = 8080; this.connect = function(data) { socket.connect(this.host, this.port); }; } var IM = new classIm(); $.get(CheckWebLogin.aspx, IM.connect.Apply(IM)); 方法三 在匿名回调函数中再调用实际的回调处理函数 。这种方法虽然可以解决同样的问题的,但是代码有点长和多余,实际开发中是不建议这样做的 。这种方法是保证了调用connect方法的对象还是IM对象,从而保证了this指向还是IM对象 。代码如下: 复制代码 代码如下: $.get(CheckWebLogin.aspx, function(data){IM.connect(data)}); |