JavaScript this 指向怎么理解
在前端开发的圈子里,你大概率听过有人吐槽“this 是 JS 里最让人头疼的东西”——哪怕是写了两年代码的老开发者,也可能在某个回调函数里突然卡壳,搞不清 this 到底指向了谁。其实这玩意儿根本不是什么玄学,只要结合生活里的“指代习惯”看,就能把逻辑掰得明明白白。
第一步:先搞懂“this”到底是啥
简单说,JS 里的 this 就是个“指代代词”,和生活里的“我”“你”“他”没区别——你说“我要吃饭”,这个“我”指的是说话的人;JS 里的 this,指的就是“当前语境里的主角”,语境变了,指代的人自然就变了。

举个生活化的例子:你买了一杯奶茶,下单时说“我要少糖加冰”,这里的“我”就是你;但如果是你帮同事代下单,同事说“我要少糖加冰”,这时候的“我”就变成了同事,不是你。JS 里的 this 也是这个道理,它不固定指向某个对象,完全看“谁在调用这段代码”。
第二步:最常见的 4 种 this 指向场景
平时写代码时,90% 的 this 指向都逃不过这 4 种情况,记下来基本就能覆盖日常需求。
第一种:全局调用时,this 指向全局对象。在浏览器里,全局对象就是 window;在 Node.js 里就是 global。比如你直接在控制台写 console.log(this),输出的就是 window 对象——就像你站在大街上(全局语境),说“我要走路”,这里的“我”就是你自己(全局对象)。
第二种:对象方法调用时,this 指向所属的对象。比如你有个小猫对象:const cat = { name: '橘猫', eat: function() { console.log(this.name + '在吃鱼') } },当你调用 cat.eat() 时,这里的 this 就指向了 cat 对象,所以输出的是“橘猫在吃鱼”。就像小猫自己说“我要吃鱼”,“我”就是小猫本身。
第三种:构造函数里的 this,指向新创建的实例。JS 里用 new 关键字创建对象时,会自动把 this 绑定到新对象上。比如写个构造函数:function Dog(name) { this.name = name; this.bark = function() { console.log(this.name + '在汪汪叫') } },然后 const myDog = new Dog('二哈'),调用 myDog.bark() 时,this 就指向 myDog 这个实例,输出“二哈在汪汪叫”——就像你用“小狗模板”做了个二哈玩偶,玩偶自己说“我要叫”,“我”就是这个二哈玩偶。
第四种:显式绑定时,this 被强制指向指定对象。如果用 call() apply() bind() 这些方法,能手动把 this 改成你想要的对象。还是用刚才的小猫例子,cat.eat.call({ name: '英短' }),这时候 this 就被强制改成了那个新对象,输出的就是“英短在吃鱼”——相当于你帮英短下单点了一份鱼,英短说“我要吃鱼”,这里的“我”就是英短,不是原本的小猫。
容易踩的坑:this 会“丢失”
很多人出错,都是因为 this 被悄悄“换了人”。比如把对象方法赋值给变量,再调用:const eatFunc = cat.eat; eatFunc(),这时候 this 就指向了全局对象,输出的是“undefined 在吃鱼”——因为 eatFunc() 是全局调用,相当于大街上有个函数在说“我要吃鱼”,但没人告诉它“我”是谁,所以 name 就成了 undefined。
解决这个坑的小技巧,就是用箭头函数。箭头函数的 this 是“继承外层的”,不会被轻易改变。比如改成 const eatFunc = () => cat.eat();,或者用 bind() 固定 this,就能避免丢失问题。
其实 JS 的 this 设计初衷,就是为了让代码更灵活——它不是让人摸不着头脑的“陷阱”,而是跟着“当前调用的主角”走的指代逻辑。只要记住“谁调用,this 就指向谁”这个核心,再结合刚才的场景例子,遇到 this 问题时,先找“谁在执行这段代码”,就能轻松理清指向了。