使用call、apply和bind解决js中烦人的this,事件绑定时的this和传参问题
1、什么是this
在JavaScript中this可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式,this 绑定的对象即函数执行的上下文环境(context)。
为了帮助理解,让我们来一起看一段代码:
// 作为对象方法调用
var test = {
a : 5,
b : 6,
sum : function () {
return this.a + this.b; // 此处this = test
}
}
alert(test.sum()); //
作为对象调用时this很容易理解,this等价于sum的调用者即上诉的test对象,如果作为函数调用时this=?
// 作为函数调用
a = 4;
b = 3;
function sum(){
return this.a + this.b; // 此处this = window
}
alert(sum()); // 7
此时函数sum是做为window对象的一个全局函数,因此sum的调用者为window,即this = window。
var test = {
a : 5,
b : 6,
sum : function (a,b) {
function getA(a) {
this.a = a; // 在window上增加了一个全局变量a
return this.a; // 此处this = window
}
function getB(b){
this.b = b; //在window上增加了一个全局变量b
return this.b; // 此处this = window
}
return getA(a) + getB(b);
}
}
alert(test.sum(4,3)); //
alert(a); // 4
alert(b); //
在这种情况下,我们希望getA() 和getB() 返回的值是test.a和test.b,但是此时闭包函数(即函数中的函数)getA和getB中this并不指向test的实例,该怎么办呢?我们不妨试试下面的方法:
var test = {
a : 5,
b : 6,
sum : function () {
var self = this; // 此处this = test的实例
function getA() {
return self.a;
}
function getB(){
return self.b;
}
return getA() + getB();
}
}
alert(test.sum());
alert(a); // 此处报错:a is not defined
alert(b); // 此处报错:a is not defined
在test对象的sum函数中用一个局部变量self来保存当前的this指针,这样在闭包函数getA和getB中就能通过self变量获取test实例的属性了。
看起来这样就能够解决闭包函数中this的问题了,但是,如果调用sum函数的并不是test的实例呢,这个时候var self=this还能起到作用,获取到test的实例吗?
2、使用call、apply和bind改变函数执行时的上下文(this)
使用call、apply和bind都能够是函数的上下文发生改变,那我们来具体看看这记者之间的区别吧。
call方法:
语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
apply方法:
语法:apply([thisObj[,argArray]])
定义:应用某一对象的一个方法,用另一个对象替换当前对象。
说明:如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。
如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。
bind方法:
语法:bind(thisArg[, arg1[, arg2[, ...]]])
定义:将接受多个参数的函数变换成接受一个单一参数。
说明:bind()方法所返回的函数的length(形参数量)等于原函数的形参数量减去传入bind()方法中的实参数量(第一个参数以后的所有参数),因为传入bind中的实参都会绑定到原函数的形参。
哎呀妈呀,讲了那么多理论的东西,我都晕了,还是看看实际的例子:
var test = {
a : 5,
b : 6,
sum : function (a,b) {
var self = this;
function getA() {
return self.a;
}
function getB(){
return self.b;
}
alert(a);
alert(b);
return getA() + getB();
}
}
var obj = {a:2,b:3};
alert(test.sum.call(obj,4,5)); // 调用时self = this = obj,alert顺序4,5,5
alert(test.sum.apply(obj,[6,7])); // 调用时self = this = obj,alert顺序6,7,5
var sum = test.sum.bind(obj,8); // 此处返回一个只有一个参数的函数sum(b)
alert(sum(9)); // 调用时self = this = obj,alert顺序8,9,5
从上面的例子我们可以很清晰的看到call、apply和bind之间的区别。其中call和apply是差不多的,只是传参的形势不同(apply的第二个参数为一个数组或arguments),他们都是直接直接执行函数;
而bind函数将test.sum简化为另一个全局函数sum(b),sum(b)只需要传入一个参数即可。
3、解决js中烦人的this
call、apply和bind都可以应用于继承,在这里不再过多赘述,网上有很多这样的例子,参考:http://blog.csdn.net/wyyfwm/article/details/46349071
而我想讲一下这段时间我遇到的一些关于this比较头疼的事情。
<button id="btn">烦人的this</button>
<script>
var test = {
isSum: true,
sum: function (event, a, b) {
if (this.isSum) { // this = button,这个时候不会执行alert(a+b)
alert(a + b);
}
}
}
var button = document.getElementById("btn");
button.addEventListener("click", test.sum, false);
</script>
这里我们就能发现问题所在了,当ID为btn的按钮被点击时会触发test.sum函数,但是这个时候的this=button,而且参数a、b如何传入呢?
这里就能够使用bind函数了,将test.sum函数简化为另一个新的函数,同时传入参数a和b,我们再看看下面的代码:
<button id="btn">this</button>
<script>
var test = {
isSum: true,
sum: function (a, b,event) {
if (this.isSum) { // 此处this=test,this.isSum = true
alert(a + b); // 9
}
}
}
var button = document.getElementById("btn");
button.addEventListener("click", test.sum.bind(test,4,5), false); // 此处test.sum.bind(test,4,5)返回一个新的函数function(event),
</script>
从上面的代码我们可以看到test.sum.bind(test,4,5)返回一个新的函数function(event),test、4、5分别被绑定到test.sum的上下文、参数a、参数b中。
当ID为btn的按钮被点击时会触发test.sum函数,此时改函数中的this=test,a=4,b=5。
这样就可以解决事件绑定时的this以及传参的问题了,包括现在常用js框架中的事件绑定,如jQuery、signals.min.js等等。
总结
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者使用Javascript能有一定的帮助,如果有疑问大家可以留言交流。
转载需注明转载字样,标注原作者和原博文地址。
使用call、apply和bind解决js中烦人的this,事件绑定时的this和传参问题的更多相关文章
- promise 的基本概念 和如何解决js中的异步编程问题 对 promis 的 then all ctch 的分析 和 await async 的理解
* promise承诺 * 解决js中异步编程的问题 * * 异步-同步 * 阻塞-无阻塞 * * 同步和异步的区别? 异步;同步 指的是被请求者 解析:被请求者(该事情的处理者)在处理完事情的时候的 ...
- js事件、Js中的for循环和事件的关系、this
一.js事件 1.事件 用户在网页中所触发的行为 鼠标滑动种类很多,键盘.表单特列: 点击:onclick 鼠标进入:onmouseenter 鼠标离开:onmouseleave 鼠标悬浮:onmo ...
- js浮点数精度丢失问题及如何解决js中浮点数计算不精准
js中进行数字计算时候,会出现精度误差的问题.先来看一个实例: console.log(0.1+0.2===0.3);//false console.log(0.1+0.1===0.2);//true ...
- 解决Js中的resize事件执行两次的方法
问题: 页面自适应的时候需要用到js的resize事件,但在执行过程中发现只要触发resize事件就会执行2次 原生js: window.onresize = function(){ console. ...
- 解决js中传值,Action获取是乱码问题
1.先在js中进行编码 var str = $("mytext").val(); //转码,两次 str = encodeURI(str); str = encodeURI(str ...
- 解决js中post提交数据并且跳转到指定页面的问题总结
今天在开发中过程中遇到了这个问题,js中利用JQuery中的 $.post("url", id, function(){}); 这个方法是数据提交正常,但是后台处理完成之后跳转无法 ...
- JS中的DOM操作和事件
[DOM树节点] DOM节点分为三大类: 元素节点. 属性节点. 文本节点: 文本节点.属性节点属于元素节点的子节点.操作时,均需要先取到元素节点,再操作子节点:可以使用getElement系列方法, ...
- js中一次性注册多个事件
在js中,如果想一次性给一个控件或者标签初测多个事件的方法: 假如有个<input>标签: <input id=”inputValue” value=”www.baidu.com”/ ...
- js中按下回车触发事件
方法一:document.onkeydown = function (e) { // 回车提交表单// 兼容FF和IE和Opera var theEvent = window.event || e; ...
随机推荐
- 我的Linux操作系统的发行版是什么?版本号是什么?
可以用下面的两个命令来查看: cat /etc/*-release hostnamectl 参考资料 ============== How To Find Out My Linux Distribut ...
- NYOJ127 星际之门(一)【定理】
星际之门(一) 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描写叙述 公元3000年,子虚帝国统领着N个星系,原先它们是靠近光束飞船来进行旅行的,近来,X博士发明了星际之门 ...
- mysql忘记密码时如何修改密码
1.首先关闭mysql服务 2.进入mysql安装目录,我的是在C:\Program Files\MySQL\MySQL Server 5.5\bin 3.dos命令行执行:mysqld -nt -- ...
- ConcurrentHashMap代码解析
ConcurrentHashMap (JDK 1.7)的继承关系如下: 1. ConcurrentHashMap是线程安全的hash map.ConcurrentHashMap的数据结构是一个Segm ...
- jquery核心
1.找到所有 p 元素,并且这些元素都必须是 div 元素的子元素 $("div > p"); 2.设置页面背景色 $(document.body).css("ba ...
- 【C++】C++中类的基本使用
1.类和成员声明,定义,初始化的基本规则 C++中类的基本模板如下: namespace 空间命名{//可以定义namespace,也可以不定义 class/struct 类名称{ public/pr ...
- 【C#】C#线程_I/O限制的异步操作
目录结构: contents structure [+] 为什么需要异步IO操作 C#的异步函数 async和await的使用 async和Task的区别 异步函数的状态机 异步函数如何转化为状态机 ...
- linux驱动面试题整理
1.字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件. 评:这只是其中一种方式,也 ...
- 关于Java 软件工程师应该知道或掌握的技术栈
鄙人星云,今天突然想写这么一篇需要持续更新的文章,主要目的用于总结当前最流行的技术和工具,方便自己也方便他人. 更新时间:2018-10-23 09:26:19 码农职业路径图 码农入门职业路径图 J ...
- 自定义命令杀死 java 进程 alias kjava
alias kjava='ps -ef|grep ProcessName |awk "{print $2}"|xargs kill -9' 上面脚本放在杀JAVA进程中,会出现一些 ...