this绑定由V8在栈帧ThisBinding字段直接写入,遵循new、显式(call/apply)、隐式(对象方法)和默认(独立函数)四大规则。箭头函数无此字段,沿词法链向上查找。setTimeout中obj.fn因脱离调用对象触发默认绑定,导致this指向全局(非严格模式)或undefined(严格模式)。
this是函数调用时V8在栈帧ThisBinding字段直接写入的确定值,非变量或属性;四大绑定规则决定该字段初始化值,箭头函数无ThisBinding字段,setTimeout中obj.fn因脱离调用对象而触发默认绑定。

想要真正掌握 this 绑定的底层原理,仅靠记忆“谁调用谁是 this”这类口诀远远不够。需要回到 V8 执行上下文栈的物理构建过程来理解——this 既不是变量,也不是属性,而是每个函数被调用时,引擎在压入新栈帧(stack frame)的那一瞬间,**直接写入当前执行上下文对象中 ThisBinding 字段的一个确定值**。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
this 的物理容器V8 在每次调用函数时,都会在调用栈顶部创建一个全新的执行上下文对象。该对象在内存中是一个结构化实体,包含三个核心字段:LexicalEnvironment、VariableEnvironment 和 ThisBinding。其中 ThisBinding 是独立字段,不参与作用域链查找,也不受 var/let 影响——它仅由调用方式决定,并在上下文创建阶段完成赋值。
这意味着,this 的值并非运行时“查出来”的,而是在调用发生时被“塞进去”的。栈帧一旦形成,ThisBinding 就已固定,后续所有代码访问 this,本质上就是读取该栈帧内存块中的那个字段。
可以将函数调用想象成创建了一个小型的“运行环境容器”,而容器内专门有一个夹层用来存放 this。区别在于,这个夹层里的内容不是通过变量声明存入的,而是取决于你如何“推开这扇门”来决定的。
V8 在创建函数执行上下文前,会解析整个调用表达式,按照优先级执行一套硬编码逻辑,最终将计算结果直接写入 ThisBinding:
new Foo(),V8 创建新对象,将其地址直接填入新上下文的 ThisBinding 字段。相当于 V8 说:“新房子造好了,钥匙就放在 this 这个抽屉里。”fn.call(obj),V8 取第一个参数 obj(或 undefined),原样写入 ThisBinding。就像你拿着一张写着目标地址的纸条,直接塞进抽屉。obj.method() 中的 obj),将其引用写入 ThisBinding;中间链(如 a.b.c.method())完全不参与判断。引擎只看最后一个点前面的那个对象,其他的中间环节一概不理。undefined,非严格模式写入 globalThis(浏览器中即 window)。这是最后的保险——没找到任何绑定线索,就按默认规则来。ThisBinding 字段箭头函数在 V8 编译阶段就被标记为“词法 this”,它根本不会创建自己的执行上下文 ThisBinding 字段。运行时访问 this,引擎直接沿词法环境链向上查找,复用外层**普通函数**执行上下文里的 ThisBinding 值。
这才是箭头函数 this 不动的真正原因——不是它被“保护”了,而是它压根就没有这个字段可改。因此 call、apply、bind 或 new 对它统统无效,因为箭头函数在编译时就决定不吃这套规则。
setTimeout(obj.fn, 100) 会丢失 this?这是一个经典问题,几乎所有开发者都遇到过。因为 obj.fn 被取值后变成纯函数引用,传入 setTimeout 时已脱离 obj。当定时器触发回调时,V8 执行的是一个孤立函数调用,调用表达式里没有点号、没有 new、也没有显式绑定参数——只能走默认绑定。
此时创建的新执行上下文,其 ThisBinding 字段被设为 undefined(严格模式)或 window(非严格),与原始 obj 再无关联。这根本不是“丢失”,而是上下文重建时,物理上就没有写入那个对象。可以说,是调用方式的改变,让 V8 在物理层面上无法把 obj 这个“地址”写入新栈帧的 ThisBinding 字段。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述