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>  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM