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

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

  • 相关阅读:
    Qt编写ffmpeg本地摄像头显示(16路本地摄像头占用3.2%CPU)
    Qt编写本地摄像头综合应用示例(qcamera/ffmpeg/v4l2等)
    Qt编写音频播放示例(带音频曲线/振幅/传输/录制等)
    Qt编写跨平台RTSP/RTMP/HTTP视频流播放器
    Qt编写4K/8K大分辨率播放器(8K占用1%CPU)
    Qt编写的视频播放综合应用示例(qmedia/ffmpeg/vlc/mpv/海康sdk等)
    Qt编写linux上视频流播放器(支持海康大华宇视等各种网络摄像机)
    Qt编写视频监控显示安卓版
    Qt音视频开发02海康sdk解码(支持句柄/回调/gpu模式/支持win/linux)
    Qt音视频开发01共享解码线程(耗时一年/性能凶残/至臻完美)
  • 原文地址:https://www.cnblogs.com/tingyu-blog/p/6212392.html
Copyright © 2020-2023  润新知