一、为什么要是用let和const代替var声明变量
1、var声明的变量是函数作用域的,存在变量的提升。(变量的声明会被提前到当前作用域的最前面)
1 //变量提升
2 function f(condition){ 3 if(condition){ 4 var value="blue"; 5 return value; 6 } 7 else{ 8 console.log(value); //undefined
9 } 10 console.log(value); //undefined
11 } 12 f(false);
由于var声明的变量存在变量提升,所以当condition为false时,输出变量不会提示value变量未定义的错误,上面的代码类似于
1 //变量提升
2 function f(condition){ 3 var value; 4 if(condition){ 5 value="blue"; 6 return value; 7 } 8 else{ 9 console.log(value); //undefined
10 } 11 console.log(value); //undefined
12 } 13 f(false);
2、var可以在同一作用域内声明一个变量多次。变量的值为最后一次声明的
1 var value=10; 2 var value="100"; 3 console.log(value)//100
3、全局作用域的绑定
使用var声明的全局变量会自动绑定为全局对象的属性。从而又可能覆盖已经存在的全局属性。
1 console.log(global.RegExp);//[Function: RegExp]
2 var RegExp="aaa"; 3 console.log(global.RegExp);//"aaa"
代码不要写在js文件中使用node 文件名去测试,因为node会把一个文件作为一个模块,声明的变量不是全局对象的属性。
可以node的repl中测试或者浏览器环境中。
使用let和const声明的变量不会覆盖全局对象的属性,而是遮盖他
1 let RegExp="aaa"; 2 console.log(global.RegExp===RegExp);//false
二、let和const
通过let和const声明的变量是块级作用域,只在{}生效;类C语言多采用块级作用域,这样做更符合我们的习惯。
let声明
a、let声明的变量是块级作用域,且不存在变量提升机制,所以使用let声明的 变量只有放到当前作用域的最前面,才能在整个作用域中使用。
1 function f(condition){ 2 if(condition){ 3 let value="blue"; 4 return value; 5 } 6 else{ 7 //console.log(value); //ReferenceError: value is not defined
8 } 9 console.log(value); //ReferenceError: value is not defined
10 } 11 f(true);
变量离开if块就会被销毁,其他的块作用由于没有什么变量vaue,所以直接使用会抛出异常。
b、let声明的变量在同一作用域中禁止重复声明
1 //var value=10;
2 let value=10; 3 let value="100";//SyntaxError: Identifier 'value' has already been declared
4 console.log(value)//100
当在不同的作用域声明变量的时候,不会报错。
1 //var value=10;
2 let value=10; 3 function f(){ 4 let value="1dad"; 5 console.log(value)//'1dad'
6 } 7 console.log(value)//10
8 f()
const声明
a.const关键字用来声明一个常量,声明后变量的值不可以更改。const声明变量必须初始化。
1 const a='dada'
2 const b;//SyntaxError: Missing initializer in const declaration
b、const声明已存在的变量会报错,(无论是let和const)
var a=10; //let a=10
const a='dada'//Identifier 'a' has already been declared
c、用const声明对象
const声明的变量不允许修改变量的值。由于变量存在的是对象的值得引用,所以const声明的对象可以修改对象的属性,但是不能把修改对象的引用。
const a={name:'yiluhuakai'}; a.name="aaa"; console.log(a.name)//'aaa'
a={};//TypeError: Assignment to constant variable.
三、临时死区
使用let和const声明的变量不会进行变量提升,变量的作用域的开始到变量声明之前的位置成为临时死区,当在该区域使用变量时回抛出异常,即使是使用相对安全的typeof操作符。
typeof a;//作用域外不会抛出异常
function f(){ // typeof a;//ReferenceError: a is not defined
if(true) { ; } let a=10; } f();
四、循环中的函数
在循环中定义一个函数,打印当前的循环的变量i;
1 var arr=[]; 2 for(var i=0;i<10;i++){ 3 arr.push(function(){ 4 console.log(i); 5 }) 6 } 7 //打印
8 arr.forEach(function(item){ 9 item();//输出10次10
10 })
为什么输出结果是10次10呢,因为上面var声明的便利会提升到循环外面,循环中函数共享(闭包)同一个i。
立即调用调用函数可以解决这个问题。
1 var arr = []; 2 for (var i = 0; i < 10; i++) { 3 arr.push((function (value) { 4 return function () { 5 console.log(value) 6 } 7 })(i)) 8 } 9 //打印
10 arr.forEach(function (item) { 11 item();//0 1 2 ... 8 9
12 })
将i作为行参传给函数,value会复制i的值,这个之就是内部函数打印的值。
更简单的解决办法:使用let使命变量
1 var arr=[]; 2 for(let i=0;i<10;i++){ 3 arr.push(function(){ 4 console.log(i); 5 }) 6 } 7 //打印
8 arr.forEach(function(item){ 9 item();//输出0 1 ... 8 9
10 })
使用let声明的变量每次循环都会声明一个新的变量,所以之前迭代的同名变量初始化j将其初始化,所以每次输出的值都不一样。
for-in和for-of也是一样。
1 var arr=[]; 2 var object={ 3 a:true, 4 b:1, 5 c:2 6 } 7 for(let key in object){ 8 arr.push(function(){ 9 console.log(key) 10 }) 11 } 12 arr.forEach(function(item){ 13 item() //a b c 14 })
使用let会每次声明一个新的变量保存key,所以会每次输出不同的值。使用var使用的是同一个变量,最终输出都是c.
const用在循环中
1 var arr=[]; 2 for(const i=0;i<10;i++){ 3 arr.push(function(){ 4 console.log(i); 5 }) 6 } 7 //打印
8 arr.forEach(function(item){ 9 item();//TypeError: Assignment to constant variable.
10 })
在普通的for循环中使用const声明变量时,第一次循环const i=0不会报错,但是当第二次循环中初始化i后,对i执行i++操作时会报错。所以不能在普通的循环中使用const声明循环变量。
但是在for-of和for-in循环中使用const不会出现问题,因为只会对新创建的变量初始化,不会去改变变量的值。
1 var arr=[]; 2 var object={ 3 a:true, 4 b:1, 5 c:2
6 } 7 for(const key in object){ 8 arr.push(function(){ 9 console.log(key) 10 }) 11 } 12 arr.forEach(function(item){ 13 item() //a b c
14 })