let真的没有变量提升吗

731 阅读3分钟

关于 let ,很多文章中都将其描述为不存在变量提升,这也是一直困扰我的一个问题,在看了一些文章后后,先抛出我认为合理的结论:let 是存在变量提升的!下面简单介绍一下我对此的认识。

变量的创建的过程

在 JS 中,一个变量存在以下几个生命周期,这是讨论下面内容的基础(我们暂时不考虑回收的问题)

  • 声明(Declaration)
  • 初始化(Initialization)
  • 赋值(Assignment)

补充:

至于 const,没有赋值阶段,只有初始化,所以不能修改值。

变量环境和词法环境

我们知道,一段 JS 代码经过 JS 引擎的编译才可以运行,在编译之后,一段 JS 代码会生成两部分内容:

  • 执行上下文
  • 可执行代码

其中执行上下文又可以分为变量环境词法环境(关于执行上下文的具体细节,在这里我们不进行过多讨论,有需要可自行查阅),通过 var 声明的变量和函数会放在变量环境中,而通过 let 和 const 声明的变量会放在词法环境之中,词法环境像一个栈,每一个作用域都是词法环境中一个单独的区域

变量提升

JS 引擎编译阶段碰到 var 声明,比如 var a = 1,会把 a 放到变量环境之中,并将其初始化为 undefined,至此也就完成了我们常说的变量提升,在执行代码的时候执行到 var a = 1 这句的时候,再将 a 赋值为 1。

而对于 let b = 2 来说,编译阶段会将 b 放到词法环境中,但是,并不会将其初始化,因此也就产生了我们常说的暂时性死区问题。

只有在执行代码的时候执行到 let b = 2 这句,let b 将其初始化为 2。

下面看一段代码

console.log(b)
let b = 2
//我们这段代码是script标签里的一段代码
//Uncaught ReferenceError: Cannot access 'b' before initialization

let c
console.log(c)
//c被声明为undefined
//undefined

这样是不是更好理解了,报错信息是 b 没有初始化,说明 b 已经声明了

这里补充一下,在浏览控制台执行这段代码的话,结果是这样的,迷惑性更强:

console.log(b)
let b = 2
//这是直接在控制台调试
//Uncaught ReferenceError: b is not defined
  • 直接在控制台操作,作用域是 global,而在一个 html 文件的 script 标签里,作用域是 script
  • 通过 let 声明的变量,不属于全局作用域 global,所以在控制台打印 b 为 not defined,因为确实在 global 里没有声明 b
  • 关于此处的更多细节可以参考这里

为什么不能重复声明

我们知道,如果两次 var a = 1是没有问题的,第二次的赋值会把第一次的覆盖掉,但是对于 let 这样会报错,这部分具体细节不展开,前面我们提到每一个作用域都是词法环境中的一个单独区域,这个单独区域内是不允许重复声明的。

let x = x

这一句代码,let x 是没有问题的,在执行之前,x 的声明已经被提升,现在试图初始化 x,而右侧的 x 是一个仅被声明而没有被初始化的变量,所以会报错,而且 x 还将永远处于仅声明而没有初始化的状态,形成永远的暂时性死区,这说明 let 的初始化只有一次机会。

总结

  • var 的声明初始化被提升了
  • function 的声明、初始化和赋值都被提升了
  • let 只有声明被提升

以上就是个人对于 let 的一些认识,可能说的不是特别详细,如有错误的地方,恳请各位大佬指正。