归纳为以下情况
- 全局下单纯的只有函数,内部无论嵌套多少层,this都会链式指向window(例子略)
- 被对象作为属性的函数,此时通过obj.property来调用某个函数时,this指向该对象
function a() {
console.log(this)
console.log(arguments)
}
const obj = {
a,
b() {
console.log(this) // 这是 b : function(){}的简写
}
}
obj.a() // this为obj
a(); // this是window
- 使用new创建的实例,new会创建一个空对象,让this指向空对象,再把原型链指向创建好,返回this,所以调用new + 方法后,返回的this被赋值给接收的实例,此时实例就是函数内的this对象(例子略)
- 箭头函数this为上级作用域的this
// 箭头函数没有This Binding,其this就是上一层作用域的this
(() => {
console.log(this)
})()
// 打印window
const obj_ = {
fun() {
// 箭头函数没有this指向,this将在上一级作用域寻找
(() => {
// 打印obj_
console.log(this)
})();
// 使用function则有具体的指向,非严格模式指向window
(function() {console.log(this)})()
}
}
obj_.fun()
// 打印obj_
- 使用特定的方法比如Function.prototype.apply/call/bind等方法可以改变this的指向(例子略)
- 直接把某个方法(不管是不是在对象内的方法)作为全局window的一些宏任务的回调函数,此时this就指向window\
// 直接作为定时器的回调函数,this指向window
// 因为此时obj.b这个方法直接作为定时器的异步的回调在其他线程,1秒后放在了宏任务队列里,此后event loop轮询到
// 才会把其压入调用栈,作为当前的执行上下文,此时,其只充当定时器的回调函数,而不是对象内的某个属性,在上下文中,this指向全局对象
setTimeout(obj.b, 1000);
// 不直接,实际上仍然是在回调函数内部调用了obj.b(),因此this指向obj
setTimeout(() => {
obj.b()
}, 2000);
// 定时器, 直接把某个函数,不管是不是对象内的属性,都会指向window
setInterval(obj.b, 3000)
- 把某个方法作为某个元素的事件监听的回调函数,那么此时回调函数的this指向这个元素
// 基于某个元素的事件监听, 只要把某个函数作为回调, this就会该元素, 如果监听window就指向window
document.querySelector('h1').addEventListener('click', obj.b)
document.body.addEventListener('click', obj.b)
window.addEventListener('click', obj.b)
本质:
方法本质是一个引用类型的变量或者说是一个对象,永远储存在堆里,而this的指向(环境/执行上下文)取决于我们怎么使用这个方法,当前方法在哪个对象下调用,在哪个环境内
当我们在对象内设置一个属性,值为方法的时候,那么这个属性就存放着这个function的堆数据的地址
而我们直接通过对象 .property / ['property'] 来调用的时候,此时方法被obj这个对象作为属性直接调用,所属的环境就是该对象
而作为元素事件监听和一些宏任务比如定时器的回调函数时,对应的任务会在对应的线程运行,判断满足触发条件后,排入Callback queue,被Event loop事件轮询,此后压入call stack调用栈,函数回调,此时this指向被监听的元素或全局对象window,此时函数的身份是:元素对象的事件监听器的触发函数或在全局的定时器的回调函数,无论此时方法是不是对象的属性(仅存放函数的地址)
如有差错且您有时间,请帮忙指正,不胜感谢