JavaScript学习总结之函数的this指向

前言

继上篇匿名函数的this指向问题后,续写和总结JavaScript中函数的this指向问题,下文将介绍有关函数this绑定的四条规则。

调用位置

关于函数的this,常常有句话,叫做谁调用就指向谁。简单来说this的指向跟函数的调用位置紧密相关,要想知道函数调用时this到底引用了什么,就应该明确函数的调用位置。一般来说需要通过函数的调用栈来判断来分析出函数真正的调用位置,具体怎么分析呢?除了目测代码(就是盯着看-。-)外,还也可以借用浏览器的开发者工具(debug工具),去推断目标函数到底是在哪里调用的,这样才能更准确的知晓this的指向。比如下面这段代码:

    function foo() {
      console.log('foo')
    }

    function bar() {
      console.log('bar')
      foo()
    }

    bar()

要想知道foo函数是由谁调用的,就可以在浏览器中打开调试工具,在foo函数中的第一行打一个断点,找到函数的调用栈,然后再找到栈中的第二个元素,这就是真正的调用位置。如下图所示:
JavaScript学习总结之函数的this指向_第1张图片
从浏览器的调试工具可以找到foo函数的真正调用位置。

默认绑定

默认绑定规则一般用于函数独立调用时。以下面的代码为例:

    var a = 2
    function foo() {
      var a = 3
      console.log(this.a)
    }

    foo() // 2

输出结果为2。因为foo函数调用时处于全局环境下(这里是window),查看一下浏览器中的调用栈:

JavaScript学习总结之函数的this指向_第2张图片

调用栈中只有foo函数一个元素,说明调用者就是当前的全局环境window,所以这里的this指向的就是window,因为最外部的a一开始是最为window.a 声明并赋值的,所以可以理解为this = window; this.a = 2。比较特殊的一点就是,如果在foo函数内部采用了严格模式,那么this就会绑定到undefined:

    var a = 2
    function foo() {
      'use strict'
      var a = 3
      console.log(this.a)
    }

    foo() //`//Cannot read property 'a' of undefined`

隐式绑定

举如下代码为例:

    var a = 2
    function foo() {
      console.log(this.a)
    }

    var obj1 = {
      a:3,
      foo: foo
    }

    obj.foo() //3

输出结果为3,说明这里的this指向的是obj1,为什么不再是指向全局环境了呢。在这里就要考虑到调用位置是否存在上下文对象,或者说是否被某个对象拥有或包含。在上述的代码中,foo函数的引用被赋给了obj1的foo属性obj1.foo = foo, 并且在foo函数被调用时,它的前面也加上了对obj1的引用。此时,当函数引用有上下文对象时,隐式绑定规则就会将函数中的this绑定到这个上下文对象,这里的上下文对象就是obj1。
其实在理解上下文对象时,个人觉得不用那么抽象,它无非就是一个不确定的代名词,简单来说你觉得它是什么,那它就是什么(-。-)。

显式绑定

默认绑定和隐式绑定在我看来是js的一个内置且被动的绑定方式,就是已经这么帮你设定好了,只要符合这两个规则且没有其他规则存在那么this的指向就按照这两个规则来。显然,这类被动的绑定方式并不符合实际的代码编写需要,比如我要指定一个函数的this,该怎么办呢?这时候就需要显式绑定了。call、apply会在显式绑定时发挥作用。参考如下代码:

    function foo() {
      console.log(this.a)
    }

    var obj1 = {
      a: 2
    }

    var a = 3

    foo.call(obj1) //2

输出结果为2。原因是因为call改变了foo函数运行的this指向,将原本this指向的window全局转为了指向obj1,所以输出的是2,从这里也可以看出,显示绑定的优先级大于默认绑定。

new绑定

首先应该明确一点,JavaScript中的new与其他面向类的语言不同,在js中new后面的只不过是一个普通的函数,仅仅是被new操作符调用了而已。使用new调用函数时,会执行如下步骤:

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行[[Prototype]]连接。
  3. 这个新对象会绑定到函数调用的this。
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

代码如下所示:

    function foo(a) {
      this.a = a
    }

    var bar = new foo(2)

    console.log(bar.a) //2

输出结果为2。

不适用的情况

ES6中出现了一种特殊的函数:箭头函数。以上的四种规则在箭头函数中都不适用,箭头函数的是根据外层函数或者全局链决定this的。其实这也是对以往ES6之前的较为复杂的this绑定规则的优化和统一,在实际编码的过程中更容易让人理解,当然箭头函数也有缺点,这里就不再展开。

总结

在写这篇总结文章之前,一直对js中的this问题理解不深,翻了几遍《你不知道的js》才算真正有所学习和领悟。本文写的并不具体,就this绑定时的绑定丢失问题并没有展开叙述,绑定的规则优先级也没有写全,暂时先留个坑,等忙完这阵再来补充-。- !

你可能感兴趣的:(javascript,chrome)