VarDeclaredNames与VarScopedDeclarations的用途差异及相关术语辨析
关于ECMAScript中
VarDeclaredNames/LexicallyDeclaredNames与对应ScopeDeclarations的区别解答 这个问题问得非常到位——很多人刚开始啃ECMAScript规范的时候都会疑惑这两组概念为啥要分开,毕竟从字面看好像重复了。其实核心原因是职责分离:一组专注于「收集名称」,另一组专注于「收集完整的声明实体」,它们服务于变量初始化流程里的不同阶段。
核心分工
VarDeclaredNames/LexicallyDeclaredNames:是轻量的名称字符串集合,只保留声明的变量名,用于快速做名称校验、冲突检查、作用域污染判断这类只需要「知道有哪些名字」的操作。VarScopeDeclarations/LexicallyScopeDeclarations:是完整的声明实体集合,包含声明的类型(var/let/const/function/class)、位置、初始化值等全部细节,用于实际执行变量绑定、初始化、提升逻辑这类需要完整声明信息的操作。
不同初始化场景下的具体作用
我们结合规范里的初始化流程来拆解:
1. 函数声明初始化
在函数执行前的初始化阶段,流程是分两步的:
- 第一步:先用
VarDeclaredNames和LexicallyDeclaredNames做名称冲突检查——比如如果同一个作用域里既用var声明了x,又用let声明了x,这一步直接就能抛出错误,根本不需要加载完整的声明内容。 - 第二步:才会用
VarScopeDeclarations和LexicallyScopeDeclarations去处理变量绑定:比如把var声明的变量挂载到函数的活动对象上(变量提升),把let/const声明的变量放进块级绑定环境(触发暂时性死区)。
2. 全局声明初始化
和函数初始化逻辑类似:
- 先通过
VarDeclaredNames和LexicallyDeclaredNames检查全局作用域的名称冲突——比如全局作用域里var x和let x不能共存,这一步只用名称就能快速判断。 - 之后再用
VarScopeDeclarations创建全局对象的属性(比如window.x),用LexicallyScopeDeclarations创建全局块级绑定(不会挂载到全局对象)。
3. 模块声明初始化
模块的绑定规则更严格,且模块里没有var声明的特殊提升逻辑,所以直接用VarScopeDeclarations和LexicallyScopeDeclarations处理绑定即可,不需要单独的名称集合——因为模块的声明检查和绑定可以一次性完成。
4. 块声明初始化
这里只需要处理块级绑定(let/const/function/class),逻辑相对简单:直接用LexicallyScopeDeclarations创建块级绑定环境,同时完成名称检查和绑定操作,不需要单独的名称集合。
5. Eval声明初始化
Eval的场景比较特殊:
- 首先需要用
VarDeclaredNames判断非严格模式下的var声明会不会污染外层作用域(比如非严格模式下eval里的var x会变成外层作用域的变量),这一步只用名称就能判断。 - 之后再用
VarScopeDeclarations和LexicallyScopeDeclarations去执行实际的绑定逻辑。
总结
简单来说,*DeclaredNames是「快速校验工具」,*ScopeDeclarations是「实际执行工具」——把这两个职责分开,能让规范的初始化流程更清晰,也能避免在只需要名称的阶段加载不必要的声明细节,提升执行效率。
内容的提问来源于stack exchange,提问作者MaximPro
相关产品推荐
相关产品推荐

