setTimeout里改变this指向

思否 问答 👍 3 含 AI 优化版

问题

setTimeout里改变this指向

在 SegmentFault 查看原问题 →

由大模型辅助整理,保留原意,优化结构与可读性。

问题概述

提问者在 setTimeout 中调用对象方法时遇到了 this 指向问题,想弄清楚 setTimeout(function(){ MyObj.showName() }, 1000)setTimeout('MyObj.showName', 1000) 这两种写法的差异,以及 MyObj.showName()MyObj.showName(不加括号)在语义上的本质区别。

涉及技术

JavaScript、setTimeout、this 绑定

期望结果

希望理解 setTimeout 中函数调用与函数引用的区别,进而明确如何在 setTimeout 回调中保持正确的 this 指向。

回答

原始回答

2020-09-13 setTimeout(function(){ MyObj.showName() }, 1000);

setTimeout('MyObj.showName', 1000);先要明确一点:MyObj.showName() 和 MyObj.showName 是不一样的。举个例子:a = MyObj.showName()

b = MyObj.showNamea 是调用了方法,然后存储了返回值b 是直接存储了方法然后在明确一下,this 的指向:默认情况指向调用对象()=> 箭头函数情况下,指向定义时的 thisbind、call、apply,指向传入的对象,但是无法改变箭头函数的那么回到原始的问题:MyObj.showName() 调用对象就是 MyObjb = MyObj.showName;b() 调用对象可以理解为 window

AI 优化 · 回答整理

解答思路

关键先要区分清楚 MyObj.showName()(调用方法,取的是返回值)和 MyObj.showName(取的是方法本身的引用,尚未调用)是两个完全不同的东西;再结合 this 的指向规则——默认由「调用者」决定——就能推出为什么直接把方法引用传给 setTimeout 会导致 this 丢失。

示例代码

var a = MyObj.showName();  // 调用了方法,a 存储的是方法的返回值
var b = MyObj.showName;    // 没有调用,b 存储的是这个函数本身

MyObj.showName();  // 调用对象是 MyObj,this 指向 MyObj
b();                // 调用对象变成了 window(非严格模式),this 不再指向 MyObj

详细说明

先明确 this 的几种指向规则:

  • 默认情况this 指向「调用该函数的对象」。
  • 箭头函数this 指向「定义时所在作用域的 this」,与调用方式无关。
  • bindcallapply:可以显式指定 this 指向传入的对象,但对箭头函数无效(箭头函数的 this 无法被这些方法改变)。

回到问题本身:

  • MyObj.showName() 是直接调用,调用对象就是 MyObj,因此方法内部的 this 指向 MyObj,一切正常。
  • 当写成 var b = MyObj.showName; b(); 时,b 只是拿到了函数本身的引用,之后单独调用 b(),此时的调用对象已经变成了全局对象(浏览器中即 window),不再是 MyObj,导致方法内部访问 this.xxx 时指向的不是原来期望的对象。
  • setTimeout(function(){ MyObj.showName() }, 1000) 之所以正常,是因为回调函数内部依然是通过 MyObj.showName() 这种「对象.方法()」的方式调用的,调用对象明确是 MyObj
  • 而类似 setTimeout('MyObj.showName', 1000) 这种字符串形式(本质上等价于把方法引用单独传出去后再调用,且通常不推荐使用字符串参数),一旦丢失了「对象.方法()」这种调用形式,this 就会指向调用时的默认对象(如 window),而不是 MyObj

小结

setTimeoutthis 丢失的根本原因是「函数引用」脱离了原来的调用对象;只要保证回调内部依然以「对象.方法()」的形式调用(如用一层普通函数包裹),或使用 bind 显式绑定 this,就能避免这个问题。

由大模型辅助整理,保留原意,补充结构与代码格式。

回答转载自 SegmentFault

评论

评论功能即将开放。你也可以通过 邮件GitHub Issue 反馈。