let和var也用于定义变量。不同的是let是块级作用域,只在定义好的块级作用域内生效。大括号是块级作用域{vara="Iamvardefined";letb="Iamdefinedbylet"}console.log(a);//我是由varconsole.log(b)定义的;//b没有定义,可以看到let定义的变量在全局范围内是不生效的。如果我们在全局范围内定义,试试看会不会输出leta="Iamdefinedbylet"varb=0;while(b<5){b++;console.log(a)}//⑤Iamavariabledefinedbyletif(true){console.log(a)}//我是let定义的变量。我们发现可以这样输出。如果我们反过来呢?for(leti=0;i<5;i++){leta="letinflowcontrolstatement花括号";varb="varinflowcontrolstatement花括号";console.log(i)}//0,1,2,3,4console.log(a);//a未定义console.log(b);//varfor(varj=0;j<1;j++){consoleinflowcontrolstatement.log(j)}//0console.log(j);//1我们发现let定义的变量不会被提升,只要有块级括号,只有在块级才有意义在函数中functiontest(){console.log(a)}()呢//报错的时候,我们发现无法输出。我们可以得出结论,流控语句中的变量都是全局变量,只是在流控语句中括号内定义的是块级的,不会提升为全局变量。for循环的另一个特点是设置循环变量的部分是一个父作用域,循环体内是一个单独的子作用域for(leti=0;i<3;i++){leti="abc";console.log(i)}//abc,abc,abcletfor循环中声明的变量,每次循环都会被引擎记录下来,但是var声明的变量会刷新并覆盖上次的值vara=[];for(vari=0;i<10;i++){a[i]=function(){returni};}console.log(a)//[f,f,f,f,f,f,f,f,f,f]a.forEach((e)=>{console.log(e())})//⑩10我们可以看到,这个方法实际上是将未执行的函数压入数组中。i已经被循环覆盖了,最终输出的是i此刻的值10vara=[];for(leti=0;i<10;i++){a[i]=function(){returni};}console.log(a)//[f,f,f,f,f,f,f,f,f,f]a.forEach((e)=>{console.log(e())})//0,1,2,3,4,5,6,7,8,9但是对于let声明的变量,引擎每次都会自动存储记录i的值,不会覆盖,所以每次output是push的时候没有对i值let进行变量提升,只有定义了之后才不会报错console.log(a);vara=1;//undefinedconsole.log(b);letb=2;//报错let有强制绑定变量的能力。原来用var声明的变量,用let重新声明时会被强制绑定。let所管辖的块级作用域中,原本由var声明的所有变量都被强制为自己声明的值,形成了一个暂时的死区,let块级作用域中let声明之前的变量会报错错误vara=1;{console.log(a);leta=2;}//a未定义vara=1;{leta=2;console.log(a);//2};console.log(a)//1vara=1;console.log(a);//1{leta=2;console.log(a);//2};let中不允许重复声明同一个变量相同的范围{vara=0;leta=1;console.log(a)}//错误{leta=0;vara=1;console.log(a)}//错误{leta=2;leta=3;console.log(a)}//Error{leta=2;console.log(a);leta=3;}//上面最后一个例子说明,当变量在调用前再次声明我们可以看到let声明的变量可以引用块级作用域外的函数leta=f();functionf(){return1}console.log(a)//1{leta=f();console.log(a)//1functionf(){return1}const声明的变量是永久变量,不可更改,声明a后必须初始化const;//错误constb=1;b=2//错误const的作用域和let命令一样:只在声明所在的块级作用域内有效。const命令声明的常量同样没有提升,也有一个暂时的死区,只能在声明的位置之后使用。const声明的常量不能像let一样重复声明。const其实保证的不是变量的值不能改变,而是变量指向的内存地址不能改变。对于简单类型的数据(数字、字符串、布尔值),值存储在变量指向的内存地址,因此相当于一个常量。但是对于复合类型的数据(主要是对象和数组),变量指向的内存地址只是一个指针。const只能保证指针是固定的。至于它指向的数据结构是不是可变的,就完全不受控制了。因此,在将对象声明为常量时必须非常小心。常量foo存储一个指向对象的地址。只有这个地址是不可变的,即foo不能指向另一个地址,但是对象本身是可变的,所以仍然可以给它添加新的属性。constfoo={};foo.prop=123;console.log(foo.prop)//123常量a是一个数组,数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。consta=[];a.push(1);console.log(a)//[1]console.log(a.length)//1a=[]//报错并验证以下引用vara=1;b=a;console.log(b);//1a=2;console.log(b)//1b=3;console.log(a)//2可以看到var定义的变量,当b=a,是a的直接拷贝,不是引用leta=1;b=a;console.log(b);//1a=2;console.log(b);//1b=3;console.log(a);//2console.log(b);//3varb=4;console.log(b)//4可见let也是一样的const也是consta=1;b的拷贝=a;console.log(b);//1b=3;console.log(a);//1console.log(b);//3varb=4;console.log(b)//4有还有很多地方很清楚的地方,大家可以指点一下,互相学习,加深理解
