浏览器工作原理与实践
李兵
前盛大创新院高级研究员
56402 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 46 讲
浏览器工作原理与实践
15
15
1.0x
00:00/00:00
登录|注册

09 | 块级作用域:var缺陷以及为什么要引入let和const?

思考时间
总结
JavaScript的支持
ES6的解决方案
变量提升
块级作用域
JavaScript中的块级作用域和变量提升

该思维导图由 AI 生成,仅供参考

在前面《07 | 变量提升:JavaScript 代码是按顺序执行的吗?》这篇文章中,我们已经讲解了 JavaScript 中变量提升的相关内容,正是由于 JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷
虽然 ECMAScript6(以下简称 ES6)已经通过引入块级作用域并配合 let、const 关键字,来避开了这种设计缺陷,但是由于 JavaScript 需要保持向下兼容,所以变量提升在相当长一段时间内还会继续存在。这也加大了你理解概念的难度,因为既要理解新的机制,又要理解变量提升这套机制,关键这两套机制还是同时运行在“一套”系统中的。
但如果抛开 JavaScript 的底层去理解这些,那么你大概率会很难深入理解其概念。俗话说,“断病要断因,治病要治根”,所以为了便于你更好地理解和学习,今天我们这篇文章会先“探病因”——分析为什么在 JavaScript 中会存在变量提升,以及变量提升所带来的问题;然后再来“开药方”——介绍如何通过块级作用域并配合 let 和 const 关键字来修复这种缺陷。

作用域(scope)

为什么 JavaScript 中会存在变量提升这个特性,而其他语言似乎都没有这个特性呢?要讲清楚这个问题,我们就得先从作用域讲起。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

ES6解决了JavaScript中存在的变量提升设计缺陷,引入了块级作用域和let、const关键字。文章通过分析变量提升问题,介绍了ES6的解决方案。通过示例代码展示了let和const的用法,以及ES6如何通过块级作用域解决变量提升问题。进一步讨论了JavaScript引擎是如何同时支持变量提升和块级作用域的,通过对变量环境和词法环境的介绍,解释了JavaScript引擎的工作机制。最后,总结了语言本身的重要性,强调了语言的价值在于为开发者创造价值。文章内容深入浅出,帮助读者快速了解ES6解决变量提升问题的原理和作用。文章通过具体的示例和深入的技术讨论,帮助读者理解了ES6的解决方案,以及JavaScript引擎的工作机制,为读者提供了深入的技术知识和实际应用价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《浏览器工作原理与实践》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(113)

  • 最新
  • 精选
  • coolseaman
    【最终打印结果】:VM6277:3 Uncaught ReferenceError: Cannot access 'myname' before initialization 【分析原因】:在块作用域内,let声明的变量被提升,但变量只是创建被提升,初始化并没有被提升,在初始化之前使用变量,就会形成一个暂时性死区。 【拓展】 var的创建和初始化被提升,赋值不会被提升。 let的创建被提升,初始化和赋值不会被提升。 function的创建、初始化和赋值均会被提升。

    作者回复: 很好,这个答案大家可以参考下

    2019-08-24
    37
    545
  • 这篇真的是神作啊。 有一个疑问,在abcd那个例子里,第一步<编译并创建执行上下文>的图里并没有块级作用域的b=undefined; d=undefined。而在第二步里<继续执行代码>的图中才出现b=undefined; d=undefined。想问下这个块级作用域的b=undefined; d=undefined是不是应该在第一步的编译阶段里就有。还是说在执行阶段像函数那样,块级作用域会有一个自己的编译阶段

    作者回复: 执行函数时才有进行编译,抽象语法树(AST)在进入函数阶段就生成了,并且函数内部作用域是已经明确了,所以进入块级作用域不会有编译过程,只不过通过let或者const声明的变量会在进入块级作用域的时被创建,但是在该变量没有赋值之前,引用该变量JavaScript引擎会抛出错误---这就是“暂时性死区”

    2019-08-24
    11
    72
  • YBB
    有个问题,在一个块级作用域中,let和const声明的变量是在编译阶段被压入栈中还是执行阶段被压入栈中?在文中的表述来看,第一个let声明的变量是在编译阶段就压入栈中的,但是后面的变量又感觉是在执行是压入栈中,有点混乱。

    作者回复: 对的,你的理解没错 函数只会在第一次执行的时候被编译,所以编译时变量环境和词法环境最顶层数据已经确定了。 当执行到块级作用域的时候,块级作用域中通过let和const申明的变量会被追加到词法环境中,当这个块执行结束之后,追加到词法作用域的内容又会销毁掉。

    2019-08-26
    4
    64
  • Tim
    看得很生气,全篇文章不提变量的「创建」「初始化」「赋值」这三种区别,把创建和初始化揉在一起了,也是看了精选留言里第一条评论之后Google才查找到,否则刚开始我真的不理解为啥都已经在词法环境找到了变量却报错了!按照这种理论的话,是否说明词法环境只有变量,并没有等于undefined? 真的不需要更新一下吗?????

    作者回复: 变量初始化和创建再上上一节《变量提升:JavaScript代码是按顺序执行的吗?》中已经讲过了, 我们将到了一个变量编译阶段和执行阶段分别要做那些事情。 这一节主要是将var和let的区别以及底层实现机制的,我看你的疑问是下面这个问题: function test(){ console.log(a) let a = 7; } test() 执行test的时候,编译阶段a已经在内存中,为什么提前访问不了? 这主要是因为V8虚拟机做了限制,虽然a在内存中,但是当你在let a 之前访问a时,根据ECMAScript定义,虚拟机会阻止的访问! 如果你还有其它具体的问题,欢迎继续提出!

    2020-01-12
    11
    55
  • 晓小东
    在ES3开始,try /catch 分句结构中也具有块作用域。补充……

    作者回复: 赞

    2019-08-25
    3
    27
  • 李懂
    执行上下文是在编译时创建的,在执行代码的时候已经有词法环境了,而且变量已经默认初始化了undefiend,为什么还会存在暂时性死区,老师解答一下!

    作者回复: 暂时性死去是语法规定的,也就是说虽然通过let声明的变量已经在词法环境中了,但是在没有赋值之前,访问该变量JavaScript引擎就会抛出一个错误。

    2019-08-24
    7
    18
  • William
    第二步,继续执行代码。 这张图我觉得有错误,当进入foo函数内部的代码块之后,并没有了编译阶段,此时,新创建的栈顶块级作用域的内容为空,而并没有 b = undefined 和 d = undefined 两项内容。 执行完 let b = 3 之后,分配内存,块级作用域出现 b = 3 一项。 执行 let d = 5 之后,为d分配内存,栈顶块级作用域增加一项 d = 5。

    作者回复: 使用let/const声明的变量,伴随着词法环境被创建,但只有在变量的词法绑定(LexicalBinding)已经被求值运算后,才能够被访问。 你也可以在let b声明之前断点下,看看scope中的值有没有,你会看scope中的值已经存在了。

    2019-08-24
    8
    10
  • 小锅锅
    老师,听你对比了c语言,既然let const存在暂时性死区,那么c语言的变量也存在同样的暂时性死区报错吗?

    作者回复: c语言都没这概念,因为你在定义之前使用一个变量,首先过不了c语言的编译

    2019-08-25
    7
  • if(0){ var myname = " 极客邦 "} 这段代码里的if条件是false很有意思。是说编译阶段不管if会不会执行。里面的代码都会编译,因此这里的myname变量提升,从而导致上面的console.log(myname)输出undefined吗? 另外let 声明的变量会提升吗?

    作者回复: 对的,第一个分析的没问题 第二个let不会产生变量提升

    2019-08-24
    2
    7
  • Geek_East
    我想,理解execution context和scope的区别是理解这个问题的一个关键;很多时候执行上下文和作用域都混着说

    作者回复: 这是两样不同的东西,一个表示一个表示函数运行时的上下文,一个表示词法作用域! 我会在下篇介绍V8的专栏中详细分析这块内容!

    2019-12-06
    1
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部