JavaScript不是解釋型語言嗎?為什麼會有“編譯時間”與“運行時”之分?
基礎知識
看接下來的內容之前,有幾個地方需要先瞭解。
JavaScript中的“未聲明”與"未定義"
看看下面兩段代碼
代碼1.1
<script type="text/javascript">
alert(a);
</script>
代碼1.2
<script type="text/javascript">
var a;
alert(a);
</script>
第一段代碼在各個瀏覽器中都會提示出錯。出錯資訊是“未定義”或者“not definded"
第二段代碼不會提示出錯,對話方塊顯示"undifinded"
並且我們如果對變數a使用typeof函數進行檢測,會得到同樣的結果"undefined"。似乎除了是否報錯這一點外,兩者都犯了同樣的錯誤就是變數"未定義"。而實際上,這是不一樣的。在我看來,前者與後者應該是類似“未聲明”與“未定義”的關係。
如果一個變數沒有經過顯示聲明或者出現指派陳述式(可以看作隱式聲明),那麼它就是未聲明的,這個時候我們使用它就會報錯“未定義”或者“not definded",實際上可以理解為“未聲明”。
如果變數經過顯示聲明但是沒有執行過指派陳述式,那麼它是“未定義”的,也就是確實意義上的"undefined"。我們可以使用它,但是它的預設值是"undefined"。
這一點上,我不知道JavaScript的標準文檔中是否有闡明,但在我看來,這是有點混淆的。
JavaScript的範圍
JavaScript的範圍我在前面的文章提過了。
http://www.cnblogs.com/zhengchuyu/archive/2008/07/22/1248286.html
這裡再簡要提幾個要點:
1. 範圍以函數進行劃分的,而不是由塊(block)劃分的。
2. 使用變數的時候將會從當前範圍開始尋找其“聲明”(隱式或者顯式),如果沒有找到再向上一級範圍尋找。
3. 變數是允許重複定義的,後一個定義將覆蓋前一個定義。
4. 函數內部如果不加關鍵字var而定義的變數,預設為全域變數。
正題:“編譯時間”與“運行時”
首先要說明的是,JavaScript是否存在編譯階段我不清楚,但是從它的某些表現上來看,我將其分成了“編譯時間”與“運行時”兩個階段,這在後面內容的理解中我認為是沒有問題的。
關於“編譯時間”與“運行時”,要從 var 關鍵字與 function 關鍵字說起。從目前我閱讀到的資料和實踐中只發現這兩個關鍵字存在這樣的特殊性。
var
照例先看代碼,我們只需在代碼1.2上改變一下語句順序
代碼2.1.1
<script type="text/javascript">
alert(a);
var a;
</script>
這一段的執行結果與代碼1.2的執行結果是一樣的。不會報錯,視窗提示"undefined"。即我們先前提到的那種“已聲明未定義”的情況。由此,我們可以猜到 var a; 這個語句應該是在第一行語句執行之前就執行了的,就像網上有人提到的“先行編譯”(實際上是不是就不得而知了,待高手指點)。
我們再稍微修改一下代碼:
代碼2.1.2
<script type="text/javascript">
alert(a);
var a = 1;
alert(a);
</script>
第一次仍然是提示"undefined",第二次才顯示了"1"。因此,實際上只是把聲明提前了,但是指派陳述式仍然沒有改變位置,即可以類比為:
代碼2.1.3
<script type="text/javascript">
var a;
alert(a);
a = 1;
alert(a);
</script>
我們之前提到這是var關鍵字的作用,現在我們來驗證一下究竟是不是var關鍵字的作用。在代碼2.1.2上刪去var關鍵字
代碼2.1.4
<script type="text/javascript">
alert(a);
a = 1;
</script>
程式報錯,也就是前文提到的“未聲明”。可以看到沒有使用var關鍵字之後,“先行編譯”的情況並沒有出現。
var關鍵字的這種特性可能會導致我們的一些細節上的錯誤,比如我在之前的文章上提出的一個問題(文中稱“神奇現象”),後來是部落格園上的大蝦們幫我解決了,再次感謝他們呵呵。http://www.cnblogs.com/zhengchuyu/archive/2008/07/22/1248576.html
function
function關鍵字類似的特性貌似要比var關鍵字稍微複雜一點。但是弄明白了 var 關鍵字的作用,對function關鍵字在這上面的表現應該也就不是很難理解了。你將會看到很明顯的兩段時期“編譯時間”與“運行時”。
這次我們不像var關鍵字那樣一步一步來了,直接看一段關鍵代碼
代碼2.2.1
<script type="text/javascript">
func(); //2
func = function() {alert(1);};
func(); //1
function func() {alert(2);};
func(); //1
</script>
執行結果我已經標註在代碼注釋裡了。可以看到,function關鍵字的“先行編譯”與var關鍵字的“先行編譯”稍有不同。它將其“聲明”與“定義”一同“先行編譯”了。代碼2.2.1可以類比為:
代碼2.2.2
<script type="text/javascript">
function func() {alert(2);};
func(); //2
func = function() {alert(1);};
func(); //1
func(); //1
</script>
也就是說,function關鍵字聲明的函數(或者稱為“類”或者“變數”我覺得都是可以的),是在“編譯時間”就執行了。而除了第4行代碼之外,其他代碼都是在“運行時”執行的,所以我們得到注釋裡的顯示結果。
分段編譯
然而JavaScript的“先行編譯”並不是發生在整個頁面的所有指令碼中。
有人提到是按<script></script>標籤“分段編譯”的。在代碼2.2.1後面添加多一個<script>塊
代碼2.3.1
<script type="text/javascript">
func(); //2
func = function() {alert(1);};
func(); //1
function func() {alert(2);};
func(); //1
</script>
<script type="text/javascript">
function func() {alert(2);};
func(); //2
</script>
執行結果看似證明了上述的結論。第一段第4個語句明顯是先於第二段第一個語句執行的。
好像還漏了一些什麼東西,想起來再補上去...