美文网首页前端乱弹99日
前端乱弹99日之理解变量提升的另外一个角度

前端乱弹99日之理解变量提升的另外一个角度

作者: 业余马拉松选手 | 来源:发表于2018-01-31 01:08 被阅读5次

    提到变量提升,估计能在网上搜到“海量”的文章,那么我这里再次重提这个“老”话题,其实还是想更深入理解JavaScript两个概念:全局变量和执行环境(Execution Context)。
    废话少说,还是先看段代码

    b();
    var a;
    b();
    console.log(a);
    a = "What a wonderful day!";
    b();
    console.log(a);
    function b(){
        console.log("in b:"+a);
    }
    

    这段代码,你觉得会输出什么样的结果呢?
    嗯,可以先自己想一下,然后再试一下。
    结果如下:

    in b:undefined
    in b:undefined
    undefined
    What a wonderful day!
    in b:What a wonderful day!
    

    或许你会觉得这个结果有点出人意料?那么我们现在就来分析下吧。
    在分析前,我们首先要先提到一个概念:JavaScript的全局对象(变量)
    我们先写一个最最简单的html页面如下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>前端乱弹99日第一日之变量提升</title>
    </head>
    <body>
        
    </body>
    </html>
    

    然后用Chrome浏览器打开这个页面,同时打开开发者模式(Mac的快捷键为option+command+i),选择其中的console选项卡,具体如下图:

    这个页面中,我们一行JavaScript代码都没写,但Chrome浏览器的JavaScript引擎却帮我们创建好了一个JavaScript的执行环境的同时,以及一个非常重要全局对象window。

    你可以通过输入window,来查看:


    window这个对象可以在这个JavaScript的执行环境中进行访问,我们就可以称它为全局变量,那么在JavaScript里,全局变量有个很好理解的办法,所有不在函数里定义的,就都能被访问到,也就都可以被称为全局变量,比如,我们在上面的代码上,创建一个变量a,具体代码如下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>前端乱弹99日第一日之变量提升</title>
        <script type="text/javascript">
            var a = "What a wonderful Day";
        </script>
    </head>
    <body>
        
    </body>
    </html>
    

    这时,我们就能在window这个对象里找到a这个变量:


    同时,我们也可以直接访问a这个变量


    现在,我们已经对JavaScript的全局变量有了一些直接的认识下,那么我们再回过来看下,JavaScript的引擎在构建执行环境都需要做些什么:
    1、先要创建执行环境的全局变量,比如在浏览器的window对象,如果是node服务里的global对象
    2、为全局变量和函数分配空间
    3、依次按照顺序执行文件里的每一行代码。

    关于第二点,我们可以先做这样一个实验,把代码改下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>前端乱弹99日第一日之变量提升</title>
        <script type="text/javascript">
            console.log(a);
        </script>
    </head>
    <body>
        
    </body>
    </html>
    

    当我们未定义,直接使用一个变量的时候,是直接会抛出异常的,如下图


    image.png

    接着,我们再把代码改一下:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>前端乱弹99日第一日之变量提升</title>
        <script type="text/javascript">
            console.log(a);
                    var a;
        </script>
    </head>
    <body>
        
    </body>
    </html>
    

    这时,执行的结果如下:


    这里要特别注意,这个a is not defined和undefined是不同含义的,前者是在JavaScript执行环境中根本没有a这个变量,而后者是有a这个变量,但a并没有值。

    而如果你之前有写过Java或是C之类的程序的话,都会觉得这样先使用后定义的方式很反人类,同时也是会直接无法编译,当然我们实际写程序的时候,也需要避免这样的写法,而这里仅是为了讨论清楚问题。

    在有一些讲变量提升的文章中提到,JavaScript的解释器,会先把var a这句话提到程序的最前面,而a确实是undefined,用这样的方式来解释变量提升。结果上是没问题的,但实际上还是有点差异的,实际JavaScript引擎在构建JavaScript执行环境的时候,会优先找到全局的变量,先为他们分配空间,也会去找所有的function,为他们分配好空间。

    也就是说,在我们自己写的程序一行一行解释执行前,全局变量和函数的内存都已经分配好了。这样我们再回过头来看最初的那段程序:

    b();
    var a;
    b();
    console.log(a);
    a = "What a wonderful day!";
    b();
    console.log(a);
    function b(){
        console.log("in b:"+a);
    }
    

    因为b这个函数已经在内存中,可以正常调用,而在b中访问全局变量a的时候,a这个变量并没有指向任何值,所以是undefined,直到执行a = "What a wonderful day!";这句话之后,a这个变量才有了指向的值。

    如果能很好的理解JavaScript的执行环境构建顺序,用来理解类似的问题,应该就不是难事儿了吧,当然这类问题似乎也更多的会出现在面试中?作为一个业余前端,实际开发中也还真没有遇到类似的问题,如果今后有遇到,我再回来补吧。

    最后奉上一个我理解的JavaScript执行环境的构建示意图:


    作为一个业余前端,才疏学浅,如果文中如果有任何地方错漏,欢迎留言~如果有误导的地方,也敬请谅解

    顺便说下,明天准备“弹”的话题是函数调用栈。

    相关文章

      网友评论

        本文标题:前端乱弹99日之理解变量提升的另外一个角度

        本文链接:https://www.haomeiwen.com/subject/bibhzxtx.html