setTimeout里改变this指向
问题
setTimeout里改变this指向
由大模型辅助整理,保留原意,优化结构与可读性。
问题概述
提问者在 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」,与调用方式无关。 bind、call、apply:可以显式指定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。
小结
setTimeout 中 this 丢失的根本原因是「函数引用」脱离了原来的调用对象;只要保证回调内部依然以「对象.方法()」的形式调用(如用一层普通函数包裹),或使用 bind 显式绑定 this,就能避免这个问题。
由大模型辅助整理,保留原意,补充结构与代码格式。
回答转载自 SegmentFault