uncategorized

JavaScript中的this指向问题

对于JavaScript中的this指向问题,一直是新手理解中的一个难点和易混淆的点

本文就我个人经验,对辨别this指向问题提出一些实操性、流程性的判断依据

真言①:谁调用,this指向谁

逻辑说明

看几个实例

1
2
3
4
5
6
7
8
9
10
const obj = {
sayHi: function () {
console.log(`hi: ${this.name}`)
}
}
// case 1
obj.sayHi() // obj发起的调用,this 指向 obj
// case 2
let sayHi = obj.sayHi
sayHi() // 在 window 环境下发起的调用,this 指向 window

上述案例还可更进一步

1
2
3
4
5
6
7
8
9
const obj = {
sayHi: function () {
function anotherFn() {
console.log(this)
}
anotherFn()
}
}
obj.sayHi()

这里打印出的this指向全局,严格模式下为 unedfined

因为 console.log 发生的位置在 anotherFnobj.sayHi 调用的上下文确实是 obj 对象,但anotherFn其实是没有调用上下文的,根据规范,非严格模式下,未指定上下文的,上下文为全局对象

一个小证明

这里再补一个谁调用指向谁的小证明:

1
2
3
4
5
6
function checkThis() {
'use strict'
console.log(this)
}
checkThis() // undefined
window.checkThis() // Window Object

这个小证明基于一个关于严格模式的事实:严格模式下,this 不发生隐式回退,也就是不指定调用上下文的,就为undefined

上面的这个函数,内部声明了启用严格模式

如果直接调用,最终查找到的其实也是window上的checkThis函数,但因为没有指定调用方,所以 this === undefined

如果带上window这个上下文调用,this === WindowObject

真言②:箭头函数this指向定义箭头函数的词法作用域

箭头函数没有this绑定,而是从函数定义时的词法作用域中查找this(静态作用域绑定OR词法作用域绑定)的,其查找过程与普通变量在作用域链上的查找过程类似

某行代码的词法作用域也可以不标准但通俗地理解为:这行代码在编写的时候所在的作用域;与之相对的动态作用域则是:不管你程序员代码如何摆放的,作用域都得等执行时根据上下文或是否调用绑定等因素确定,并且显著的一个特征是:在一连串的函数调用中,调用链的某个节点可以访问之前节点上的本地变量

无论是把函数放到别的对象中调用,还是直接暴力bind上下文,箭头函数都不会屈服

以下是将上例改为箭头函数

1
2
3
4
5
6
7
8
9
10
const obj = {
sayHi: () => {
console.log(`hi: ${this.name}`)
}
}
// case 1
obj.sayHi() // sayHi 定义时上下文是 window,this 指向 window, 不因调用方改变
// case 2
let sayHi = obj.sayHi
sayHi() // sayHi 定义时上下文是 window,this 指向 window

真言③:构造函数是个例外

最初的例子再改个调用方式

1
2
3
4
5
6
7
const obj = {
sayHi: function() {
console.log(`hi: ${this.name}`)
}
}

new obj.sayHi() // 这里的 this 指向 new 出来的实例

构造函数会创建新的对象作为执行构造函数时的上下文
如果构造函数没有返回新对象,这个创建的对象就是new后的实例

附言①:显式指定 this

有很多显而易见的方法可以指定 thisArg

如:

  1. bind 函数
  2. call 函数
  3. apply 函数

还有一些可能不是很常用的,但也可以绑定 thisArg 如:

  1. map
  2. forEach
  3. every
  4. filter
  5. some

附言②:元素绑定的事件回调

元素绑定的事件回调中,this 指向绑定事件的元素本身

附言③:顶级作用域的this ?

对于Browser来说,顶级作用域里的this(也就是最外层代码的this) 指向的是 WindowObject

而对于Nodejs,顶级作用域中的this指向的是module.exports对象

即:

1
2
3
4
5
// browser 环境
this === window // true

// nodejs 环境
this === module.exports // true

自测①

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const myObj = {
foo: "bar",
print: function () {
self = this
console.log(this.foo)
console.log(self.foo)
;; (function () {
console.log(this.foo)
console.log(self.foo)
})()
}
}

myObj.print()
print = myObj.print
print()

第一个 myObj.print() 的输出为 bar bar undefined bar

第二个 pring() 的输出为 undefined undefined undefined undefined

Share