笔记根据阮一峰老师的ECMAScript 6 入门,内容基本来自于此书,总结是为了理解学习和加深印象,把认为重要的部分记录下来。
1. let命令
基本用法
ES6中新增加了let
用来声明变量,他和var
的用法类似,但是let声明的变量只在let所在的代码块内有效。
1 |
|
如上面代码所示,如果在代码块外面调用了let的话,结果会报错。这表明let声明的变量只在他所在的代码块有效。
所以for
循环中,我们用let声明会很合适。1
2
3
4
for( let i = 0; i < arr.length; i++){}
console.log(i)//ReferenceError: i is not defined
上面代码的计数器i,只在for循环内有效。
下面的代码如果使用var
,最终的输出结果是10。
1 |
|
因为var
声明的i
在全局作用域中都有效,当在循环当中时a[6]
返回function () {console.log(i);};
,当在for
循环执行完毕后i
的值会变成10。当执行a[6]()
时结果为10。
如果使用let
,声明的变量只在块级作用域内有效,代码如下。
1 |
|
如上面代码所示,因为i
只在本轮循环内有效,所以每一次循环的i
其实都是一个新的变量,所以最后结果为6。
不存在变量提升
let
不会像var
存在变量提升的现象,所以let
要在声明之后调用,否则会出现错误。
1 |
|
如上代码,在执行代码前,js会提前读取声明的var
和function
的变量名(变量值没有预先加载),所以在执行代码前,变量foo
已经存在了会输出undefined
,而变量bar
不会发生变量提升,所以会抛出一个错误。
暂时性死区
只要有块级作用域内存在let
命令,他所声明的变量就绑定这个区域,不再受到外部影响。
1 |
|
上面代码中,因为块级作用域内let
声明了局部变量tmp
,tmp
就绑定到了这个块级作用域中,块级作用域外的var
声明会失效,在块级作用域内在还没有let
声明前就对tmp
赋值的话就会报错。
ES6中规定,如果区块中存在let
和const
命令,这个区块对这两种声明的变量从一开始就形成了封闭的作用域,凡是在声明之前就使用这些变量的话就会报错。
简单来说,在代码块内,如果使用let
命令来声明变量,这个变量在声明之前都是不可用的。这个在语法上称为“暂时性死区”(temporal dead zone,简称TDZ)。
1 |
|
上面的代码中,在let
命令声明变量tmp
前,都属于tmp
的“死区”。
“暂时性死区”也意味着typeof
操作会出现错误。
1 |
|
但是如果一个变量根本没有被声明,使用typeof
反而不会报错。1
2//undeclared_variable为为声明变量
typeof undeclared_variable // "undefined"
所以,新语法在有了let
声明之后,一定要声明完变量之后再去使用,否则有可能会报错。
还有一些特别的“死区”。1
2
3
4
5
6
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
上面代码运行会报错,因为在把y
的值赋给x
前,y
还没有声明,属于死区。
总之,暂时性死区的本质就是,在已进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有在声明变量后才可以获取和使用该变量。
不允许重复声明
let
不允许在相同作用域中,重复声明同一个变量。
1 |
|
也不能在函数内部重新声明参数。
1 |
|
2. 块级作用域
为什么需要块级作用域?
在ES5中只有全局作用域和函数作用域,没有块级作用域,这会造成很多问题。
计数的循环变量泄露为全局变量。
1 |
|
ES6的块级作用域
let
是为JavaScript新增了块级作用域。1
2
3
4
5
6
7
8
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
最后输出的结果为5,这表示let
声明的是一个块级作用域,外层的代码块不受内层的影响,如果使用var
声明的话,最后输出结果为10。
块级作用域出现后,立即执行匿名函数(IIFE)就不是唯一的选择了。1
2
3
4
5
6
7
8
9
10
11
12
// IIFE写法
(function () {
var tmp = ...;
...
}());
// 块级作用域写法
{
let tmp = ...;
...
}
3. const命令
const也用来声明变量,这个变量是常量一旦声明,他的值是不可以改变的。
1 |
|
在严格模式下,改变声明的常量的值会报错。在常规模式下不会报错但也不起作用。1
2
3
4
const PI = 3.1415;
PI = 3; // 常规模式时,重新赋值无效,但不报错
PI // 3.1415
const声明的变量不可以改变值,所以一旦声明后必须立即初始化。
1 |
|
如上面代码所示,在严格模式下,如果没有立即初始化,会报错。在常规模式下,不会报错,但是以后对foo
赋值的话也是无效的。
1 |
|
const
的作用与和let
相同,都是在块级作用域之内有效,并且也不存在声明提升,会出现暂时性死区,只能在声明之后才能够调用。
1 |
|
const
声明的常量,也和let
一样不可以重复声明。
1 |
|
对于复合型变量名,变量名不会指向数据,而是指向数据所在的地址。const
命令可以使指向的地址不变,但是不能保证地址内的数据不变。比如,如果使用const
声明了一个对象,其实是声明了一个指向对象的地址,这个地址是不可变的,但是可以为这个对象来添加新的属性。
1 |
|
如上,变量a
是一个数组,数组本身是可以改写的,但是如果将另一个数组赋值给a
,就会报错。
如果想让一个对象,不能添加新的属性,可以使用Object.freeze
的方法。
1 |
|
如上,添加新的属性不会起作用,在严格模式下还会报错。
ES5声明变量的方法有两个:var
,function
。ES6除了这两种方法外,还有let
,const
,import
和class
这四种方法。
4. 跨模块常量
const
声明的常量只在当前代码块有效。如果想设置跨模块的常量,可以采用下面的写法。
1 |
|
5. 全局对象的属性
ES6规定了,使用var
和function
声明的全局变量,依旧是全局对象的属性;使用let
命令,const
命令和class
命令声明的全局变量,不属于全局对象的属性。
1 |
|
以上。