一文弄懂js中的var,let,const

55 阅读5分钟

在es6中新增了 let 和 const 关键字用来声明变量。要了解let、const和 var 的区别,我们先来聊聊变量提升

什么是变量提升

在JavaScript中,变量提升(Hoisting)是一种特殊的现象,它指的是变量和函数声明在代码执行之前就被提升到当前作用域的顶部。这意味着无论声明在何处,都会被视为在当前作用域的开始处声明。这个概念对于理解JavaScript代码的执行顺序非常重要。

通俗来讲,就是会把变量函数的声明给定义到当前作用域的顶部,但是先不给他赋值。 让我们来举个例子:

a = 1;
var a;
console.log(a);
 function test(){
     a = 1;
     var a;
     console.log(a);
 }

在上面这两段代码,按照道理来讲,假如一行一行执行,第一行就会报错,因为a未定义,但是由于变量提升,上面这段代码可以输出a的值,但是输出是underfined

函数同样存在着变量提升 js中存在着两种声明函数的方式

//函数声明式:
function fun1 () {}
//变量形式声明: 
var fun2 = function () {}

当使用变量方式声明函数时,变量同样会提升,此时变量还未定义,因此会报一个变量不是函数错误

fn() 
var fun1 = function () { 
console.log(1) } // Uncaught TypeError: fn is not a function 
fun() 
function fnu2 () { 
console.log(1) } // 输出结果:1

为什么会存在变量提升?

在es6之前,js和其他大部分语言不同是作用域只分为函数作用域(函数内部算一个作用域)和全局作用域,而其他语言支持块作用域(一个{}内为一个作用域)。 es6不支持块级作用域,将作用域内部的变量统一提升就是最快速,简单的设计。 这样做虽然能够提升性能和增大容错,但同样出现着问题。 1.容易导致变量被覆盖

var myname = "邓邓"
function showName(){
  console.log(myname);
  if(0){
   var myname = "茹茹"
  }
}
showName()

猜猜上面这段代码输出的是邓邓还是茹茹?答案是underfined。因为函数内部的变量myName会提升,并且在内部定义的变量跟外部变量名相同时会覆盖。

2.变量未销毁

int i=10086;
function foo(){
  for (var i = 0; i < 5; i++) {
  }
  console.log(i); 
}
foo()

上面这段输出的不是10086,也不是0,而是5.也是因为for循环中定义的变量i被提升到了foo函数顶端。

let,const,var的的使用和区别

我们正式引入我们今天的主角es6出现的let,const,块级作用域。 块级作用域从大括号开始 -> 到大括号结束。

块级作用域有以下几个特点:

  • 声明变量不会提升到代码块顶部

let/const 声明并不会被提升到当前代码块的顶部,因此你需要手动将 let/const 声明放置到顶部,以便让变量在整个代码块内部可用。

function getValue(condition) {
if (condition) {
let value = "blue";
return value;
} else {
// value 在此处不可用
return null;
}
// value 在此处不可用
}
  • 禁止重复声明

如果一个标识符已经在当前作用域被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。例如:

var count = 30;
let count = 40; // Uncaught SyntaxError: Identifier 'count' has already been declared
  • 局部作用域中声明跟全局变量同名的变量时一个新变量
function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // 同样的变量!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

在上述的这段代码中,由于var并不支持块作用域,所以最终只生成了一个变量 num,函数体内所有对 num 的赋值操作都会直接改变变量环境中的 num 的值。所以上述代码最后输出的是 2,而对于相同逻辑的代码,其他语言最后一步输出的值应该是 1,因为在 if 里面的声明不应该影响到块外面的变量。 当我们把var换成let,输出结果就对了,因为let支持块作用域,当出作用域时变量销毁,代码块中的x和函数中的x不是同一个变量,他们的声明也不会受到影响。

let和const的区别

let是声明了一个变量,而const声明了一个常量。变量可以随便改变他的值,而常量不能改变他的值。 但是常量存储对象数据类型时候,存储的是对象的地址,也就是他指向的地址不能变,但是指向对象的属性可以发生变化。

暂时性死区

其实上文中说的let不会将变量提升这句话并不完全正确,我们经过调试可以发现其实在作用域开头也初始化了变量,只是系统不允许访问,访问会报错。 ES6 规定:如果区块中存在 let 和 const,这个区块对这两个关键字声明的变量,从一开始就形成了封闭作用域。假如尝试在声明前去使用这类变量,就会报错。这一段会报错的区域就是暂时性死区。上面代码的第4行上方的区域就是暂时性死区。