关于 JS 函数中的 this

93 阅读3分钟

在面对 JS 面试题的时候,this 往往会成为困扰我们解题的绊脚石。比如下面的代码:

var object = {
    fn: function(){
        console.log('hi');
    }
}

var a = object.fn;
object.fn(); //打印出的 this 是 object
a(); //打印出的 this 是 window

那该如何分辨出不同的函数执行的 this 呢?我们可以先从函数调用入手。

函数调用

对于 JS 中的函数调用,作为新手我们比较熟悉的就是以下两种形式:

funciton(a, b);
object.child.function(a, b);

但是正如一开始的面试题所呈现的那样,这两种形式的函数调用都没办法很直观的看出 this 的值。但以 ES5 为例,还有第三种我们不太熟悉的函数调用形式:

function.call(context, a, b);

当然,除了 call 语法,还有 apply 语法,我们先不讨论 apply

其实,前两种函数调用形式只是相当于 JS 提供的语法糖,它们可以随时等价地转换成 call 的形式:

funciton(a, b) <===> funciton.call(undefined, a, b) 
object.child.function(a, b)  <===> object.child.function.call(object.child, a, b)

那么通过转换成 call 形式的函数调用后,我们就可以清晰的看出 this 是什么:

this 就是 function.call(context, a, b) 中的 context !!!

所以其实每一次我们在调用函数的时候,其实都会传入 this ,即 context 。只不过大多数时候 JS 通过语法糖的形式帮我们简化了写法。所以当我们需要弄清楚 this 是什么的时候,我们只需要把函数改写成 call 形式,那么 this 就会显而易见了。

但是还是会有奇怪的事情发生。

比如我们看下面的代码:

function fn(){
    console.log(this);
}
fn();

我们把它转换成 call 形式为:

function fn(){
    console.log(this);
}
fn.call(undefined);//可简化成 fn.call()

那么按照我们上面的理论,此时浏览器应该会直接打出 undefined ,因为 this 应该是 undefined 。但是浏览器自身会有一条规则:

如果传进去的 context 为 null 或者 undefined ,那么浏览器会将 window 对象作为默认的 context,除非是严格模式下,context 才会保持 undefined

那么上面的函数打印出来的结果应该是 window。

那么对于 object.child.fn(a, b) 的 this

var object = {
    fn: function(){
        console.log(this)
    }
}
object.fn()

我们可以把它转换成下面的形式:

var object = {
    fn: function(){
        console.log(this)
    }
}
object.fn.call(object) //this 就是 object

而对于 [] 形式的函数调用:

function fn(){
    console.log(this)
}

var array = [fn1, fn2];
array[0]();
可以转换成
array.0.call(array)//虽然形式不正确,但是意思是这个意思

那么就可以看到 this 是 array 了。

箭头函数

有意思的是,当我们讨论箭头函数 ()=>{}时,会发现箭头函数是没有 this 的,如果一些代码的箭头函数中有 this,那么它肯定是这个箭头函数外面的 this。

总结

  1. this 就是以 call 形式的函数调用时所传入的 context,即第一个参数。
  2. 如果函数调用不是 call 形式,那么可以将其转换成 call 形式的函数调用,这样有助于我们辨别 this。

以上总结参考了方应杭老师的《this 的值到底是什么?一次说清楚》

©本总结教程版权归作者所有,转载需注明出处