this的指向基本规则:谁调用了函数,函数内的this就指向谁
一、全局作用域下this的指向
在全局作用域下,this===window,并且self和frames也是window,另外,ES11中globalThis也是指向window
环境 | 如何获取全局对象 |
---|---|
web | window/self/frames/this |
node | global |
worker | self |
通用 | globalThis(在node v12+中,global === globalThis) |
二、全局函数中的this
1、非严格模式下,this === window
function test() {
console.log(this === window) // true
}
test()
2、严格模式下,函数直接调用,this指向undefined,通过window调用,this指向window
'use strict'
function test() {
return this
}
test() // undefined
window.test() // window
三、对象中的this指向最近的引用
1、对象方法中的this指向该对象
const obj = {
name: '小明',
getName: function () {
console.log(this.name) // 小明
}
}
obj.getName()
2、对象的方法中的闭包中的this默认指向window
function test() {
function eat() {
console.log(this) // window
}
eat()
}
const obj = {}
obj.test = test
obj.test()
但是当闭包作为对象的方法时,闭包中的this又指向该对象:
function test() {
const person = {
eat: function () {
console.log(this) // person对象
}
}
person.eat()
}
const obj = {}
obj.test = test
obj.test()
3、对象原型上的方法中的this指向该对象
const obj = Object.create({
getSum: function () {
console.log(this.a + this.b) // 3
}
})
obj.__proto__.a = 1
obj.__proto__.b = 2
obj.getSum()
四、构造函数中的this指向实例对象
如果说构造函数中的this指向实例对象,这还不够严谨,通过new操作符在构造函数中首先创建一个空对象,然后将this指向这个空对象,最后将this返回出去赋值给了外界的变量,这个变量就是实例对象,实例对象就是返回的this
function Test() {
this.name = '小明'
this.getName = function () {
console.log(this.name)
}
}
const t = new Test()
t.getName()
- 函数中不写return语句,默认return undefined;如果在函数执行前加上new操作符,那么这个函数中默认return this。
- 如果一个函数是用来当做构造函数使用,就会结合new操作符,默认return this,这句可以不写,系统会自动加上;如果返回的是一个简单数据类型,会被忽略,还是return this;如果返回复杂数据类型,那么new操作符接收到的就是该复杂类型数据。
- new Test()时,Test函数由window调用,但是new会在Test内做一些事情
- 创建一个空对象
- 将this指向这个空对象,逐行执行将构造函数中的属性和方法添加到这个空对象中,并且将构造函数的prototype赋值给对象的__proto__
- return this
五、ES6类中,this指向实例对象,但静态方法中的this指向当前类
六、事件处理函数中的this指向被绑定的DOM对象
<button>按钮</button>
<script>
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
console.log(this)
})
</script>
七、回调函数(除了事件的回调)中的this被重定向到window
- 数组的所有方法:forEach、map、sort、reduce、filter、some、every
- 定时器:setInterval、setTimeOut
- Promise:异步前和异步后都是指向window
const arr = [1, 2, 3]
arr.forEach((item) => {
console.log(this, item)
})
setTimeout(function () {
console.log(this)
}, 100)
八、call、apply、bind可以改变this的指向
- 这三个方法的优先级高于基本规则
- 要注意bind返回的函数可以当做构造函数使用,通过new操作符又会改变this的指向,此时this指向构造函数中新创建的空对象
九、ES6箭头函数中的this指向外部作用域的this
1、箭头函数不管基本规则,也不管call/apply/bind,一直指向外部作用域的this,优先级最高
var a = 10
var b = 20
const obj = {
a: 1,
b: 2,
getObjSum: function () {
console.log(this.a + this.b)
},
getSum: () => {
console.log(this.a + this.b) // this指向上级this,obj所在作用域的this指向window
}
}
obj.getObjSum() // 3
obj.getSum() // 30
2、箭头函数不适用的场景
- 箭头函数不能当做构造函数使用。new操作符干了啥第二步:new操作符会将this指向由window改为实例对象,但是箭头函数中的this永远指向它的上一级this。所以箭头函数在设计时就不允许通过new执行,硬要执行会抛错:
Uncaught TypeError: Person is not a constructor
- 原型方法的定义也不能使用箭头函数。当原型的方法被实例调用时,我们希望this是指向该实例的,但是箭头函数会使this指向上一级,这不符合我们的期望
- 当想要使用arguments时,也不能用箭头函数。但是你可以使用剩余运算符(...)获取实参
- 事件的回调中,如果要对DOM进行操作,这种场景也不要使用箭头函数
- 对象的方法中,如果要使用对象中的某个属性,这种场景也不要使用箭头函数
- 如果需要使用call/apply/bind改变this指向,方法定义时也不要使用箭头函数,因为箭头函数的优先级最大
- 当使用forEach、map等方法时,如果传了第二个参数,就不能使用箭头函数
- 总结:箭头函数中没有this,它的this指向上一级this,如果说你的函数中需要使用当前this,那么就不要使用箭头函数