ECMAScript和JavaScript的关系?
1996年11月,JavaScript的创造者—-Netscape公司,决定将JavaScript提交给国际标准化组织ECMA,希望这种语言能成为国际标准。次年,ECMA发布262号文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言成为ECMAScript,这个版本就是1.0版。
该标准一开始就是针对JavaScript语言制定的,但是没有称为JavaScript,原因有二:
- Java时Sun公司的注册商标,根据授权协议,只有NetScape公司可以合法使用JavaScript这个名字,而且JavaScript本身已被NetScape公司注册为商标。
- 体现这门语言的制定者时ECMA,而不是NetScape,这样有利于保证这门语言的开放性和中立性。
因此ECAM和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现(另外还有JScript和ActionScript)。
下表是各个版本ES发布时间:
标准标题 | 发布时间 |
---|---|
ECMA-262 | 1997年7月 |
ECMA-262 Edition2 | 1998年8月 |
ECMA-262 Edition3 | 1999年12月 |
ECMA-262 Edition5 | 2009年12月 |
ECMA-262 Edition6 | 2015年6月 |
ECMA-262 Edition7 | 2016年7月 |
ECMAScript 2016(ES7)开始,版本的发布将会变得更加频繁, 这也意味着未来每个新的发行版本都会包含尽可能少的特性,而发行周期则缩短为1年,并且每年只发行确保一年期限内能够完成的所有特性。
let和const
let命令
基本用法
ES6新增了let命令,用法和var类似,区别是let声明的变量只在其所在的作用域有效。
{
var a = 3;
}
console.log(a); // 3
{
let a = 3;
}
console.log(a); // ReferenceError: a is not defined
一个典型的例子,看看var和let的区别:
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6](); // 10
上述i是全局声明的,a[0]到a[10]绑定的都是同一个函数function(){console.log(i)}
,所以在执行的时候i变成10了。会打印出10.
var b = [];
for(let i = 0; i < 10; i++) {
b[i] = function () {
console.log(i)
}
}
b[6](); // 6
使用let声明变量,let劫持了for循环的作用域,所以每个i都是有自己的作用域的。于是最后输出的结果是6。
不存在变量提升
let不像var会存在变量提升。所以必须先声明变量才能使用,否则报错。
console.log(x); // undefined
var x;
console.log(x); // ReferenceError: x is not defined
let x;
暂时性死区(temporal dead zone, TDZ)
ES6明确规定,如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。只要在声明之前使用这些变量,就报错。
上面那个例子也算TDZ。
if(true) {
tmp = 'abc'; // ReferenceError: tmp is not defined
console.log(tmp); // ReferenceError: tmp is not defined
let tmp; // TDZ结束
console.log(tmp);
}
还有些闭包较为隐蔽,不易察觉。函数参数默认赋值时es6才出现的,使用let。:)
function foo(x = y, y = 2) {
console.log(x);
}
foo(); // ReferenceError: y is not defined
因为函数参数的赋值从左到右,给x赋值时,y还处于TDZ。所以赋值失败。
ES6规定暂时性死区和不存在变量提升,主要是为了减少运行时错误,毕竟这样的失误在ES5是存在的。现在有了这种规定,浏览器会自动避免此类错误。
关于是否存在变量提升,这里有一篇文章。