ECMAScript 6 学习笔记(一):let / const 命令

前言

现在 ES6 已经非常普及了,基本上前端新出的框架都用上了ES6的语法。作为新兴时代的前端,如果你还对 ES6 不了解那就真的过分了,兄弟们赶紧撸起袖子搞起来吧!附上阮一峰写的书 ECMAScript 6 入门

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言

ECMAScript 和 JavaScript 的关系

ECMAScript 是 Javascript 的规范,Javascript 是 ECMAScript 的实现

声明的新方式 let / const

使用ES6,我们需要全面使用let/const替换var,我们常常使用let来声明一个值会被改变的变量,而使用const来声明一个值不会被改变的变量,也可以称之为常量。

1. let

不同于 var,let 声明的变量只在 let 命令所在的代码块内有效

1
2
3
4
5
6
7
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1

for 循环的计数器,就很合适使用let命令

下面的代码使用 var 声明 i,console 里面的 i 是全局声明的,会随着循环而变化最后为 10,使得 a[0] , a[1] … a[9] 函数里面的 i 都是 10,所以最后 a6 结果也是 10

1
2
3
4
5
6
7
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10

而使用 let 声明的变量仅在块级作用域内有效,如下,当前的 i 只在本轮循环有效,所以每一次 console 的 i 其实都是一个新的变量,所以输出是 6

1
2
3
4
5
6
7
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6

let 不存在变量提升

var 声明的变量,可以在声明之前使用,值为undefind
而 let 声明的变量一定要在声明后使用,否则报错

1
2
3
4
5
6
7
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

暂时性死区

只要块级作用域内存在 let 命令,它所声明的变量就“绑定”这个区域,不再受外部的影响。在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

1
2
3
4
5
6
7
8
9
10
11
if (true) {
// TDZ开始
tmp = 'dota'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp;
// TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}

不允许重复声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function () {
let a = 10;
var a = 1;// 报错
}
function () {
let a = 10;
let a = 1;// 报错
}
function func(arg) {
let arg; // 报错
}
function func(arg) {
{
let arg; // 不报错
}
}

2. 块级作用域

ES5

1
2
3
4
5
6
7
8
9
10
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world'; // if代码块内var变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
}
}
f(); // undefined

ES6

1
2
3
4
5
6
7
function f1() {
let n = 5;
if (true) {
let n = 10; // if代码块内声明了新的n变量,外层代码块不受内层代码块的影响
}
console.log(n); // 5
}

块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了

1
2
3
4
5
6
7
8
9
10
11
// IIFE 写法
(function () {
var tmp = ...;
...
}());
// 块级作用域写法
{
let tmp = ...;
...
}

3. const

const声明一个只读的常量。一旦声明,常量的值就不能改变。

1
2
3
const COUNT = 10
COUNT = 11;
console.log(COUNT) //Uncaught TypeError: Assignment to constant variable.

对于const来说,只声明不赋值,就会报错。

1
2
const foo;
// SyntaxError: Missing initializer in const declaration

const 声明与 let 基本相同

  1. 只在声明所在的块级作用域内有效。
  2. const命令声明的常量也是不提升
  3. 同样存在暂时性死区,只能在声明的位置后面使用。
  4. const声明的常量,也与let一样不可重复声明。

const本质

const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const foo = {};
// 可以为 foo 添加一个属性
foo.prop = 123;
foo.prop // 123
// 但是如果将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
const a = [];
a.push("1")
console.log(a) // ["1"]
const a = [];
a = [1,2,3]
console.log(a) // 报错

4. 顶层对象的属性

顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。ES5之中,顶层对象的属性与全局变量是等价的。

ES6一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩。

1
2
3
4
5
var a = 1;
window.a // 1
let b = 1;
window.b // undefined