1、let关键字
ES6不再使用var来声明一个变量,而使用let,可以认为let是优化后的var。
基本用法
它的用法类似于var,但所声明的变量,只在声明变量所在的代码块内有效。
{
let a = 1;
var b = 2;
}
console.log(a); //ReferenceError:a is not defined
console.log(b); //2
for循环的计数器,就很适合使用let声明。
for(let i = 0;i < 10;i++){
//do something
}
console.log(i); //ReferenceError:i is not defined
如果使用var声明for循环计数器。
for(var i = 0;i < 10;i++){
//do something
}
console.log(i); //10
产生区别的原因是:
var声明的变量为全局变量,全局只有一个变量i;
let声明的变量仅在块级作用域内有效,当前的i只在本轮循环有效,每一次循环的i其实都是一个新的变量。
你可能会问,如果每一次循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?
这是因为JavaScript引擎内部会记住上一轮循环的值,初始化本轮循环的变量i时,就是在上一轮循环的基础上进行计算的。
另外,for循环还有一个特别之处,设置循环变量的那一部分是一个父作用域,而循环体内部是一个单独的子作用域。
for(let i = 0;i < 3;i++){
let i = 'a';
console.log(i);
}
// a
// a
// a
上面代码允许输出3次a。这表明循环内部的i和循环计数的i不在同一个作用域,有各自单独的作用域。
不存在变量提升
“变量提升”现象,指变量可以在声明前使用。
使用var声明的变量便存在“变量提升”现象,值为undefined。
let声明的变量纠正了这种现象,它所声明的变量必须在声明后使用,否则就会报错。
//var关键字声明变量的情况
console.log(a); //输出为undefined
var a = 1;
//let关键字声明变量的情况
console.log(b); //ReferenceError:b is not defined
let b = 2;
暂时性死区
ES6明确规定,如果区块存在let和const声明变量,从一开始就形成了封闭作用域。凡在声明之前使用这些变量,就会报错。
也就是说,即使存在全局变量声明,在区块中再次声明同名变量,在区块中使用这个变量是会报错的,不再受外部的影响。这在语法上,称为“暂时性死区”(temporal dead zone,检查TDZ)。
var t = 1;
{
t = 2; //ReferenceError:t is not defined
let t;
}
在没有let之前,typeof运算符是百分之百安全的,永远不会报错,现在这一点不成立了。
typeof x; //ReferenceError:x is not defined
let x;
比较隐蔽的TDZ:
function b(x = y,y = 2){
return [x,y];
}
b(); //ReferenceError:y is not defined
var m = m; //不报错
let n = n; //ReferenceError:n is not defined
不允许重复声明
同一作用域内,let关键字不允许重复声明同一个变量。
function f1(){
let a = 10;
var a = 1;
}
f1(); //SyntaxError: Identifier 'a' has already been declared
function f2(){
let a = 10;
let a = 1;
}
f2(); //SyntaxError: Identifier 'a' has already been declared
function f3(arg){
let arg;
}
f3(); //SyntaxError: Identifier 'arg' has already been declared
function f4(arg){
{
let arg;
}
}
f4(); //不会报错
2、const关键字
基本用法
const声明的是一个只读常量。一旦声明,常量的值就不能改变。这就意味着,const一旦声明变量,就必须立即初始化。
const PI = 3.14;
console.log(PI); //3.14
PI = 3.14159; //TypeError: Assignment to constant variable
const b; //SyntaxError: Missing initializer in const declaration
const关键字也和let关键字一样:
只在声明所在的块级作用域内有效;
不存在变量提升;
存在暂时性死区。
本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
对于简单数据类型(数值、字符串、布尔值),等同于常量;
对于复合数据类型(主要对象和数组),const只保证指向实际数据的指针是固定的,至于它指向的数据结构是不是可以变,就完全不能控制。
const obj = {};
//为obj添加一个属性
obj.prop = 123;
console.log(obj.prop); //123
//将obj指向另一个对象
obj = {}; //TypeError: Assignment to constant variable
如果真的要将对象冻结,应使用Object.freeze方法。
var constantize = (obj)=>{
Object.freeze(obj);
Object.keys(obj).forEach((key,i)=>{
if(typeof obj[key] === 'object'){
constantize(obj[key]);
}
})
}