一、为什么要是用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 })