ES6中新增let关键字,用于声明变量。其用法类似var,但是所声明的变量只在let命令所在的块内有效。(块级作用域文章后面会进行讲解)
: 1.只作用于块级作用域。
2.不存在变量提升。
3.不允许重复声明。
4.暂时性死区(TDZ)。
1.只作用于块级作用域。
例如:
{
let a=10;
var b=5;
console.log(a,b) //输出a=10 b=5;
}
console.log(b); //输出 b=5;
console.log(a); //报错 a is not defined
上述代码let声明的变量在块级作用域{ }中是有效的,但是在最后一行的输出就会直接报错,因为没有在块作用域{}中,a就没有被定义。
let命令更适合for循环
例如
{
for(let i=0;i<5;i++){
console.log(i); //输出 0,1,2,3,4
}
console.log(i);//报错 i is not defined
}
{
for(var i=0;i<5;i++){
console.log(i); //输出 0,1,2,3,4
}
console.log(i); //输出 5
}
变量i是let声明的 当前i只在本轮循环有效,且只在本块级作用域有效,所以每一次循环的i其实都是一个新的变量。
你可能会问:如果每一轮循环的变量都是重新声明的 那它怎么知道上一轮循环的值,从而计算出本轮的值呢?
这是因为javascript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
另外,for循环还有一个特别之处,for一个整体算一个块级作用域,那么设置循环变量那部分是一个父级作用域,而循环体内部是一个单独的子作用域。
2.不存在变量提升
let与var的区别,var存在变量提升,但是let不存在变量提升。
那么什么是变量提升呢?
变量提升:就是变量在声明前可以使用,而且不会报错,但是值为undefined。
意思是var声明的变量在其声明前使用是可以的不会报错,
但是let声明的变量一定要在其声明之后使用,否则就会报错。
例如
{
console.log(a);//输出 undefined
var a=2;
}
{
console.log(b) //报错 Cannot access 'b' before initialization
let b=5;
}
上诉代码用var声明的a会发生变量提升,那么值为undefined。
而有let声明的b就不发生变量提升,在声明前使用,b就是一个不存在的,那么就直接报错。
3.不允许重复声明
let不允许在相同的作用域内重复声明同一个变量。
例如
{
var a=10;
let a=5;//直接报错 Identifier 'a' has already been declared
}
{
let b=10;
let b=5; //直接报错 Identifier 'b' has already been declared
}
{
const c=10;
let c=5; //直接报错 Identifier 'c' has already been declared
}
{
function f1(foo){
let foo; //报错 同时也不能在函数内部重新声明参数
}
}
以上代码都是在相同作用域下重复对同一个变量的声明,那么就直接报错。
4.暂时性死区
只要用let声明的变量,那么这个变量就绑定这个块级作用域,不再受外部的影响。
例如
{
var tmp=123;
if(true){
tmp="abc";//这里将会报错
let tmp;
}
}
因为let存在暂时性死区,只要区块中存在let与const命令,就算在最开始用var定义了tmp,但是在if里又是一个块级作用域,在if这个区域就形成一个封闭作用域,然后在let声明前对tmp的使用就会报错。
const声明一个只读常量,一旦声明,常量的值就不能被改变。
const的与let的特点基本相同,同样不存在变量提升,存在暂时性死区,只能在声明后使用。
例如
{
const p=8.12;
p=10;//报错 Assignment to constant variable.
}
const实际并不是变量的值不能改动,而是指向的那个内存地址所保存的数据不得改动;数据一半存储在堆中,
对于简单的基本数据类型(string number boolean null undefined)值保存在指向的那个内存地址,因此等同于常量。
但对于引用数据类型(object array ),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针
是固定的(即指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能够控制了。
例如
{
const foo={
};
foo.num=123;//这里就会成功不会报错,可以添加新的属性。
}
ES5中只有全局作用域和函数作用域,没有块级作用域,ES6新增了块级作用域。
例如
{
function f1(){
let a=5;
if(true){
let a=10;
}
console.log(a);//输出 a=5。
}
上述代码有2个代码块,f1是以个大的块作用域,里面嵌套了一个if小的块作用域,在if之外输出a是不会被影响的,从而输出a=5。
块级作用域是可以嵌套的。
嵌套的作用域中外层的作用域不能访问内层作用域的变量,但是内层可以访问外层的。
例如
//例子1
{
function f1(){
let a=10;
if(true){
console.log(a);
}
}
f1();//输出a=10;
}
//例子2
{
function f1(){
let b=0;
console.log(b);//输出b=0;
function f2(){
let c=10;
}
console.log(c);
}
f1();//报错 c没有定义
}
例子1的代码内层if的作用域能够访问外层f1的a;
但是例子2中的f1函数console.log(c)不能访问内层f2的C,就会报错。