全局作用域下同一变量为何可搭配window.a与let声明,却不能同时用var和let声明
核心原因:ES规范对全局作用域的词法环境做了分层设计
1. 全局作用域的两层环境记录
全局执行上下文的词法环境分为两个独立的存储区域,对应你提到的两种内存划分:
- 对象式环境记录:也就是所说的「全局内存」,绑定到浏览器宿主的全局对象
window,所有var声明、顶层函数声明都会存在这里,直接给window加属性也等价于在这层新增绑定。 - 声明式环境记录:也就是所说的「脚本内存」,是ES6新增的存储区域,专门用来存
let、const、class等块级声明的绑定,不会挂载到window对象上。
标识符查找时会优先匹配声明式环境记录的内容,匹配不到才会继续查找对象式环境记录的内容。
2. 第一段代码运行正常的原因
// At global scope window.a = 42; // Creates binding in the outer global environment let a = "answer"; // Creates binding in the inner global environment console.log(a); // Shows "answer"
window.a = 42只是给window对象新增了一个普通属性,仅在对象式环境记录里新增了a的绑定,不会在全局作用域的「已声明标识符校验池」里注册这个变量名。let a = "answer"是在声明式环境记录里新增绑定,两层存储区域相互独立,不存在命名冲突,所以运行正常。- 打印时优先取声明式环境记录的
a,因此输出answer,如果要获取42的取值可以手动访问window.a。
3. 第二段代码抛重复声明错误的原因
// At global scope var a = 42; let a = "answer";
var a = 42除了会在对象式环境记录里新增window.a的绑定,还会把a这个名字注册到全局作用域的「已声明标识符校验池」里,标记为已经被声明过的变量。let声明变量前会强制校验当前作用域的校验池,只要存在同名的已注册变量(不管是var声明的还是其他let/const声明的),就会直接抛出SyntaxError: Identifier 'a' has already been declared的语法错误,和变量实际存储在哪层环境记录没有关系。
内容的提问来源于stack exchange,提问作者srrsre
相关产品推荐
相关产品推荐

