javascript运行过程中的“预编译阶段”和“执行阶段”

来源:互联网 发布:webview 加载js 阻塞 编辑:程序博客网 时间:2024/06/08 08:32

javascript相对于其它语言来说是一种弱类型的语言,在其它如java语言中,程序的执行需要有编译的阶段

而在javascript中也有类似的“预编译阶段”(javascript的预编译是以代码块为范围,即每遇到一个代码块都会进行 预编译>执行),

了解javascript引擎的执行机理,将有助于在写js代码过程中的思路总结

首先科普下javascript中的两种声明方式,var和function,前者声明的是变量,后者声明的是方法

在预编译中,javascript对这两种声明做出了两种处理方案

<script>  var a = "1";    //声明变量a   function b(){    //声明方法b        alert();  }  var c = function(){    //声明变量c    alert();  }</script>

以上代码块中,a、c为变量赋值,b为函数声明,当执行以上的代码时,

首先会进入预编译阶段,

对与变量赋值a、c会在内存中开辟一块内存空间并指向变量名,且赋值为undefined

对于函数声明,则同样会进行开辟内存空间,但此时会直接将函数体进行处理,即用函数声明方式,则在预编译阶段便已完成了函数的创建工作

预编译阶段:(PS:不管代码中声明变量和声明函数的顺序如何,在预编译阶段会先声明变量,再声明函数,但是变量初始化过程发生在执行期,而不是预编译期

<script>    var a = undefined;    var c = undefined;        var b = function(){      alert();    }</script>

执行阶段:

<script>    a = "1";    c = function(){      alert();    }</script>

整体执行步骤:

<script>    var a = undefined;    var c = undefined;        var b = function(){      alert();    }    a = "1";    c = function(){      alert();    }</script>

题目:

   <script>          var a =1;          function test(){              alert(a); //a为undefined! 这个a并不是全局变量,这是因为在function scope里已经声明了(函数体倒数第4行)一个重名的局部变量,                           //所以全局变量a被覆盖了,这说明了Javascript在执行前会对整个脚本文件的定义部分做完整分析,所以在函数test()执行前,                           //函数体中的变量a就被指向内部的局部变量.而不是指向外部的全局变量. 但这时a只有声明,还没赋值,所以输出undefined。              a=4                     alert(a);  //a为4,没悬念了吧? 这里的a还是局部变量哦!              var a;     //局部变量a在这行声明              alert(a);  //a还是为4,这是因为之前已把4赋给a了          }          test();          alert(a); //a为1,这里并不在function scope内,a的值为全局变量的值      </script> 

PS:相对与window环境下的变量、函数声明,每一个作用域都会对其下的变量和函数进行先声明

<script>    function Hello() {          alert("Hello");      }      Hello();      function Hello() {          alert("Hello World");      }      Hello();</script>

我们会看到这样的结果:连续输出了两次Hello World。而非我们想象中的Hello和Hello World。

我们会看到这样的结果:连续输出了两次Hello World。而非我们想象中的Hello和Hello World。
这是因为Javascript并非完全的按顺序解释执行,而是在解释之前会对Javascript进行一次“预编译”,在预编译的过程中,会把定义式的函数优先执行,也会把所有var变量创建,默认值为undefined,以提高程序的执行效率。也就是说上面的一段代码其实被JS引擎预编译为这样的形式

<script>    function Hello() {          alert("Hello");      }      function Hello() {          alert("Hello World");      }         Hello();       Hello();</script>

我们可以通过上面的代码很清晰地看到,其实函数也是数据,也是变量,我们也可以对“函数“进行赋值(重赋值)。

当JavaScript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理。
做如下处理:
1. 在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义
2. 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = …这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)
所以,就会出现当JavaScript解释器执行下面脚本时不会报错:

    alert(a);    // 返回值undefined       var a =1;       alert(a);    // 返回值1 

由于变量声明是在预编译期被处理的,所以在执行期间对于所有代码来说,都是可见的。但是,你也会看到,执行上面代码,提示的值是undefined,而不是1。这是因为,变量初始化过程发生在执行期,而不是预编译期。在执行期,JavaScript解释器是按着代码先后顺序进行解析的,如果在前面代码行中没有为变量赋值,则JavaScript解释器会使用默认值undefined。由于在第二行中为变量a赋值了,所以在第三行代码中会提示变量a的值为1,而不是undefined

3.从如下结果中我们知道先是连续两次输出Hello Wrold!,最后连续两次输出test,得出这样的结果是因为javascript并非是完全按照顺序执行的,而是在执行之前先进行一个预编译,预编译 时声明式函数被提取出来,优先执行,而且相同的函数会进行覆盖,再执行赋值式函数。

<script type='text/javascript'>    test();                    //输出Hello World!    function test(){               alert('hello');     //声明式函数    }    test();                    //输出Hello World!    var test=function(){    //赋值式函数        alert('test');    }    test();                    //输出test    function test(){      //声明式函数        alert('Hello World!');    }    test();                    //输出test</script>

实际执行顺序:

<script type='text/javascript'>    var test=undefinedfunction test(){               alert('hello');     //声明式函数    }    function test(){      //声明式函数        alert('Hello World!');    }    test();                    //输出Hello World!    test();                    //输出Hello World!    var test=function(){    //赋值式函数        alert('test');    }    test();                    //输出test    test();                    //输出test</script>

4.下面代码显示显示hello,再显示hello world!,这是因为javascript中的给个代码块是相互独立的,当脚本遇到第一个script标签时,则javascript 解析器会等这个代码块加载完成后,先对它进行预编译,然后再执行之,然后javascript解析器准备解析下一个代码块,由于javascript是按 块执行的,所有一个javascript调用下一个块的函数或者变量时,会出现错误

<script type='text/javascript'>    function test(){        alert('hello');                //显示hello    }    test()</script><script type='text/javascript'>    function test(){        alert('hello world!');        //显示hello world!    }    test()</script>

5.虽然javascript是按块执行的,但不同的块却属于相同的全局作用域,不同的块的变量和函数式可以相互使用的,也就是某个块可以使用前面块的变量和函数,却不可以使用它之后的块的变量和函数

<script type='text/javascript'>    alert(name);                    //显示undefined    var name='Jude';    function test(){        alert('hello');    }    fun();                            //不能调用下一个块的函数</script><script type='text/javascript'>    alert(name);           //可以调用上一个块的变量,显示Jude    test();                //可以调用上一个块的函数,显示hello    function fun(){        alert('fun');    }</script>

  综上所述,javascript在执行时的步骤是:

    1、先读入第一段代码块

    2、对代码块进行语法分析,如果出现语法错误,直接执行第5步骤

    3、对var变量和function定义的函数进行“预编译处理”(赋值式函数是不会进行预编译处理的)

    4、执行代码块,有错则报错

    5、如果还有下一段代码块,则读入下一段代码块,重复步骤2

    6、结束
更多信息可查看博客:http://www.cnblogs.com/RunForLove/p/4629510.html

原创粉丝点击