箭头函数里的 this 到底指向谁?

  关于箭头函数里的 this 这个问题一直很模糊,之前看了很多教程虽然知道了其行为,但是具体实现规则一直不太清晰。今天为了彻底搞懂这个问题,查了很多资料,感觉查到的很多例子不是很能清晰的描述这种行为。所以今天特地自己写一篇文章来说明一下。

普通函数里的 this

  先来说一下普通函数里的 this。

1
2
3
4
5
var a = 1
function test() {
console.log(this.a) // this指向window,打印1
}
test()

  在普通函数里,this 的值在定义时是不明确的,要等到调用时才能确定,谁调用就指向谁。上面代码里的 test() 是在全局作用域调用的,所以自然就指向 window,控制台会打印 1。

  再来看一个例子:

1
2
3
4
5
6
7
8
var a = 1
var obj = {
a: 2,
test: function() {
console.log(this.a)
}
}
obj.test() // 2

  上面的代码我们把 test 函数定义在对象 obj 里,调用的时候是用 obj 来调用的,所以 test 函数里的 this 自然就指向 obj,最后函数会打印 2。

  再来看一个:

1
2
3
4
5
6
7
8
9
var a = 1
var obj = {
a: 2,
test: function () {
console.log(this.a)
}
}
var w = obj.test
w() // 1

  上面的代码我们把 obj.test 函数里的代码赋值给了一个全局变量 w,然后用全局变量 w 调用了 test 函数,所以这里的 this 指向的也是 window。

  还有一种情况:

1
2
3
4
5
6
7
8
9
10
11
12
var a = 1
var obj = {
a: 2,
test: function () {
function fn() {
console.log(this.a)
}
return fn()
}
}

obj.test() // 1

  这里来看 obj.test 方法,这个方法里我们创建了一个 fn 函数,最后将 fn 函数的结果作为 obj.test 的结果返回,这里的 fn 是用 return 方法返回的,并没有被其它对象直接调用,普通函数里 this 也并不会被继承,所以这里的 this 指向全局的 window。

  setTimeout 方法和 setInterval 方法中的 this 均指向 window,如下:

1
2
3
4
5
6
7
8
9
10
11
var a = 1
var obj = {
a: 2,
test: function () {
setTimeout(function () {
console.log(this.a)
}, 1000)
}
}

obj.test() // 1

  上面的几种情况就是普通函数里 this 的指向。

  使用 call()、apply()、bind() 等方法可以手动改变普通函数里 this 的指向,在本文里就不详细叙述了。

箭头函数里的 this

先说结论:

  • 箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this(结论出自 MDN - 箭头函数)
  • 使用 call()、apply()、bind() 等方法并不会直接修改箭头函数内的 this
  • 箭头函数的 this 是在创建时就已经确定了
1
2
3
4
5
6
7
8
9
var a = 1
var obj = {
a: 2,
test: () => {
console.log(this.a)
}
}

obj.test() // 1

  这里的 this 是在箭头函数里的,所以会从自己的作用域链的上一层,也就是 obj 对象来继承 this。最后一行 obj 是在全局中调用的,所以 obj 的 this 指向 window,其结果打印的是 1。

1
2
3
4
5
6
7
8
9
10
11
function a() {
function b() {
console.log(this === window) // true
var c = () => {
console.log(this) // window
}
return c()
}
return b()
}
a()

  上面的这个例子,c 是箭头函数,所以 c 里的 this 继承自己的作用域链的上一层 b,b 是直接 return 返回的结果,this 指向 window。

  我们再把上面的例子修改一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
a: 'xxx'
}

function a() {
function b() {
console.log(this === obj) // true
var c = () => {
console.log(this) // obj对象
}
return c()
}
b.call(obj)
}
a()

  上面的例子我们手动用 call() 方法把 b 的 this 指向更改为了 obj,通过打印出来的结果再一次印证了箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this 这一结论。