ECMAScript全局执行上下文疑问:回调复用及ScriptEvaluation步骤解析
关于setTimeout回调执行上下文的疑问
先看这段代码:
debugger setTimeout(() => { console.log('foo') }, 3000)
在Chrome DevTools中查看调用栈时,(anonymous)代表全局执行上下文:
- 代码首次触发debugger时,调用栈显示:
(anonymous) - 若单步执行,3秒后回调触发再次暂停,此时调用栈显示:
(anonymous) setTimeout(async) (anonymous) // 这是全新的全局执行上下文吗?
核心疑问1:回调的全局执行上下文是否和初始的一致?
假设浏览器为宿主环境,代码执行完毕后调用栈为空,那么浏览器是否必须重新创建全局执行上下文并将匿名函数加入调用栈?也就是说原全局执行上下文并未被保留。
ECMA规范相关描述
在未来某个时间点,当作业所属代理中无运行上下文且该代理的执行上下文栈为空时,实现必须:
- 执行任何宿主定义的准备步骤。
- 调用作业抽象闭包。
- 执行任何宿主定义的清理步骤,之后执行上下文栈必须为空。
在任意特定时间,当满足以下所有条件时,scriptOrModule(脚本记录、模块记录或null)为活动脚本或模块:
- GetActiveScriptOrModule() 返回scriptOrModule。
- 若scriptOrModule为脚本记录或模块记录,令ec为执行上下文栈中顶层的、其ScriptOrModule组件为scriptOrModule的执行上下文。ec的Realm组件为scriptOrModule.[[Realm]]。
当调用栈为空时,浏览器似乎会执行InitializeHostDefinedRealm步骤:
9.6 InitializeHostDefinedRealm ( )
抽象操作InitializeHostDefinedRealm无参数,返回包含unused的正常完成记录或抛出完成记录。调用时执行以下步骤:
- 令realm为CreateRealm()的返回值。
- 令newContext为新的执行上下文。
- 将newContext的Function设为null。
- 将newContext的Realm设为realm。
- 将newContext的ScriptOrModule设为null。
- 将newContext推入执行上下文栈;newContext成为当前运行的执行上下文。
- 若宿主要求使用外来对象作为realm的全局对象,令global为宿主定义方式创建的该对象。否则,令global为undefined,表示应创建普通对象作为全局对象。
- 若宿主要求realm全局作用域中的this绑定返回非全局对象的其他对象,令thisValue为宿主定义方式创建的该对象。否则,令thisValue为undefined,表示realm的全局this绑定应为全局对象。
- 执行SetRealmGlobalObject(realm, global, thisValue)。
- 令globalObj为? SetDefaultGlobalBindings(realm)的返回值。
- 在globalObj上创建任何宿主定义的全局对象属性。
- 返回unused。
目前的不确定点在于执行上下文的创建方式:假设当调用栈为空时,回调的全局执行上下文由抽象操作InitializeHostDefinedRealm( )创建;若调用栈非空,则内部调用ScriptEvaluation。
核心疑问2:ScriptEvaluation为何要挂起当前执行上下文?
查看ScriptEvaluation步骤的第9步,为何需要挂起当前运行的执行上下文?ScriptEvaluation()操作是否未创建全局执行上下文?
该抽象操作的核心似乎确实是创建全局执行上下文,但却包含挂起当前执行上下文的步骤。
ScriptEvaluation的步骤
Script Evaluation的步骤如下:
- 令globalEnv为scriptRecord.[[Realm]].[[GlobalEnv]]。
- 令scriptContext为新的ECMAScript代码执行上下文。
- 将scriptContext的Function设为null。
- 将scriptContext的Realm设为scriptRecord.[[Realm]]。
- 将scriptContext的ScriptOrModule设为scriptRecord。
- 将scriptContext的VariableEnvironment设为globalEnv。
- 将scriptContext的LexicalEnvironment设为globalEnv。
- 将scriptContext的PrivateEnvironment设为null。
- 挂起当前运行的执行上下文。
- 将scriptContext推入执行上下文栈;scriptContext成为当前运行的执行上下文。
- 令script为scriptRecord.[[ECMAScriptCode]]。
- 令result为Completion(GlobalDeclarationInstantiation(script, globalEnv))的返回值。
- 若result.[[Type]]为normal,则:
- 将result设为Completion(对script求值)的返回值。
- 若result.[[Type]]为normal且result.[[Value]]为空,则:
- 将result设为NormalCompletion(undefined)。
- 挂起scriptContext并将其从执行上下文栈中移除。
- 断言:执行上下文栈不为空。
- 恢复执行上下文栈顶层的上下文作为当前运行的执行上下文。
- 返回? result。
内容的提问来源于stack exchange,提问作者dbzx10299
相关产品推荐
相关产品推荐

