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

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

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

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

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

    作者回复: 对的,你的理解没错

    函数只会在第一次执行的时候被编译,所以编译时变量环境和词法环境最顶层数据已经确定了。

    当执行到块级作用域的时候,块级作用域中通过let和const申明的变量会被追加到词法环境中,当这个块执行结束之后,追加到词法作用域的内容又会销毁掉。

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

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

     3
     5
  • 晓小东
    2019-08-25
    在ES3开始,try /catch 分句结构中也具有块作用域。补充……

    作者回复: 赞

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

    作者回复: 使用let/const声明的变量,伴随着词法环境被创建,但只有在变量的词法绑定(LexicalBinding)已经被求值运算后,才能够被访问。

    你也可以在let b声明之前断点下,看看scope中的值有没有,你会看scope中的值已经存在了。

     5
     4
  • …Lucky
    2019-09-04
    老师,按照最后的思考题。let,const会在编译阶段创建,但不赋值。但是上面几个图中都是直接赋值的undefined。这是否矛盾
    ?
     2
     3
  • 爱吃锅巴的沐泡
    2019-09-01
    对文中foo()函数的分析和一些问题:
               我调试了一下,①断点打在 let b = 2,此时的scope中只有local:a = 1,b = undefined,c = undefined;并没有block,这应该说明js是解释性语言,一句一执行的。
               ②当断点走到 let b = 3时,这时进入了作用域,scope中有了block:b = undefined,d = undefined,这应该说明在进入作用域之前AST已经生成,并确定了作用域的范围。
               问题:1、老师提到在进入作用域时let声明的变量被创建,结合断点可以证明,那么是不是说 let声明的变量在该作用域内提升了,但没有提升赋值语句?因为在②处已经有了d = undefined。
               问题:2、把foo()中的作用域变形如下:
                               {
                                      let b = 3
                                      console.log(d)
                                      var c = 4
                                      let d = 5
                                      console.log(a)
                                      console.log(b)
                                 }
                       当断点走到 let b = 3处,scope的block中只有b = undefined,并没有d = undefined,是因为“暂时性死区”是js在语法上的设置,防止访问声明前的变量,而在进入作用域之前就会有语法树的生成,所以在编译到console.log(d)时,遇到错误,所以没有在词法环境中创建变量d。这样分析是否正确?
    展开
     1
     3
  • 朙
    2019-08-24
    if(0){ var myname = " 极客邦 "} 这段代码里的if条件是false很有意思。是说编译阶段不管if会不会执行。里面的代码都会编译,因此这里的myname变量提升,从而导致上面的console.log(myname)输出undefined吗?
    另外let 声明的变量会提升吗?

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

    
     2
  • sugar
    2020-01-31
    想问一下,js代码中这种写法:{ var a =
     100; },和不加外边这层大括号,有什么区别吗?我看到vue的源码中有很多这样的用法,却没明白目的是啥
    
     1
  • 爱吃锅巴的沐泡
    2020-01-14
    老师您好,对于Y s留言用户的问题,我想接着问: ES5标准文档中规定,
             执行环境包括:词法环境、变量环境、this绑定。
             其中执行环境的词法环境和变量环境组件始终为词法环境对象。当创建一个执行环境时,其词法环境组件和变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程中,变量环境组件永远不变,而词法环境组件有可能改变。

    问题1:变量环境组件永远不变,而词法环境组件有可能改变。
              这里您给出的解释是说词法环境里会有块级作用域的进入和退出,但这是ES5的规范呀,还没有作用域的概念呀,这里不解?
              变量环境组件为什么永久不变?

    问题2:创建执行环境时,变量环境和词法环境最初是同一个值,想知道这个值具体是指什么值?

    问题3:我理解的ES5中 变量环境中存储的是提升的变量和函数声明(都是类似var xx=undefined; function funname(){}),所以变量环境是不变的,在执行过程中变量的变化是在词法环境中体现的,词法环境管理着静态作用域的。 到了ES6,有了let和const,是不是就把原来词法环境中变化的变量转移到了变量环境中,把let和const的变化放到了现在的词法环境中。

    希望老师能举个例子或者画个图,详细分析一下在ES5中变量环境和词法环境中变量的变化?
    ES5和ES6的执行环境的区别是啥?

    问题有点多,但是网上写的和规范内容都不一样,感觉不靠谱,请老师解答!
    展开
    
     1
  • Kyeon
    2019-09-17
    老师好,看到您在评论中说let并不会变量提升。那么,课后题浏览器报Cannot access 'myname' before initialization,看起来像是引擎已经知道有myname这么个变量了。但是如果按照let变量不会提升的话,那引擎是怎么知道myname这个变量尚未初始化的呢?谢谢~
     1
     1
  • 无名
    2019-08-30
    1<script type="text/javascript">
    2    let myname = "outer name";
    3    {
    4        let myage = 10;
    5        let myclazz = "1(4)班";
    6        console.log(myname);
    7        let myname = 'inner name';
    8    }
    9</script>

    老师,对于以上代码,我有些疑惑:
    1、断点到2的位置时:
        我的理解:myname 应该在【词法环境】中创建了【Script区】,里面:myname=undefined
        实际上:没有看到myname=undefined,执行完2时,才在右边的【Script区】中显示myname="outer name";

    2、断点到4的位置时:
        我的理解:myage、myclazz、myname应该在【词法环境】中创建了【Block区】,里面:myage=undefined、myclazz=undefined、myname=undefined;然后执行完4时,myage=10;然后执行完5时,myclazz = "1(4)班";然后执行到6时,报Cannot access 'myname' before initialization (原因是【暂时性死区】);
        实际上:在【词法环境】中是创建了【Block区】,但只看到了myage=undefined、myclazz=undefined;没有看到myname=undefined。
    展开
    
     1
  • 蓝配鸡
    2019-08-24
    let myname= '极客时间'
    {
      console.log(myname)
      let myname= '极客邦'
    }

    编译过程:
    生成执行上下文压入栈
    变量环境为空
    词法环境中myname=undefined压入栈

    执行过程:
    词法环境中myname=极客时间
    新开一个 myname =undefined 压入词法环境栈
    查找myname并输出undefined
    赋值当前栈头上myname=极客邦
    pop栈头

    结束

    展开

    作者回复: 还要考虑暂时性死区的问题,这个我在文中没介绍,可以自行搜索下。可以参考下其它评论。

     2
     1
  • 爱吃锅巴的沐泡
    2019-08-24
    有个疑问:
    在思考题中,
    1、执行到console.log(myname)这句话时,编译阶段已经完成,那么词法环境中的栈顶 是不是已经有了该作用域块了,let myname =‘极客邦’ 是不是也已经在栈顶的作用域快中了?
    2、执行到console.log(myname)这句话时,是按着从词法环境栈顶到栈底到变量环境的顺序查找,栈底已经存在了函数级的 let myname了,那为什么还是会报错呢?

    作者回复: 暂时性死区,你可以参考下其它评论

    
     1
  • mfist
    2019-08-24
    1. 在块级作用域中,从{开始到let myname= '极客邦' 代码之间会形成一个暂时性死区,如果中间去访问变量myname,会报初始化之前不能访问myname的错误。Uncaught ReferenceError

    2. 另外上面的一个foo函数也会报d没有定义吧,d在块级作用域中声明,在外面是访问不到的
    function foo(){
        var a = 1
        let b = 2
        {
          let b = 3
          var c = 4
          let d = 5
          console.log(a)
          console.log(b)
        }
        console.log(b)
        console.log(c)
        console.log(d)
    }
    foo()
    展开

    作者回复: 对的,你的分析没问题,这两行都会报错

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

    作者回复: 变量初始化和创建再上上一节《变量提升:JavaScript代码是按顺序执行的吗?》中已经讲过了,
    我们将到了一个变量编译阶段和执行阶段分别要做那些事情。

    这一节主要是将var和let的区别以及底层实现机制的,我看你的疑问是下面这个问题:
    function test(){
        console.log(a)
            let a = 7;
    }
    test()

    执行test的时候,编译阶段a已经在内存中,为什么提前访问不了?

    这主要是因为V8虚拟机做了限制,虽然a在内存中,但是当你在let a 之前访问a时,根据ECMAScript定义,虚拟机会阻止的访问!

    如果你还有其它具体的问题,欢迎继续提出!

    
    
  • 玉皇大亮
    2020-01-09
    打印错误: Uncaught ReferenceError: Cannot access 'myname' before initialization,说明在大括号{}内的块级作用域中,myName变量的声明是会被提升的,但是同var不同的是,仅仅是声明提升了,但是赋值没有提升,所以没有初始化
    
    
  • 不二
    2019-12-31
    通过var 声明的变量,在编译阶段,会存储在变量环境中,此时值为undefined,然后访问的时候,值为undefined。
    通过let声明的变量,在编译阶段,会存储在词法环境中,此时值其实也为undefined, 只不过js隐藏此时不允许访问,提示 “暂时性死区”的错误。
    例如:
    console.log(a); //结果为undefined
    console.log(b); // Uncaught ReferenceError: Cannot access 'b' before initialization
    var a = 1;
    let b = 1;
    展开
    
    
  • -_-_aaa
    2019-12-29
    文中”通过 let 声明的变量,在编译阶段会被存放到词法环境(Lexical Environment)中。
    在函数的作用域内部,通过 let 声明的变量并没有被存放到词法环境中。”,第二段”通过 let 声明的变量并没有被存放到词法环境中”,可是上图中画的声明的变量b已经放到词法环境中了,只不过没有赋值,所以是不是写错了,应该是没有赋值,而不是没有放到词法环境中。
    
    
我们在线,来聊聊吧