js this指向问题以及改变this指向的三种方法(call、apply、bind)

js this指向问题以及改变this指向的三种方法(call/apply/bind)

JavaScript中,this指向是一个很令人头疼的一个东西,由于JavaScript可以运行在浏览器环境中,也可以运行在node环境中,两者的this指向有着不同的特性,this指向问题往往会令你的代码朝着意想不到的方向运行,因此JavaScript也提供了几种修改this指向的方法,我们一一说明。

浏览器环境中this的指向

下面先介绍在浏览器环境中this的指向问题;

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 全局模式下,this指向window
        console.log(this);

        // 全局变量是设置在window上的,因此下面的写法是一样的
        a = 10;                     // window.a = 10
        this.b = 10;
        console.log(a === b);       // true

        function fn1() {
            console.log(this);
        }
        fn1();              // this指向window
        window.fn1();       // this还是指向window

        var obj = {
            c: 10,
            fn2: function () {
                console.log(this);
            }
        }
        obj.fn2()           // this指向obj
        window.obj.fn2()    // this指向obj

        var obj2 = {
            d: 10,
            e: {
                f: 10,
                fn3: function () {
                    console.log(this);
                }
            }
        }
        window.obj2.e.fn3()     // this指向e

        let fn4 = window.obj2.e.fn3;
        fn4()                   // this指向window
    </script>
</body>
</html>

总结:

  • this在定义的时候不会指定,只有在函数调用的时候才会指定
  • this指向的是在调用函数时最近的调用者

node环境中this的指向

而在node环境中,this的指向和浏览器中有着较大出入;
在全局打印this,这里的this是一个空对象{}

console.log(this); // {}

在浏览器环境中,这里的结果是true,但是在node环境中,会报错,因为变量b是定义在this上的,而this指向的是一个空对象,而a === b中的变量b是根本没有定义的

a = 10;
this.b = 10;
console.log(a === b); // ReferenceError: b is not defined

有趣的是,在函数中,this指向的又不是空对象,而是global对象,指向函数fn1的调用者global

 function fn1() {
    console.log(this);
}
fn1(); // Object [global] {...}
var obj = {
    c: 10,
    fn2: function () {
        console.log(this);
    }
}
obj.fn2() // { c: 10, fn2: [Function: fn2] } => 指向obj
var obj2 = {
    d: 10,
    e: {
        f: 10,
        fn3: function () {
            console.log(this);
        }
    }
}
obj2.e.fn3() // { f: 10, fn3: [Function: fn3] } => 指向e

总结:

  • node环境中直接打印的this指向空对象,浏览器环境直接打印this指向window
  • this指向和浏览器环境中是一样的,只是区别在于,浏览器环境全局是window,而node环境全局是global

改变this指向的三种方法;

call

function fun1(a, b, c) {
    console.log(this);
    console.log(a + b + c);
}
obj = { name: 'jerry', age: 18 };

fun1() // 这里函数里的this指向window对象

fun1.call(obj, 1, 2, 3); // 这里函数中的this指向obj,而不是fun1

apply

applycall的区别是apply传递的参数需要使用数组,而call直接传就行了

 function fun2(a, b, c) {
    console.log(this);
    console.log(a + b + c);
}

fun2.apply(obj, [1, 2, 3])

bind

bind区别是bind只会改变this指向,并不会执行函数,而callapply不仅改变this指向,并且会执行函数

 function fun3(a, b, c) {
    console.log(this);
    console.log(a + b + c);
}

fun3.bind(obj, 1, 2, 3); // 并不会执行fun3

需要特别注意的是,bind方法并不会直接去更改函数的this指向,而是返回一个新的修改了this指向的函数,如果要调用,则需要使用其他变量接收或者直接调用

 function fun3(a, b, c) {
    console.log(this);
    console.log(a + b + c);
}

fun3.bind(obj, 1, 2, 3); // 并不会执行fun3

fun3() // 这里的this指向并没有发生改变

// 如果想要调用,用变量接收
let newFun3 = fun3.bind(obj, 1, 2, 3);
newFun3()

// 或者直接调用
fun3.bind(obj, 1, 2, 3)()

一个bind方法的小例子

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button>获取验证码</button>
    <script>
        // 注意这里,如果不加bind(this),那么定时器里面的this指向的是window
        // 这里加了bind(this),那么定时器里面的this就指向了btn事件源,从而可以使用this来操作btn
        let btn = document.querySelector('button');
        btn.onclick = function () {
            let time = 10;
            this.disabled = true;
            this.innerText = `重新获取(${time})`
            this.timer = setInterval(function () {
                time -= 1;
                this.innerText = `重新获取(${time})`
                if (time == 0) {
                    this.disabled = false;
                    this.innerText = `获取验证码`
                    clearInterval(this.timer)
                }
            }.bind(this), 1000)
        }
    </script>
</body>
</html>


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com