為什麼 javascript 函式宣告會提升 而 函式表示式不會
Js程式碼分為兩個階段:編譯階段和執行階段
我們習慣將var a = 2;看作一個宣告,而實際上JavaScript引擎並不這麼認為。它將var a和a = 2當作兩個單獨的宣告,第一個是編譯階段的任務,而第二個則是執行階段的任務。
這意味著無論作用域中的宣告出現在什麼地方,都將在程式碼本身被執行前首先進行處理。可以將這個過程形象地想象成所有的宣告(變數和函式)都會被“移動”到各自作用域的最頂端,這個過程被稱為提升。
宣告本身會被提升,而包括函式表示式的賦值在內的賦值操作並不會提升。
變數宣告
Js編譯器會把變數宣告看成兩個部分分別是宣告操作(var a)和賦值操作(a=2)
宣告操作在編譯階段進行,宣告操作會被提升到執行環境的頂部,值是undefined(表示未初始化)
賦值操作會被留在原地等待執行階段
var a = 2;
function foo() {
console.log(a); //undefined
var a = 10;
console.log(a); //10
}
foo();
// 相當於
var a = 2;
function foo() {
var a;
console.log(a); //undefined
a = 10;
console.log(a); //10
}
foo();
定義函式有兩種方式:函式宣告和函式表示式
函式宣告提升會在編譯階段把宣告和函式體整體都提前到執行環境頂部,所以我們可以在函式宣告之前呼叫這個函式
函式表示式,其實就是變數宣告的一種,宣告操作會被提升到執行環境頂部,並賦值undefined。賦值操作被留在原地等到執行。
// 函式宣告
foo(); //100
function foo(){
console.log(100);
}
// 函式表示式
baz(); // TypeError: baz is not a function
var baz = function(){
console.log(200);
}
//相當於
var baz;
baz();
baz = function() {
console.log(200);
};
控制語句
Js中使用函式級作用域,不存在塊級作用域。所有普通塊中的宣告都會被提升到頂部,所以控制語句對宣告的控制就顯得完全沒有效果
if (false) {
var a = 10;
}
console.log(a); //undefined
// 相當於
var a;
if (false) {
a = 10;
}
console.log(a) //undefined
函式優先
提升操作會優先進行函式的宣告
函式會首先被提升然後才是變數,重複的變數宣告會被忽略,只剩下賦值操作,多個函式宣告可以進行覆蓋
宣告的順序是這樣的:
1. 找到所有的函式宣告,初始化函式體,如有同名的函式則會進行覆蓋
2. 查詢變數宣告,初始化為undefined,如果已經存在同名的變數,就什麼也不做直接略過
// 1
foo(); //200
function foo() {
console.log(100);
}
function foo() {
console.log(200);
}
// 2
console.log(foo); //function foo(){...}
function foo(){
console.log(200);
}
var foo = 100;
一個普通塊內部的函式宣告通常會被提升到所在作用域的頂部,這個過程不會像下面的程式碼暗示的那樣可以被條件判斷所控制:
foo(); // "b"
var a = true;
if (a) {
function foo() { console.log("a"); }
} else {
function foo() { console.log("b"); }
}
但是需要注意這個行為並不可靠,在JavaScript未來的版本中有可能發生改變,因此應該儘可能避免在塊內部宣告函式。