js中的var,let,const 的区别

一. var 

1. var声明作用域 

使用var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁: 

function test(){
  var a='hi' //局部变量
}
test()
console.log(a)//报错

//如果省略操作符 
function test(){
  a='hi' //全局变量
}
test() 
console.log(a) //'hi' 
//去掉var操作符之后,就变成了全局变量,只要调用一次test(),就会定义这个变量,并且可以在函数外部访不  
//推荐这样做,容易造成混乱

 2. 声明提升 ,也就是把所有变量声明都拉倒函数作用域的订单,此外,使用var声明同一个变量也没有问题;

function test(){
  console.log(a)
  var a='hi'
}
test() // undefined 

//等价于下列代码 
function test(){
  var a; 
  console.log(a)
  a='hi'
}

//声明同一个变量
function foo(){
  var a=10;
  var a=20;
  var a=30;
  console.log(a); 
}
foo(); //30

二. let 

1.let 跟var 的作用差不多,最明显的区别是,let声明的范围是块作用域,而var声明的范围是函数作用域 

//var 没有块作用域   块作用域是是函数作用域的子集 因此let也没有函数作用域
if(true){
 var name='matt'; 
 let age=26;
 console.log(name); //matt
 console.log(age); //26
}
console.log(name); //matt 
console.log(age); //age没有定义 

2. let 也不允许同一个块作用域出现冗余声明

var name; 
var name; 

let age;
let age; // age已经声明过了


//不在同一个块中可以重复声明 
let age=30;
console.log(age); //30
if(true){
 let age=26;
 console.log(age); //26
}

//在同一作用域内,不能混用 var 和 let 声明同名变量,否则会抛出 SyntaxError。

var name;
let name;//SyntaxError 

let age; 
var age; //SyntaxError

3. let 有暂时性死区 就是let声明的变量不会在作用域中被提升

// var 会提升
console.log(name); //undefined 
var name='Matt'

//let 不会提升.
console.log(age)// age没有定义
let age=16; 
在let声明之前的执行瞬间被称为"暂时性死区"

4. 使用let在全局作用域中声明的变量不会变成window对象的属性(var声明的则会)

var name='matt'
console.log(window.name); //matt 

let age=26
console.log(window.age); //undefined 

5.for循环中的let声明和var 声明

在let 出现之前,for循环定义的迭代变量会渗透到循环体外部;

for(var i=0;i<5;++i){
  //循环逻辑
}
console.log(i); //5

//改成使用let之后,这个问题就消失了,因为迭代变量的作用域仅限于for循环内部;
for(let i=0;i<5;++i){
 //循环
}
console.log(i); //没有定义

 还有个面试经常提及的问题,在for循环的循环体内定义一个定时器,循环结束后,查看结果

 1. 使用var 的时候 

for(var i=0;i<5;i++){
  setTimeout(()=>console.log(i),1000)
}
//你可能会以为输出0,1,2,3,4
//实际会输出5,5,5,5,5

解释: 我们知道,循环结束的条件是i=5, 从事件循环机制的角度来分析上面的代码,是这样的: 

调用堆栈:用于存储程序按顺序调用的函数的详细信息的堆栈。

   1. 首先在Call Stack(调用堆栈)中执行同步代码var i=0,此时i小于5,执行下一行代码,发现是一个setTImeout计时器,它是一个宏任务,这个时候会被推入到WEB API中,计时器开始执行; 

   2. 执行i++,在Call Stack中进入下一次迭代循环,重复上面操作 

    其实上面的代码中WEB API一共存放过5个计时器,每个计时器进入到WEB API, 1s之后就会被推      入到QUEUE(队列)中(最后队列中也会有5个计时器),等待EVENT LOOP(事件循环)检测到Call           Stack为空时,将QUEUE中每一个计时器推入到Call Stack执行,最后全部代码执行完成,从调用栈       中退出

       使用var 的时候,由于每一次循环得到的迭代变量会被上一次的覆盖,最后i=5,退出循环,迭代变           量也被泄露了出去了,每一个计时器引用同一个i,因此会打印5个5

     《JavaScript高级程序设计4》: 是因为在退出循环时,迭代变量保存的是导致循环退出的值 :

       在之后执行超时逻辑时,所有i都是同一个变量,因而输出的都是同一个最终值

2.使用let 的时候 

for(let j=0;j<5;j++){
  setTimeout(()=>{
    console.log('j',j) //1 2 3 4 5
  })
}

 个人理解 for每次循环都是不同的块级作用域,let声明的变量是块级作用域的,所以也不存在重复声明的问题

JavaScript高级程序设计4》: 在使用let声明迭代变量时,js引擎在后台会为每个迭代循环声明一个新的迭代变量,每个setTimeout引用的都是不同变量实例,所以console.log()输出的使我们期望的值,也就是循环执行过程中每个迭代变量的值 

这种每次迭代声明一个独立变量实例的行为适用于所有风格的for循环,包括for-in和for-of循环

三. const 

1. const 的行为与let基本相同,唯一一个虫重要的区别是用它声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时的错误

const age=26;
age=36; //TypeError: 给常量赋值 

2.const声明的限制只适用于它指向变量的引用,换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制 

const person={ }
person.name='Matt'; //ok 

3. js引擎会为for循环中的let声明分别创建独立的变量实例,虽然const变量跟let变量很相似,但是不能用const 来声明迭代变量(因为迭代变量会自增);

for (const i=0; i<10;++i)() //TypeError: 给常量赋值

你可能感兴趣的:(javascript,前端,vue.js)