前言

大家都知道这三个方法可以改变this指向,但是如果经常不用很容易忘记又有点模糊,今天又复习了一遍做一个整理。当然我也会顺便说一说每一种方法的运用场景,毕竟了解一个知识点就应该知道这个知识点的作用。

call方法

详解

call的第一个参数是一个对象,函数中的this指向这个对象,后面的参数就是传递到函数中的各种各样的参数。fn函数定义在全局中,我们通过call(o,1,2)让函数调用时候的this变成了o,执行函数结果为3.控制台打印的结果如下图所示。

const o = {
    name: 'linglong'
}
function fn(a,b) {
    console.log(this);
    console.log(a + b);
};
fn.call(o,1,2);
复制代码

运用

call可以实现函数的继承,具体如何实现呢,我们来看代码。

function father(name, age) {
    console.log(this);
    this.name = name;
    this.age = age;
} 
function son (name, age) {
    father.call(this, name, age);
}
var son1 = new son();
son1('linglong', 20);
console.log(son1);
复制代码

输出的结果我们可以看到两个son,第个是在调用father函数的时候father函数里面打印了this,this就指向son;第二个是在执行完后答应son1的结果。在子类中调用父类的方法并利用call改变父类中的this指向实现了继承。

apply

详解

aplly与call类似,第一个参数也是this,后面的参数就是传到函数的实参,但是必须是一个数组(伪数组)类型。

var o = {
    name: 'linglong',
}
function fn(arr) {
    console.log(this);
    console.log(arr);
}
fn.apply(o, ['ningjing']);
复制代码

上面的例子里传递了一个字符串数组,然后this变成o对象,并且将ningjing输出,也就是拿到了这个字符串。注意输出得到不再是一个数组而是一个字符串

运用

apply改变this的指向与call不一样的是传递的参数是一个数组,然后输出的时候是一个字符串,利用这个特点我们可以数组的最大值最小值。

const arr = [121, 112, 33, 5454];
var max = Math.max.apply(null,arr)//apply方法将arr传递给Math.max函数
var min = Math.min.apply(window,  arr);
console.log(max, min);
复制代码

apply方法将arr传递给Math.max方法,arr传递过去后不再是数组而是一串数字。

但是第一个参数null不太好,最好指向函数的调用者,Math调用了max函数,让null改成Math。

bind方法

bind我之前写过一篇文章,这里就直接引用了。

特性

bind可以改变this的指向,但是他不会去执行这个函数,什么意思呢?我们来看代码

  • 例1
var futher = function (name){
    this.name = name;
},
function son (){
    console.log(this);
}
son.bind(futher)
复制代码

在这个例子里面并不会让son函数执行,这是bind的特性之一。但是son里面的this指向了futher,这是bind的特性之二。

  • 例二
       var o = {
           name: 'andy'
       };

       function fn(a, b) {
           console.log(this);
           console.log(a + b);
       };
       var f = fn.bind(o, 1, 2);
       f();
复制代码

bind会有一个返回值,这个返回值是改变this后返回的新函数。这个例子里面bind首先将fn函数里面的this指向o对象。然后参数1,2传递给了fn函数里面的a和b。然后执行f函数,this打印结果是o对象,a+b结果是3。

小tip:

bind的第三个特性是返回一个新函数,这与第1个特性不执行函数不矛盾,上面的例子我们用f接收了这个新函数再执行才有了结果哦。

bind特性的总结

我们简单总结一下,bind有3个特点。

  • 第一函数不执行。
  • 第二是bind里面的第一个参数就代表了this。
  • 第三个是会返回一个改变了this的新函数。

bind案例功能

我们有一组按钮,点击某个按钮后为了防止多次点击使按钮禁用。三秒后再开启按钮。

案例代码

 // 4. 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
        // var btn1 = document.querySelector('button');
        // btn1.onclick = function() {
        //     this.disabled = true; // 这个this 指向的是 btn 这个按钮
        
        //     setTimeout(function() {
        //         btn1.disabled = false; // 此时定时器函数里面的this 指向的是btn
        //     }, 3000)
        // }
复制代码

在这个例子里,我们通过选择器获取到了按钮,在点击事件发生后执行的函数里面,先通过disabled让button禁用。然后设计一个定时器,我们知道在定时器里面的this指向的是window,所以this.disabled = false会报错滴,于是乎改成了btn1.disabled。但是这样写不够优雅,假如我们的按钮变量名修改了那么计时器里面的操作也要随着更改。那么如何优化呢? 我们可以在定时器外面var that= this;定时器里面函数that.disabled= false.

如何让定时器函数里面的this指向按钮而不是btn呢,我们可以利用call,apply,bind改变this。

利用bind优化案例代码

我们知道改变this前面说了三种关键字,那么用哪种更好呢,毫无疑问bind比较合理。因为bind不仅改变了this而且不会立即调用函数。而是通过定时器去触发这个函数。代码如下

       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);//这个this指向的是btn对象
            }
        }
复制代码

定时器函数里面的this指向的是window,而bind不在定时器函数里面,在btn.onclick函数里面,所以bind(this)中的this是btn对象。当某个btn对象被点击的时候,让disabled生效,三秒后执行定时器函数,bind的特性是函数不会立即执行,所以让定时器函数内部的this变成btn后三秒后再执行,dispaled失效解除禁用。

三种方法的总结

  • 三种方法都可改变this指向
  • call改变this指向,将参数单个传递给函数后立即调用函数。
  • apply改变this指向,将参数数组传递给函数后立即调用函数。
  • bind改变内部的this指向但不会立即调用函数
  • call经常用于继承
  • apply和数组有关比如求数组的最大值最小值
  • bind不会调用函数但是可以改变this指向,比如改变定时器函数的this。

Ending...