Javascript中改变函数内部this指向的方法:call()、apply()、bind()

在整理Javascript中改变函数内部this指向的方法之前,先介绍一下不同函数的调用方式以及函数内this的指向。

一、函数的调用方式

  1. 普通函数
  2. 对象的方法
  3. 构造函数
  4. 绑定事件函数
  5. 定时器函数
  6. 立即执行函数
<script>
        // 函数的调用方式

        // 1. 普通函数
        function fn() {
            console.log('人生的巅峰');
        }
        // 调用函数
        // fn(); fn.call();

        // 2. 对象的方法
        var o = {
            sayHi: function() {
                cconsole.log('人生的巅峰');
            }
        }

        // 调用函数
        o.sayHi();

        // 3. 构造函数
        function Star() {};
        // 调用函数
        new Star();

        // 4. 绑定事件函数
        btn.onclick = function() {}; // 点击了按钮就可以调用这个函数

        // 5. 定时器函数
        setInterval(function() {}, 2000); // 这个函数是定时器自动2秒钟调用一次

        // 6. 立即执行函数
        //第一种写法
        (function() {
            console.log('人生的巅峰');
        })();

        //第二种写法
        (function() {
            cconsole.log('人生的巅峰');
        }());

        // 立即执行函数是自动调用
    </script>

二、函数内this的指向

  • this指向,是当我们调用函数的时候确定的,调用方式的不同决定了this的指向不同,一般我们指向我们的调用者。

三、改变函数内部this指向

  • JavaScript 为我们专门提供了一些函数方法来帮我们处理函数内部 this 的指向问题,常用的有 bind()call()apply()三种方法。

3.1、call()方法

call()方法可以立即调用一个函数,并可以改变该函数的this指向

使用方法: fun.call(thisArg,arg1,arg2,…)

  • thisArg: 在 fun 函数运行时指定的 this 值
  • arg1,arg2,…: 其他参数
  • 返回值: 就是fun函数的返回值,因为它就是调用函数

因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承。
 

   <script>
        // 改变函数内this指向 js提供了三种方法  call() apply() bind()

        // 1. call()
        var o = {
            name: 'andy'
        }

        function fn(a, b) {
            console.log(this);
            console.log(a + b);
        }
        fn.call(o, 1, 2);
        // call 第一个可以调用函数 第二个可以改变函数内的this 指向
        // call 的主要作用可以实现继承
        function Father(uname, age, sex) {
            this.uname = uname;
            this.age = age;
            this.sex = sex;
        }

        function Son(uname, age, sex) {
            Father.call(this, uname, age, sex);
        }

        var son = new Son('刘德华', 18, '男');
        console.log(son);
    </script>

Son构造函数想继承Father构造函数的属性,在Son构造函数中,调用Father构造函数,同时改变this指向,让其指向Son构造函数。这个时候用到了call函数。

实现继承的过程: 创建一个Son的实例化对象son,这时把‘xiaoming’,18分别传给function Son(uname, age)中的uname,age,此时形参uname=‘xiaoming’; age=18; 然后function Son(uname, age)中有一个函数Father.call(this, uname, age),这里的uname和age就是形参,所以他们此时也有了对应的值,然后第一个参数改为this,Son构造函数里面的this应该指向它的实例化对象,也就是这里的son。带着这三个参数去调用Father函数,也就是son的uname属性赋值为形参uname的值‘xiaoming’,son的属性赋值为形参age的值18。实现了继承。

3.2、apply()方法

apply()方法可以立即调用一个函数,并可以改变该函数的this指向

使用方法: fun.apply(thisArg,[argsArray])

  • thisArg: 在 fun 函数运行时指定的 this 值
  • [argsArray]: 传递的值,数组或伪数组形式
  • 返回值: 就是fun函数的返回值,因为它就是调用函数

因为它传入的参数是一个数组,所以我们可以用它处理一些和数组有关的问题,比如借用Math.max()求数组的最大值/最小值。

    <script>
        // 改变函数内this指向 js提供了三种方法 call() apply() bind()

        // 2. apply() 应用 运用的意思
        var o = {
            name: 'andy'
        };

        function fn(arr) {
            console.log(this);
            console.log(arr); // 打印出来的不是数组,而是字符串pink
        }
        fn.apply(o, ['pink']);
        // 1. 第一个也是调用函数 第二个可以改变函数内部的this指向
        // 2. 但是它的参数必须是数组(伪数组)
        // 3. apply 的主要应用  比如说我们可以利用 apply 借助于数学内置对象求最大值
        // Math.max();
        var arr = [1, 66, 3, 99, 4];
        // var max = Math.max.apply(null, arr);
        var max = Math.max.apply(Math, arr);
        var min = Math.min.apply(Math, arr);
        console.log(max, min);
        // 如果传递的数组里面是字符串型的,打印出来的就是字符串型的;如果传递的数组里面是数字型的,打印出来的就是数字型的
    </script>

Math.max是一个方法(函数),对其使用apply方法,这里不需要改变this指向,所以第一个参数让它指回Math,第2个参数传入要处理的数组即可。这样就可以借用Math方法处理数组了。

apply方法会自动将传入的数组处理为对应的数据类型。

3.3、bind()方法

bind函数不会直接调用函数,但是可以改变函数内部的this指向,应用非常广泛。

使用方法: fun.bind(thisArg,arg1,arg2,…)

  • thisArg: 在 fun 函数运行时指定的 this 值
  • arg1,arg2,…: 其他参数
  • 返回值: 由指定的 this值和初始化参数改造的原函数拷贝,也就是说返回值是一个函数

因此当我们只是想改变 this 指向,并不想立即调用这个函数的时候,可以使用bind,比如改变定时器的this指向

比如有一个需求,点击某个按钮后,按钮被禁用,2秒后恢复正常。这里定时器里面的this指向的是window,而我们想让它指向当前被点击的button,因此需要修改this指向,但是我们又不希望它立即调用,而是等到满足定时器的条件后才执行,所以要用到bind函数

这里有一个很巧妙的点,所以function () { this.disabled = false; }.bind(this) 中this已经在定时器的外面了,所以他不再是指向window,而定时器外面的环境是按钮点击事件,也就是说this指向的是当前点击的btn,而通过bind函数,巧妙的把定时器函数的this指向改成了当前点击的btn,就可以实现当前功能。

<body>
    <button>点击</button>
    <button>点击</button>
    <button>点击</button>
    <script>
        // 3. bind()
        var o = {
            name: 'andy'
        };
        function fn(a, b) {
            console.log(this);
            console.log(a + b);
        };
        var f = fn.bind(o, 1, 2);
        f();  // 3
        // 1. 不会调用原来的函数 可以改变原来函数内部的this 指向
        // 2. 返回的是原函数改变this之后产生的新函数
        // 3. 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向 此时用bind
        // 4. 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
        // var btn1 = document.querySelector('button');
        // btn1.onclick = function() {
        //     this.disabled = true; // 这个this 指向的是 btn 这个按钮
        //     // var that = this;   // 方法1
        //     setTimeout(function() {
        		    // 定时器函数里面的this 指向的是window
        //         // that.disabled = false;    // 方法1
        		   // 方法2:用bind()改变this指向,bind在setTimeout函数的外面
        //         this.disabled = false; // 此时定时器函数里面的this 指向的是btn
        //     }.bind(this), 3000); // 这个this 指向的是btn 这个对象
        // }
        var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                this.disabled = true;
                setTimeout(function() {
                    this.disabled = false;
                }.bind(this), 3000);
            }
        }
    </script>
</body>

3.4、call()、apply()、bind() 总结

相同点:

  • 都可以改变函数内部的 this指向

不同点:

  • call apply  会立即调用函数,返回值就是函数执行后的返回值,并且改变函数内部的this指向
  • call apply 传递的参数不一样,call 传递参数,apply 必须数组形式
  • bind 不会立即调用函数,返回值是一个函数,改造后的原函数拷贝,可以改变函数内部this指向
  • bind 是返回对应函数,便于稍后调用;apply call 则是立即调用
  • call bind 传递的参数为新的this指向和原函数的其他参数,apply 传递的参数为新的this指向和一个数组

主要应用场景:

  • call 经常做继承
  • apply 经常跟数组有关系,比如借助于数学对线实现数组最大值与最小值
  • bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向

猜你喜欢

转载自blog.csdn.net/DIUDIUjiang/article/details/126500855