JS 的 this 指向问题是老生常谈的难点了。我当初从 Java 转过来时极其不适应,花了好长时间才摆脱这个阴影。网上也有很多关于 this 的文章了,本文就简单说说,再聊点不一样的。

this 主要是在函数中使用,在函数外使用的话,一律指向全局对象
console.log(this)    // window

var o = {
  global: this       // window
}

在函数内使用 this 时,具体指向是由函数的调用方式决定,而不是根据函数定义方式决定

1. 函数作为普通函数运行时,可以看做是当做全局对象的方法运行,此时 this 指向全局对象
function hello() {
  console.log(this)
}
hello()    // window 作为普通函数运行
2. 函数作为对象的方法调用时,函数内的 this 指向该对象

所以上面一条,普通函数可以看做是全局对象的方法,所以 this 指向全局对象

var name = 'global'

var obj = {
  name: 'local',
  getName: function () {
    console.log(this.name)
  },
  outer: function () {
    function inner() {
      console.log(this.name) 
    }
    inner()      // 这是第 12 行代码
  }
}

obj.getName()    // local 作为 obj 的方法调用,此时 this 指向 obj
obj.outer()      // 打印什么?控制台试试吧

这里提到了一个大家容易忽视的点: 嵌套函数
还记得上面标题说的吗? this 的指向是由函数的调用方式来决定的

由于大多数嵌套函数是直接被调用的, 比如:第 12 行代码,调用 inner()
这时 inner 是被当做普通函数调用的,也可以看做是 window.inner() ,所以此时 inner 内部的 this 指向 window,打印 global

常见的解决方案如下:

var obj = {
  name: 'local',
  outer: function () {
    var that = this
    function inner() {
      console.log(that.name) 
    }
    inner()
  }
}

// 声明变量 that ,在 inner 内部用 that 代替 this
obj.outer()    // local

还有个问题

var name = 'global'
var obj = {
  name: 'local',
  getName: function () {
    console.log(this.name)
  }
}

obj.getName()    // local
var getName = obj.getName
getName()     // ?

直接调用 getName() 会打印 global
还是前面说的,虽然 obj.getName() 在声明的时候是作为 obj 的方法
但是把它赋值给 getName, 再调用 getName() 和 obj.getName() 的调用方式已经不同了

3. apply call bind 改变 this 指向

这个没啥还说的,强制改变 this 的指向,bind 的优先级最高。

var name = 'global'
var obj = {
  name: 'local',
  getName: function () {
    console.log(this.name)
  }
}

var obj2 = {
  name: 'xiaoming'
}

var obj3 = {
  name: 'laowang'
}

obj.getName()     // local
obj.getName.call(obj2)    // xiaoming

var getName = obj.getName.bind(obj3)    // 将 getName 内部的 this 绑定到 obj3
getName()     // laowang 不再是 window

getName.call(obj2)    // laowang
                      // bind 不会受到 apply 和 call 影响
4. 定时器设置的函数,this 会指向全局
var name = 'global'
var obj = {
  name: 'local',
  getName: function () {
    console.log(this.name)
  }
}

var obj3 = {
  name: 'laowang'
}

setTimeout(obj.getName, 1000)            // global
setTimeout(obj.getName.bind(obj3), 2000) // laowang

bind 强制绑定优先级最高,不受定时器影响
正确调用方式如下:
外面包一层匿名函数

setTimeout(function () {
  obj.getName()
}, 1000)            // local
5. ES6 箭头函数

ES6 推出了箭头函数,详细教程可以参考阮一峰老师的教程
箭头函数没有自己的 this 和 arguments, 因此在箭头函数内部使用 this 和 argments, 其实使用的是外层的 this 和 arguments

var name = 'global'
var obj = {
  name: 'local',
  getName: () => console.log(this.name),
  outer: function () {
    var inner = () => console.log(this.name)
    inner()
  }
}
obj.getName()    // global 由于箭头函数没有自己的 this,所以 getName 内部的 this 其实是函数外部的 this,指向全局
obj.outer()      // local

作为函数的方法,最好不要用箭头函数,因为箭头函数内部的 this 不再指向该对象。
所以箭头函数虽然好用,但是不要滥用哦

箭头函数还常用于数组的 forEach/map/some/every/filter/reduce 等循环方法中,比如

var arr = [1, 2, 3].map(item => item * 2)

june
842 声望24 粉丝

飞书内推:[链接]