ES6 在原有两种声明变量命令(var 和 function)的基础上,又加入了一共四种声明变量的命令 let const import class,本文先对 let 和 const 的用法进行说明。
let
先说说 let,同样是变量声明 let 相对于 var 引入了块级作用域的概念。
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
这个例子说明了同样是在外部调用代码块内部声明的变量,结果用 let 声明的报错,用 var 声明的返回值。 这说明用 let 声明的变量只在声明时的代码块可用。 最典型的例子就是这道面试题了:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
a[7](); // 10
a[8](); // 10
a[9](); // 10
之所以每次输出的值都是 10,是因为用 var 声明的变量没有块级作用域的特性,会进行变量提升,所以 i 就是全局变量,每一次遍历都会把之前的 i 覆盖掉。 如果用 let 就不会发生这样的情况:
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
因为 let 声明的变量只在当前代码块内有效,所以每次遍历的 i 都是独立的,互不影响。 另外对于 for 循环的代码块还有一个特殊的地方,就是循环变量的地方是一个作用域,循环体内部有又是一个单独的作用域:
for (let i = 0; i < 3; i++) {
let i = "inside";
console.log(i);
}
//"inside"
//"inside"
//"inside"
输出三次‘inside’,表明函数内部的 i 和循环变量 i 分别在不同的作用域。
再也没有变量提升
用 var 声明的变量会有变量提升的情况,大家应该都很熟悉,但是这其实并不符合正常的逻辑,变量应该在声明语句之后才可以使用。
console.log(a); // undefined
var a = 1;
console.log(a); // ReferenceError: a is not defined
let a = 1;
暂时性死区(temporal dead zone)
在用 let 声明变量的同时会绑定(binding)这个区域,使变量不受外部影响
var a = "outside";
if (true) {
console.log(a); // ReferenceError
let a;
}
可以看出在代码块中使用 let 声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死区”(temporal dead zone)
不允许重复声明
let 不允许在相同的代码块中重复声明相同的变量
//报错
function foo() {
let a = 10;
var a = 1;
}
foo();
// 报错
function foo() {
let a = 10;
let a = 1;
}
foo();
即使像这样也不行:
//报错
function foo(a) {
let a = 10;
}
foo();
块级作用域
ES6 之前只有全局作用域和函数作用域,这样带来了很多棘手的问题。 比如:
var a = 10;
function f() {
console.log(a);
if (true) {
var a;
}
}
f();
// undefined
上面代码本来是 if 代码块使用自己内部的 a,函数内使用外部的 a,但是因为变量提升的问题,导致 if 代码块内的 a 影响到了外部的 a,所以函数执行后输出 undefined。 在 ES6 引入了 let 之后事情就变得简单许多,let 的作用实际上就是给 JavaScript 引入了块级作用域。
function foo() {
let i = 10;
if (true) {
let i = 20;
}
console.log(i); // 10
}
foo();
上面代码说明用 let 内部的代码不会影响外部的代码。
const
const 和 let 的区别是,声明的是一个只读常量,一旦声明,值就不能改变。
const a = 1;
a = 2;
// 报错
const 所声明的常量不能改变,所以声明和赋值的过程必须同时进行。
const a
// 报错
const 的作用域与 let 相同,都具有块级作用域的概念。
if (true) {
const a = 10;
}
console.log(a); // a is not defined
const 也不会有变量提升的操作,所以同样存在暂时性死区,只能在声明后使用。
if (true) {
console.log(a);
const a = 10;
}
// ReferenceError: a is not defined
同样不可重复声明
const a = 1;
const a = 2;
// SyntaxError: Identifier 'a' has already been declared
const 声明的变量真的不能改变吗?
对于简单类型的数据(数值、字符串、布尔值)而言,值确实是不能改变,但对于复杂类型(对象和数组),变量保存的只是指向内存地址的指针,const 只能保证这个指针不会被改变,至于指针所指的内容则完全不受控制。 数组:
const arr = [];
arr[0] = 1;
console.log(arr); // [1]
arr = [2]; // 报错
对象:
const obj = {};
obj.a = 123;
obj.a; // 123