jQuery AJAX回调函数this指向问题


Posted in Javascript onFebruary 08, 2010

如在全局作用域调用一个含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)});
Javascript 相关文章推荐
css值转换成数值请抛弃parseInt
Oct 24 Javascript
js控制的遮罩层实例介绍
May 29 Javascript
2014年最火的Node.JS后端框架推荐
Oct 27 Javascript
js实现Select下拉框具有输入功能的方法
Feb 06 Javascript
JS限制文本框只能输入数字和字母方法
Feb 28 Javascript
javascript数组随机排序实例分析
Jul 22 Javascript
JavaScript的Vue.js库入门学习教程
May 23 Javascript
Angular弹出模态框的两种方式
Oct 19 Javascript
Vuex入门到上手教程
Jun 20 Javascript
基于vue-router 多级路由redirect 重定向的问题
Sep 03 Javascript
vue-router 路由传参用法实例分析
Mar 06 Javascript
Vue.js中使用Vuex实现组件数据共享案例
Jul 31 Javascript
JavaScript Event学习第九章 鼠标事件
Feb 08 #Javascript
JavaScript 类似flash效果的立体图片浏览器
Feb 08 #Javascript
js 省地市级联选择
Feb 07 #Javascript
js 自定义的联动下拉框
Feb 07 #Javascript
比较搞笑的js陷阱题
Feb 07 #Javascript
javascript 鼠标拖动图标技术
Feb 07 #Javascript
数组Array进行原型prototype扩展后带来的for in遍历问题
Feb 07 #Javascript
You might like
php 静态变量的初始化
2009/11/15 PHP
ThinkPHP使用心得分享-上传类UploadFile的使用
2014/05/15 PHP
PHP防止表单重复提交的几种常用方法汇总
2014/08/19 PHP
YII2框架中使用yii.js实现的post请求
2017/04/09 PHP
PHP与Web页面交互操作实例分析
2020/06/02 PHP
javascript或asp实现的判断身份证号码是否正确两种验证方法
2009/11/26 Javascript
js字符串截取函数slice、substring和substr的比较
2016/05/17 Javascript
js将json格式的对象拼接成复杂的url参数方法
2016/05/25 Javascript
关于Vue.js一些问题和思考学习笔记(2)
2016/12/02 Javascript
使用jQuery和ajax代替iframe的方法(详解)
2017/04/12 jQuery
js实现删除li标签一行内容
2019/04/16 Javascript
利用Vue实现一个markdown编辑器实例代码
2019/05/19 Javascript
解决vue字符串换行问题(绝对管用)
2020/08/06 Javascript
[02:16]DOTA2英雄基础教程 干扰者
2014/01/15 DOTA
python登录QQ邮箱发信的实现代码
2013/02/10 Python
Python sys.path详细介绍
2013/10/17 Python
Python ORM框架SQLAlchemy学习笔记之映射类使用实例和Session会话介绍
2014/06/10 Python
Python获取电脑硬件信息及状态的实现方法
2014/08/29 Python
python实现批量获取指定文件夹下的所有文件的厂商信息
2014/09/28 Python
从源码解析Python的Flask框架中request对象的用法
2016/06/02 Python
浅析Python函数式编程
2018/10/06 Python
使用python的pexpect模块,实现远程免密登录的示例
2019/02/14 Python
Mac安装python3的方法步骤
2019/08/09 Python
pymysql模块的使用(增删改查)详解
2019/09/09 Python
python-OpenCV 实现将数组转换成灰度图和彩图
2020/01/09 Python
CSS3 Media Queries详细介绍和使用实例
2014/05/08 HTML / CSS
CSS3,线性渐变(linear-gradient)的使用总结
2017/01/09 HTML / CSS
opencv实现图像平移效果
2021/03/24 Python
生产部厂长助理职位说明书
2014/03/03 职场文书
高中毕业生登记表自我鉴定范文
2014/03/18 职场文书
抵押贷款承诺书
2014/05/30 职场文书
全国文明单位申报材料
2014/05/31 职场文书
森林病虫害防治方案
2014/06/02 职场文书
Nginx反向代理配置的全过程记录
2021/06/22 Servers
MySQL中LAG()函数和LEAD()函数的使用
2022/08/14 MySQL
MySQL 原理优化之Group By的优化技巧
2022/08/14 MySQL