zoukankan      html  css  js  c++  java
  • 详解js运行机制(1)-- 预解析

      一、问题的提出

      我们都知道,js是一个解释型的语言,js代码在运行时,是按照js在文档中出现的先后次序,依次逐条语句执行的。那么问题来了。我们看下面这个小例子

    <script type="text/javascript">
        f1();
        function f1(){
            console.log('执行了函数f1');
        }
    </script>

      这个程序能正确执行吗?

      如果按上面的理论,这个f1函数的调用出现在了声明的前面,显然当执行调用的语句时,js还没看到f1声明的部分,应该报错才对。但事实恰恰相反,这个程序是能正常执行的。这就牵涉到js程序执行的一个机制,叫做预解析。

      二、预解析定义

      其实js代码在执行过程分为两步,第一步叫预解析,第二步才是真正执行。

      所谓预解析,就是:在当前作用域中,JavaScript代码执行之前,浏览器首先会默认的把所有带var和function声明的变量进行提前的声明或者定义。

      对这句话的解析:

      1.何谓当前作用域,就是你声明的变量和函数其所在的作用域,在上例中,f1函数是声明在全局作用域下的,所以声明f1的当前作用域就是全局作用域。

      2.预解析不仅提升函数声明

      3.预解析还会提升变量声明,也谓之变量提升

      三、函数提升

      js执行环境会先扫描当前作用域中所有var声明的变量和function声明的函数,然后把他们提升到当前作用域的顶端。

      上例在运行时,f1的声明虽然是在调用语句之后,但其实js执行环境会对原始的代码做个预解析,解析过后的结果就变成下面这个样子

    <script type="text/javascript">
        function f1(){
            console.log('执行了函数f1');
        }
        f1();
    </script>

      差别很明显,一目了然,不解释。这样的代码当然能正常执行。

      同理,形如

    <script type="text/javascript">
        f1();
        f2();
        function f1(){
            console.log('执行了函数f1');
        }
        function f2(){
            console.log('执行了函数f2');
        }
    </script>

      这里声明的2个函数f1和f2都会被提升,提升过后的结果是这样的:

    <script type="text/javascript">
        function f1(){
            console.log('执行了函数f1');
        }
        function f2(){
            console.log('执行了函数f2');
        }
        f1();
        f2();
    </script>

      四、变量提升

      先看这个例子

    <script type="text/javascript">
        console.log(num);
    </script>

      这个代码执行时会报错:,因为num没有声明。

      如果改成下面这个样子:

    <script type="text/javascript">
        console.log(num);
        var num = 10;
    </script>

      结果是输出:undefined。意外吗?

      原因是js执行环境对var num = 10;这条声明变量的语句做了提升,它等价于  

    <script type="text/javascript">
        var num;
        console.log(num);
        num = 10;
    </script>

      这就不难理解为啥输出的是undefined了。

      再看一个经典的面试题:

    <script type="text/javascript">
        var num = 10;
        function f1(){
            console.log(num);
            var num = 20;
        }
        f1();
    </script>

      这个例子输入的不是10,也不是20,正确答案是:undefined。

      原因:此题代码等价与

    <script type="text/javascript">
        var num = 10;
        function f1(){
            var num;
            console.log(num);
            num = 20;
        }
        f1();
    </script>    

      在全局作用域下声明了一个变量num,在函数f1的作用域内声明了局部变量num,2个变量同名,那么在函数内部,起作用的是局部变量num,此时,后写的声明变量的语句var num = 20;被提升,当然它不会被提升到全局作用域,它只会被提升到声明这个局部变量的当前作用域的顶端,也就变成了等价代码的样子,所以输出就是undefined。

      结论:

      1.函数提升是整体提升

      2.变量提升是只提升声明,不提升赋值

      五、需要注意的地方

      (1)函数表达式不会被提升

      下面的例子执行时会报错

    <script type="text/javascript">
        f1();
        var f1 = function(){
            console.log('函数表达式');
        }
    </script>

      错误如下:。错误提示是f1不是一个函数。

      为什么这里的f1没有被提升呢?因为f1是个函数表达式。

      如果深究,f1也被提升了,但是它没有被当成函数提升,而是当成变量了。因为这个时候f1是通过var声明的变量,只不过它指向了一个函数对象。所以它提升过后的结果等价于如下带代码

    <script type="text/javascript">
        var f1;
        f1();
        f1 = function(){
            console.log('函数表达式');
        }
    </script>

      这里f1被当成变量提升的,而f1又没有赋初值,所以f1在调用的时候还是undefined,那当然会报错了。

       (2)预解析不会跨越<script>代码块。函数和变量的提升,只在自己所在的<script>块之内提升。

  • 相关阅读:
    Java RMI 使用例子
    Hessian 使用例子
    Spring 使用javaconfig配置aop
    Spring 使用xml配置aop
    Spring Junit集成测试
    Spring 使用javaconfig配置
    Spring @Autowired注解用在集合上面,可以保持接口的所有实现类
    在linux上搭建nexus私服(CentOS7)
    PostgresSQL使用Copy命令能大大提高数据导入速度
    PHP curl get post请求
  • 原文地址:https://www.cnblogs.com/ldq678/p/9758757.html
Copyright © 2011-2022 走看看