JS程序的解析過程分為編譯和執行兩個階段。
編譯也叫做JS預處理,編譯器將JS腳本代碼轉換成字節碼,執行期間,解釋器借助執行期環境將字節碼生成機械碼並按順序執行。
預編譯
JS是解釋型語言而非編譯型語言,所以代碼在執行期才被解析器一行一行地動態編譯和執行而非在執行之前完成編譯。JS邊編譯邊執行。
JS引擎在預編譯期對所有聲明的變量和函數進行處理。所以JS解釋器執行以下腳本的時候不會報錯。
alert(a); //返回undefined
var a = 1;
alert(a); //返回值1
//由於變量的聲明在預編譯期被處理,所以在執行期對所有代碼來說都是可見的。
//而變量的初始化過程發生在執行期而非預編譯期,執行期JS解釋器按照代碼順序進行解釋執行,在第一行代碼處a尚未被初始化賦值,
//所以JS解釋器會使用默認值undefined
同理,在函數聲明之前調用函數也是合法的。
f(); //返回值1
function f() {
alert(1);
}
但是按照下面方式聲明函數,JS解釋器會報語法錯誤。
f(); //返回語法錯誤
var f = function() {
alert(1);
}
雖然變量和函數的聲明可以放在文檔的任意位置,但是良好的編程習慣應該是在JS代碼之前聲明全局變量和函數並為全局變量初始化賦值。
在函數內部也應該先聲明變量然后再引用。
JS代碼是按塊被引擎預編譯和解釋執行的,所謂塊就是<script>標簽分割的代碼段。下面兩個<script>標簽分別代表兩個代碼塊。
<script>
var a = 1;
</script>
<script>
function f() {
alert(a);
}
</script>
由於JS按塊執行,在一個JS塊中調用后面的塊中聲明的變量或者函數會報語法錯誤。
例
<script>
alert(a); //提示語法錯誤,變量a沒定義,對象f找不到
f();
</script>
<script>
var a = 1;
function f() {
alert(1);
}
</script>
雖說JS按塊執行,但是不同的塊都屬於同一個全局作用域,即塊之間的變量和函數是可以共享的。
我們可以借助事件機制改變JS執行順序。
當文檔流完全被加載完畢,再次訪問就不會出現語法錯誤,比如將訪問第二個代碼塊中的變量和函數的代碼放在頁面初始化事件函數中,就不會提示語法錯誤。、
<script>
window.onload = function() { //頁面初始化(加載)完畢之后才執行頁面初始化事件處理函數
alert(a);
f();
}
</script>
<script>
var a = 1;
function f() {
alert(a+a);
}
</script>
//除了頁面初始化事件外,還可以通過各種交互事件如鼠標事件、鍵盤事件、時鍾觸發等改變JS代碼執行順序。