前端日子我上课的时候跟饥人谷的学生讲了《let 声明的五个特色》,其中一个就是「let 声明会提高到所在块的顶部」,然而今天早上有个学生就问我了:git
MDN 上说 let 不会提高,为何你说 let 会提高呢?
当时我内心一方:难道我讲错了?github
因而我看了 MDN 英文版的原文,发现写的也是:面试
In ECMAScript 2015, let do not support Variable Hoisting, which means the declarations made using "let", do not move to the top of the execution block.
看来我真的错了?因而我继续翻看 ECMA-262.pdf,发现了两处地方支持个人论点。微信
首先是 13.3.1 Let and Const Declarationside
let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
这说明即便是 block 最后一行的 let 声明,也会影响 block 的第一行。这就是提高(hoisting)(这句话存疑)。函数
以及 18.2.1.2 Runtime Semantics: EvalDeclarationInstantiation( body, varEnv, lexEnv, strict)学习
The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.
这句话从侧面证实了 let hoisting 的存在。 ui
ECMAScript 都提到了 var/let hoisting,我不知道还有什么理由认为 let hoisting 不存在。this
因此,我就把 MDN 的英文版和中文版给纠正过来了:(后来又被 TC39 的人改了)
在 ECMAScript 2015中, let 也会提高到语句块的顶部。可是,在这个语句块中,在变量声明以前引用这个变量会致使一个 ReferenceError的结果
但愿被以前 MDN 某个版本误导的同窗周知。
有些同窗仍是认为 let 不会提高,试试理解下面的代码:
let a = 1 { a = 2 let a }
若是 let 不会提高,那么 a = 2 就会将外面的 a 由 1 变成 2 啊。
运行发现 a = 2 报错:Uncaught ReferenceError: a is not defined
这说明上面的代码近似近似近似近似近似近似地能够理解为:(注意看注释中的 TDZ)
let a = 1 { let a // TDZ 开始的地方就是这里 'start a TDZ' a = 2 // 因为 a = 2 在 TDZ 中,因此报错 a // TDZ 结束的地方就是这里 'end a TDZ' }
因此,let 提高了。可是因为 TDZ 的存在,你不能在声明以前使用这个变量。
什么是 hoisting?
我并无查到明确的定义,只能综合理解一下。
JS 的 var 的 hoisting 最好理解:无论你把 var a 写在函数的哪一行,都好像写在第一行同样;当前函数做用域里的全部 a 都表示你写的这个 a,这就是 hoisting。
如下是维基百科的释义:
Variables are lexically scoped at function level (not block level as in C), and this does not depend on order ( forward declaration is not necessary): if a variable is declared inside a function (at any point, in any block), then inside the function, the name will resolve to that variable. This is equivalent in block scoping to variables being forward declared at the top of the function, and is referred to as hoisting.
什么是 TDZ?
这个依然没要找到明确的定义,大意是「在某个时间点以前,你不能访问某个变量,即便这个变量已经存在了」。
JS 的 let 中这个「时间点」就是 LexicalBinding。
JS 的 let 中这个「存在」的意思是「The variables are created」。
至于 TDZ 里这个变量有没有被声明,不是 TDZ 关心的。
不过说句实在话,let 有没有 hoist 都无所谓,代码还不是那样写。对 let 先使用再声明的都是在耍流氓(面试官最喜欢刷流氓了)
感谢 @寸志 @胡子大哈 @Code Hz 的补充。本文相关概念确实都没有明肯定义,大部分是语言设计者造的概念,因此语言使用者理解起来有差别也是正常的
出现各类误解的症结也正是由于这些概念名没有明确的定义。有兴趣的话能够看看 GitHub 上的讨论:let
hoisting? · Issue #767 · getify/You-Dont-Know-JS
我会在下一篇文章中详细说明什么是变量的生命周期,而后部分推翻我本身这篇文章的结论(打脸?)。(对啊,只记结论是没有用的)
加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦
每日一题,每周资源推荐,精彩博客推荐,工做、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送