Life and freedom Ge Lin ——— Draw by Razzh

let 是否会“hoisting”

Feb 14 · 5 min

#一个例子

console.log(a) // undefined
var a = 1

var 声明的变量会 Hoisting,也就是变量提升,那如果把 var 变量换成 let 变量,代码执行就会报错:

console.log(a)
let a = 1
// Uncaught ReferenceError: Cannot access 'a' before initialization

在报错中提到了 initialization,还记得我们经常碰到的变量未定义的报错吗? xx is not defined

为什么这里不会报“未定义”错误,而是说“初始化”错误?那什么是初始化?实际上 JS 是有一套预编译的过程,我们通过 varlet 在预编译中的表现说明一下为什么报错会是 initialization ,而不是 not defined

先从 var 的角度切入:

console.log(a)
var a = 1

在我们书写的代码中,JS 引擎并不是一开始就直接一行行执行代码,而是有一个预编译的环境,我们叫做 GO (全局上下文),它会全局寻找 var 声明的变量,将它们提升到 GO 对象中,并给它们每一个赋值为 undefined,此时的 GO:

GO {
  a: undefined
}

然后再执行我们的 JS 代码,console.log(a),打印出 GO 中的 undefined。现在将 var 变量换成 let 变量,我们再看看:

console.log(a)
var a = 1

这时在 JS 预编译的过程中,也会像我们刚刚那样,它会全局寻找 let 声明的变量,将它们提升到 GO 对象中,但它并不给 let 对象赋值为 undefined,而是什么都不做:

GO {
  a: 
}

到这里,let 声明的变量 a 实际上已经被提升到了全局的 GO 中,只不过未初始化,所以在后续执行 console.log 的时候会抛出 Cannot access 'a' before initialization 这个错误。

ECMA262 9.1章提到:If the binding exists but is uninitialized a ReferenceError is thrown, regardless of the value of S.

意译成中文:如果绑定应该没有完成初始化的值会抛出 ReferenceError 错误。

所以从严格意义上来讲,let 在它初始化(initialization)前的部分,是确实提升了。在代码执行前,GO 会提前收集所有声明的绑定。

我们通过对比 letvar 两个关键字,发送他们在变量提升的时候表现并不一致,由于历史的原因,在 JS 之前的版本使用 var 声明的变量,并使用 undefined 来表示变量的初始化,在声明 a 之前访问变量 a,结果是 undefined 并不会出现报错的行为,这很容易让人困惑,所以在 ES2015 中的 let/const 就是为了解决这个问题的,它的设计是在初始化的时候并不会声明为 undefined,所以当我们直接访问变量 a 时,就会抛出错误。

ES2015 中变量初始化之前不能访问变量,是在传递一种弱化变量提升概念的意图,变量提升是之前版本对于 undefined 的理解带来的历史遗留问题。本质的核心是我们应该理解在执行上下文的创建阶段,环境记录对象会提前收集所有的声明绑定。而在代码执行阶段才会针对每个变量绑定进行赋值操作。

#参考

最后引用了这波能反杀博主的小册,第 3.4 章:执行期上下文

浙ICP备2024129591号-1
春秋(Live) - 张敬轩
--:-- / --:--
  1. 1春秋(Live)张敬轩
  2. 2不吐不快(live)张敬轩
  3. 3男孩最痛(live)张敬轩
  4. 4粤语残片(live)陈奕迅
  5. 5几分之几(live)卢广仲
  6. 6地球很危险古巨基
  7. 7樱花树下(live)张敬轩

春秋 (Live) - 张敬轩 (Hins Cheung)

词:林夕

曲:Edmond Tsang

那夜谁将酒喝掉

因此我讲得多了

然后你摇着我手拒绝我

动人像友情深了

我没权终止见面

只因你友善依然

仍用接近甜蜜那种字眼通电

没人应该 怨地怨天

得到这结局

难道怪罪神没有更伪善的祝福

我没有为你伤春悲秋不配有憾事

你没有共我踏过万里

不够剧情延续故事

头发未染霜 着凉亦错在我幼稚

应快活像个天使

有没有运气再扮弱者 玩失意

有没有道理为你落发

必须得到世人同意

心灰得极可耻 心伤得无新意

那一线眼泪 欠大志

爱若能堪称伟大 再难挨照样开怀

如令你发现为你而活到失败

令人不安 我品性坏

我没有为你伤春悲秋不配有憾事

你没有共我踏过万里

不够剧情延续故事

头发未染霜 着凉亦错在我幼稚

应快活像个天使

有没有运气再扮弱者玩失意

有没有道理为你落发

必须得到世人同意

心灰得极可耻 心伤得无新意

那一线眼泪 欠大志 太没意思

若自觉这叫痛苦未免过份容易

我没有被你改写一生怎配有心事

我没有被你害过恨过

写成情史 变废纸

春秋只转载要事

如果爱你欠意义

这眼泪 无从安置

我没有运气放大自私的失意

更没有道理在这日

你得到真爱制造恨意

想心酸 还可以 想心底 留根刺

至少要见面上万次