《JavaScript 核心原理解析》学习笔记 Day 2 未声明变量 变量声明与词法声明
前置知识:变量提升、暂时性死区。此处略。
JavaScript 中有六种声明用语句:let、const、var、function、class、import。其声明处理方式如下:
变量声明(varDelcs):
1. var 变量声明。在创建变量名(varName in varDecls)后,会绑定一个 undefined 值作为初始化,因此 var 声明的标识符未赋值时也可访问;
2. function 函数声明。其名称声明规则同 var。
词法声明(lexicalDecls):
1. let 变量声明。在创建词法名字(lexicalNames)后,不会进行值绑定,因此未赋值时 let 或 const 声明的标识符存在暂时性死区,访问即会报错;
2. const 常量声明。声明后的常量不可修改,也存在暂时性死区;
3. class 类声明。类的内部为严格模式,其名称声明规则同 let;
4. import 导入声明。其名称声明规则同 const。
综上,所有的声明本质上只有三种处理模式:var 变量声明、let 变量声明和 const 常量声明。
而向未声明变量赋值时,JavaScript 会在全局范围内创建它,但其表现与 var 声明的变量不同。另外 eval() 中的var 声明效果也有所不同。其原因在于虽然它们都被创建为全局对象 global 的属性,但所有在静态语法分析期或在 eval() 中使用 var 声明的变量名都会被置于一个全局对象外维护的变量名列表(varNames 列表)中,在这个变量名列表中的变量是“直接声明的变量”,不能使用 delete 删除。所以 var 声明的变量不能用 delete 删除,但被赋值的未声明变量可用 delete 删除。然而在 eval() 中使用 var 声明的变量名又可以从此变量名列表中被移除,因此 eval() 中使用 var 声明的变量可以通过 delete 删除。具体区别见如下代码:
var a=100
b=200
eval('var c=300')
// a、b、c 均为全局对象 global 的属性,但只有非 eval() 中通过 var 声明的变量是不可配置的
Object.getOwnPropertyDescriptor(global, 'a'); // {value: 100, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(global, 'b'); // {value: 200, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(global, 'c') // {value: 300, writable: true, enumerable: true, configurable: true}
// a 是不可以通过 delete 删除,而 b、c 可被删除
delete a // false
delete b // true
delete b // true
a // 100
b // ReferenceError: b is not defined
c // ReferenceError: c is not defined
另外词法名字列表(lexicalNames 列表)中的项都是不可移除的,也就无法通过 delete 删除。
最后再来看 var x=y=100 这行代码,即 var x=(y=100)。右侧是表达式 y=100,其对未声明变量 y 进行了赋值,也就在全局创建了一个可用 delete 删除的变量 y。而表达式 y=100 的执行结果 100 又绑定给了 var 声明的不可通过 delete 删除的变量 x。 另外注意:声明语句中无赋值。赋值发生在两个表达式之间,而声明语句 var x 中的 x 是一个标识符,而非表达式。具体区别见如下代码:
// x 是 var 声明的变量,y 是未声明变量
var x = y = 100
// x 不可配置,y 可配置
Object.getOwnPropertyDescriptor(global, 'x') // {value: 100, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(global, 'y') // {value: 100, writable: true, enumerable: true, configurable: true}
// x 无法通过 delete 删除,而 y 可被 delete 删除
delete x // false
delete y // true
x // 100
y // y is not defined