JavaScript知识点回顾(十四)——this(二)

178 阅读2分钟

这是我参与8月更文挑战的第十四天,活动详情查看:8月更文挑战

绑定规则

我们来看看在函数的执行过程中调用位置如何决定this的绑定对象。我们需要先找到调用位置,然后判断需要应用四条规则中的哪一条。首先让我们来看看这四条规则

默认绑定

首先要介绍的是最常用的函数调用类型:独立函数调用。可以把这条规则看成兜底的默认规则。

    function foo(){
        console.log(this.a);
    }
    var a = 2;
    foo();       //打印2

声明在全局作用域中的变量就是全局对象的一个同名属性。(比如上面的a = 2)当我们调用foo()时,this.a被解析成了全局变量a。为什么会这样呢?因为函数调用时,应用了this的默认绑定,因此this是指向全局对象的。

隐式绑定

另一条需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:1,
        foo:foo
    };
    obj.foo();  //打印2

当foo()被调用时,它的前面加上了对obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因为调用foo()时this被绑定到obj,因此this.a和obj.a是一样的。注意:对象属性引用链中只有最后一层在调用位置中起作用。

隐式丢失

一个非常常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定的规则。

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a:2,
        foo:foo
    };
    var bar = obj.foo;
    var a = 3;
    bar();    //打印3

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()实际上是一个不带任何修饰的函数调用,因此应用了默认绑定。 一种更常见并且出人意料的情况发生在传入回调函数时:

    function foo(){
        console.log(this.a);
    }
    function doFoo(fn){
        fn();   //fn()的调用位置;
    }
    var obj = {
        a:2,
        foo:foo
    };
    var a = 3;      //全局变量3
    doFoo(obj.foo); //打印3

参数传递实际上是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样。回调函数丢失this绑定是非常常见的。除此之外,还有一种情况this的行为会出乎我们意料:调用回调函数的函数可能会强制的修改this。