zoukankan      html  css  js  c++  java
  • 使用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能有一定的帮助,如果有疑问大家可以留言交流。

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

  • 相关阅读:
    〖Linux〗Kubuntu设置打开应用时就只在打开时的工作区显示
    〖Linux〗Kubuntu, the application 'Google Chrome' has requested to open the wallet 'kdewallet'解决方法
    unity, dll is not allowed to be included or could not be found
    android check box 自定义图片
    unity, ios skin crash
    unity, Collider2D.bounds的一个坑
    unity, ContentSizeFitter立即生效
    类里的通用成员函数应声明为static
    unity, Gizmos.DrawMesh一个坑
    直线切割凹多边形
  • 原文地址:https://www.cnblogs.com/tingyu-blog/p/6212392.html
Copyright © 2011-2022 走看看