你的位置:首页 > Java教程

[Java教程]JS的预编译和执行顺序 详析(及全局与局部变量)

最近在复习javascript的事件处理时发现了一个问题,于是总结一下:javascript的预编译和执行顺序的问题:

 
<html> <head>  <title>事件处理</title>  <meta http-equiv="content-type" content="text/html;charset=utf-8"/>  <script type='text/javascript'>    //页面在在完成加载后    window.onload=function(){      var input=document.getElementById('button');      var p=document.getElementById('p');      var i=1;      while(input){        input.onclick=function(){          p.innerHTML+='<br />('+ i +') '+this.nodeName;        }        i++;        input=input.parentNode;      }    }  </script> </head> <body>  <div>  <input type='button' value='Event事件' id='button' />  <p id='p'>事件捕获的顺序:</p>  </div> </body></html>

当我更改了代码中红色的部分后得到的结果又不相同:


<html> <head>  <title>事件处理</title>  <meta http-equiv="content-type" content="text/html;charset=utf-8"/>  <script type='text/javascript'>    //页面在在完成加载后    window.onload=function(){      var input=document.getElementById('button');      var p=document.getElementById('p');      var i=1;      while(input){        input.onclick=function(){          p.innerHTML+='<br />('+ i++ +') '+this.nodeName;        }        input=input.parentNode;      }    }  </script> </head> <body>  <div>  <input type='button' value='Event事件' id='button' />  <p id='p'>事件捕获的顺序:</p>  </div> </body></html>

于是,得出这两种不同的结果那是因为javascript代码在运行时有预编译和执行两个阶段,在预编译阶段会对函数和变量进行处理,对所有的声明变量会赋值为underfined,对所有的声明函数也会赋值为函数的定义。
 
 
下面我们来测试javascript的执行过程
 
1.javascript代码执行顺序时按照脚本标签<script>出现的顺序来确定的,浏览下面页面你会发现代码是按从上到下的顺序执行的
<script type='text/javascript'>  alert('one'); </script> <script type='text/javascript'> alert('two'); </script> <script type='text/javascript'> alert('three'); </script>

 

2. 因为变量在预编译时被赋予一个undefined初值,所以下面代码中,第一个变量name在代码中没有被赋值,所有就延用undefined这个值,下面的name被赋予了Jude,所以第二次输出的是Jude这个字符。
<script type='text/javascript'>  alert(name);          //显示undefined  var name='Jude';  alert(name);          //显示Jude</script>
  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>

 


  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>

 


  6.javascript在预编译阶段是以函数来划分作用域的,然后再通过var 声明的变量来与声明函数开辟内存空间,对var变量赋初值undefined。在执行阶段再根据作用域来嘴变量进行赋值.
 
  第一个代码块中函数里面的变量a是局部变量,因为a在函数内重新用var定义,所以输出undefined,而变量b是全局变量,因为在函数内没有用var重新声明b,所以在给变量b赋值时到全局变量中找全局变量b的值,所以输出的是b.
 
  第二个代码块中的函数内都重新声明了变量a和b,所以他们都是函数内的局部变量,所以都输出undefined。
 

<script type='text/javascript'>  var a='a';  var b='b';  function test(){    alert(a);        //显示undefined    alert(b);        //显示b    var a='test';  }  test();</script><script type='text/javascript'>  var a='a';            var b='b';            function test(){    alert(a);        //显示undefined    alert(b);        //显示undefined    var a='test';    var b='test';  }  test();</script>

 


  综上所述,javascript在执行时的步骤是:
 
    1、先读入第一段代码块
 
    2、对代码块进行语法分析,如果出现语法错误,直接执行第5步骤
 
    3、对var变量和function定义的函数进行“预编译处理”(赋值式函数是不会进行预编译处理的)
 
    4、执行代码块,有错则报错
 
    5、如果还有下一段代码块,则读入下一段代码块,重复步骤2
 
    6、结束
 
 然后,再说说全局变量和局部变量。直接看代码:
<script type="text/javascript">  var a = "Hello";  function test(){    var a;    alert(a);    a = "World";    alert(a);  } </script> 

 

<script type="text/javascript">  var a = "Hello";  function test(){    alert(a);    a = "World";    alert(a);  } </script> 

 



这个就是全局变量跟局部变量的scope问题吗?我说:"当全局变量跟局部变量重名时,局部变量的scope会覆盖掉全局变量的 scope,当离开局部变量的scope后,又重回到全局变量的scope。所以两段代码运行的结果分别为:1) undefined World 2) Hello World。然后我随意编了如下一个例子给她:

 
  <script>     var a =1;     function test(){      alert(a);      var a = 2;      alert(a);     }     test();     alert(a);   </script> 

 


        大家猜结果等于多少?是输出1 2 1 吗?嗯嗯,当我把测试case发给她之前也是这么认为的,但测试输出后……运行结果是 undefined 2 1。当时百思不得其解,问了谷老师才知道,我对JS还不是非常了解,所以痛下苦功,学习+测试,总结如下:        一、Javascript的变量的scope是根据方法块来划分的(也就是说以function的一对大括号{ }来划分)。切记,是function块,而for、while、if块并不是作用域的划分标准,可以看看以下几个例子:

 

 
  <script>   function test2(){     alert ("before for scope:"+i);  // i未赋值(并不是未声明!使用未声明的变量或函数全抛出致命错误而中断脚本执行)                                // 此时i的值是underfined     for(var i=0;i<3;i++){       alert("in for scope:"+i); // i的值是 0、1、2, 当i为3时跳出循环     }     alert("after for scope:"+i); // i的值是3,注意,此时已经在for scope以外,但i的值仍然保留为3          while(true){       var j = 1;       break;     }     alert(j);  // j的值是1,注意,此时已经在while scope以外,但j的值仍然保留为1        if(true){       var k = 1;     }     alert(k); //k的值是1,注意,此时已经在if scope以外,但k的值仍然保留为1   }      test2();   //若在此时(function scope之外)再输出只存在于test2 这个function scope里的 i、j、k变量会发生神马效果呢?   alert(i); //error! 没错,是error,原因是变量i未声明(并不是未赋值,区分test2函数的第一行输出),导致脚本错误,程序到此结束!   alert("这行打印还会输出吗?"); //未执行   alert(j); //未执行   alert(k); //未执行   </script> 

 


        二、Javascript在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定实变量的作用域。怎么理解呢?看下面一个例子:

  <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> 

 


        三,当全局变量跟局部变量重名时,局部变量的scope会覆盖掉全局变量的scope,当离开局部变量的scope后,又重回到全局变量的scope,而 当全局变量遇上局部变量时,怎样使用全局变量呢?用window.globalVariableName。

 
    <script>       var a =1;       function test(){           alert(window.a); //a为1,这里的a是全局变量哦!         var a=2;   //局部变量a在这行定义         alert(a); //a为2,这里的a是局部变量哦!       }       test();       alert(a); //a为1,这里并不在function scope内,a的值为全局变量的值     </script>