结论
这个代码看着非常简单但是却可以通过这么短短的代码理解很多知识,首先看看这行代码的执行结果:
var x = y = 100;
console.log(x); // 100
console.log(y); // 100
Object.getOwnPropertyDescriptor(window, 'x');
//{value: 100, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(window, 'y');
//{value: 100, writable: true, enumerable: true, configurable: true}
正如大家所熟知的x,y相当于都是给全局变量声明了一个属性,但是两个属性的属性配置却截然不同([[Configurable]]:设置属性是否可以被删除,属性是否可更改),为什么会这样?
JavaScript 虽然被称为是“动态语言”,但确实是拥有静态语义的。
静态语义想了解更多,请点击这里
所以var x= y=100 中 y=100 是个赋值语句是个动态语句,在动态执行的时候动态的声明了一个全局属性Y,动态声明的是可以删除的,而 var x 是个变量声明,而 ECMAScript 规定所有在静态语法分析期 中使用var声明的变量名就被放在以个列表中。然后约定,这个变量名列表中的变量是“直接声明的变量”,不能使用delete删除。
声明和语句的区别在于发生的时间点不同,声明发生在编译期,语句发生在运行期。声明发生在编译期,由编译器为所声明的变量存放在在相应的变量表增加一个名字。语句是要在运行时执行的程序代码。因此,如果声明不带初始化,那么可以完全由编译器完成,不会产生运行时执行的代码。
延伸
JavaScript 只有变量和常量两种标识符,六条声明语句中:
- let x 声明变量 x。不可在赋值之前读。
- const x …
声明常量 x。不可写。 - var x …
声明变量 x。在赋值之前可读取到 undefined 值。 - function x …
声明变量 x。该变量指向一个函数。 - class x …
声明变量 x。该变量指向一个类(该类的作用域内部是处理严格模式的)。 - import …
导入标识符并作为常量(可以有多种声明标识符的模式和方法)。
除了这六个语句之外,还有两个语句有潜在的声明标识符的能力,不过它们并不是严格意义上的声明语句(声明只是它们的语法效果)。这两个语句是指:
for (var|let|const x …) …
for 语句有多种语法来声明一个或多个标识符,用作循环变量。
try … catch (x) …
catch 子句可以声明一个或多个标识符,用作异常对象变量。
由于标识符是在用户代码执行之前就已经由静态分析得到,并且创建在环境中,在 ECMAScript 6 之后出现的let/const变量在“声明(和创建)一个标识符”这件事上,与var并没有什么不同,只是 JavaScript 拒绝访问还没有绑定值的let/const标识符而已。