使用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());     // 11

作为对象调用时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));  // 7
alert(a);              // 4
alert(b);              // 3

在这种情况下,我们希望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能有一定的帮助,如果有疑问大家可以留言交流。

转载需注明转载字样,标注原作者和原博文地址。

时间: 2024-10-26 09:33:54

使用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 鼠标悬浮:onmouseover  鼠标移除:onmouseout 鼠标按下:onmousedown 鼠标抬起:onmouseup 鼠标移动:onmousemove 表单聚焦:onfocus 表单失去焦点:onblur 浏览器加载完成:onload js事件是一直存在的,可以绑定方法,也可以不绑定,如果没有绑定,事件

关闭myeclipse中烦人的鼠标划过,自动提示功能

eclipse越来越智能,身为码农的我却越来越伤心.虽然你很智能,但请你提供一些有用的信息给我,不要乱七八槽的,不问青红皂白就塞一大堆提示给我,对不起,哥不需要这些!!! 都知道,使用myeclipse开发java,jsp,js时,鼠标划过某个类,对象,方法……会自动提示一些没有用的信息(至少对我来说),好烦人啊,终于到了忍无可忍的时候,决定将这没用的提示干掉,还我一个清静世界. 我的是myeclipse10.1,请先打开window->preferences 执行一下3步 1.java->e

js浮点数精度丢失问题及如何解决js中浮点数计算不精准

js中进行数字计算时候,会出现精度误差的问题.先来看一个实例: console.log(0.1+0.2===0.3);//false console.log(0.1+0.1===0.2);//true 上面第一个的输出会超出我们的常识,正常应该为true,这里为什么会是false呢,直接运行会发现0.1+0.2在js中计算的结果是: console.log(0.1+0.2);//输出0.30000000000000004 这对于浮点数的四则运算(加减乘除),几乎所有的编程语言都会出现上面类似的精

解决js中post提交数据并且跳转到指定页面的问题总结

今天在开发中过程中遇到了这个问题,js中利用JQuery中的 $.post("url", id, function(){}); 这个方法是数据提交正常,但是后台处理完成之后跳转无法成功.经过分析,后台只是将要跳转的页面的html发回了前台,也就是说前台与后台利用JQuery的post方法只能发生数据的交互,并不能实现页面的跳转.所以,这就导致无法正常跳转. 解决方案: 1.将我们要提交的数据加入到指定的form表单中,在form表单中设置需要跳转的url和方法类型 post或get等

JS中的DOM操作和事件

[DOM树节点] DOM节点分为三大类: 元素节点. 属性节点. 文本节点: 文本节点.属性节点属于元素节点的子节点.操作时,均需要先取到元素节点,再操作子节点: 可以使用getElement系列方法,取到元素节点. [查看元素节点] 1.getElementById:通过id取到唯一节点.如果ID重名,只能取到第一个. getElementsByName(): 通过name属性   getElementsByTagName(): 通过标签名   getElementsByClassName()

去除下载文件属性中烦人的锁定状态

我们用浏览器下载文件的时候,往往会附加一个锁定的状态,执行些被锁定的文时,会出现一个安全警告框. 要去除这个安全警告,必须在右键的属性选项中点击"解除锁定"的按钮: 虽然这个是为了安全考虑,但是很多时候还是觉得非常烦人的.之前在网上找了个注册表可以去掉这个自动锁定的功能,但最近重装系统后,发现这个烦人的特性又回来了.便再次搜索了一下,发现园子里有篇文章介绍得比较详细:Windows沙拉:为什么下载的文件打开时会有警告,而且会被"锁定"? 该文章也介绍了两种解决的方法

JS——变量和函数的预解析、匿名函数、函数传参、return

JS解析过程分为两个阶段:编译阶段.执行阶段.在编译阶段会将函数function的声明和定义都提前,而将变量var的声明提前,并将var定义的变量赋值为undefined. 匿名函数: window.onload = function () { var oBtn01 = document.getElementById('btn01'); var oBtn02 = document.getElementById('btn02'); //注意这里不能有括号,skin01()的话就立刻执行了哦,所以不

使用jmeter中_javaScript函数和__substring函数嵌套截取字符串变量做传参

一.说明: 一哥们的业务场景是这样的,对一款商品进行下单后,会生成一个订单号,进行支付时,只需要订单号的后8位随机码,这个时候就要对传入的动态订单号进行字符串截取,这样才能支付成功,假如只测          试一次可以手动获取,但需要执行多次就需要动态获取. 二.实现步骤 1.首先就是需要沟通,彻底明白业务场景,这样才有利于接口测试工作展开,不然云里雾里的乱整没什么实际意义. 2.根据提供的脚本和梳理的业务场景,并结合目前遇到的瓶颈:A接口生成一个订单号[每次下单都是动态订单号],B接口进行支