1、什麼是預解析? 在當前作用域下,JS 運行之前,會把帶有 var 和 function 關鍵字的事先聲明,併在記憶體中安排好。(這個過程也可以理解為變數提升)然後再從上到下執行 JS 語句(預解析只會發生在通過 var 定義的變數和 function 上) 2、var 聲明的變數 使用 var 聲 ...
1、什麼是預解析?
在當前作用域下,JS 運行之前,會把帶有 var 和 function 關鍵字的事先聲明,併在記憶體中安排好。(這個過程也可以理解為變數提升)然後再從上到下執行 JS 語句(預解析只會發生在通過 var 定義的變數和 function 上)
2、var 聲明的變數
使用 var 聲明的變數預解析:告訴解析器知道有這個名字的存在並預設將該變數賦值 undefined ,如下:
console.log(x); //undefined var x = 5;
變數 x 雖然是在 console.log 後面定義的,但使用 var 申明的 x 會提前保存在記憶體中,並賦值 undefined ,然後再從上往下執行 JS 語句 。它的執行順序類似於下麵的結構:
var x; console.log(x); //undefined x = 5;
先聲明瞭 x,x 沒有定義所以賦值為 undefined ,輸出的結果自然為 undefined,然後再給 x 賦值為 5
需註意的是,如果變數聲明沒有使用 var,則不存在變數提升。如下:
console.log(x); //error: x is not defined x = 5;
x 沒有使用 var 聲明,所以報錯找不到 x
3、functin 聲明的函數
使用 function 聲明函數的預解析:先告訴解析器這個函數名的存在,然後在告訴解析器這個函數名的函數體是什麼,如下:
console.log(f); function f() { console.log("123"); }
聲明函數會把整個函數都提升到最前面 ,所以瀏覽器中結果會輸出整個函數,結果如下:
function f() { console.log("123"); }
如果在一個函數作用域中聲明一個變數 ,那麼它也會提升到函數作用域的最上面,如下:
var x = 5; function f() { console.log(x); // undefined var x = 2; } f();
以上雖然全局作用域聲明瞭一個變數 x,但函數里也聲明瞭一個變數 x,所以會先查找函數裡面是否有變數 x,如果有就不會在全局下查找了。函數裡面的變數 x 會被提升到函數作用域的最前面 ,並且賦值為 undefined,所以輸出結果為 undefined ,類似於如下結構:
var x = 5; function f() { var x; console.log(x); // undefined x = 2; } f();
函數的參數也可以理解為函數作用域的變數 ,如下:
var x = 5; function f(x) { console.log(x); // undefined } f(); console.log(x); // 5
為函數 f 傳遞一個形參 x,由於函數在調用時沒有傳遞實參(也就是說變數 x 沒有賦值),所以為 undefined 。而在全局下輸出 x 自然在全局下查找變數 x ,結果為 5
4、函數優先
變數聲明和函數聲明都會被提升,如果同一個作用域下聲明的兩個相同變數或相同函數,後一個會覆蓋前一個,如下:
var x = 5; var x = 10; console.log(x); // 10 function f() { console.log("xx"); } function f() { console.log("yy"); } f(); // yy
但有一個需要註意的細節,如果聲明的變數與函數名相同 ,那又會怎麼覆蓋呢?可以看如下例子:
var f = 5; function f() { console.log("xx"); } f(); // error: f is not a function
JavaScript 中,函數的預解析優先順序是要高於變數的預解析的。無論函數在什麼位置聲明,都優選把整個函數提升到最前面。所以上面的例子中,雖然函數 f 是在變數 f 下麵定義的,但是在預解析時先解析函數 f,然後再解析變數 f,後面的變數 f 會把前面的函數 f 覆蓋,最後 f 為 5 為數值類型,所以調用 f 時報錯,f 不是一個函數。
需要註意的是 ,如果變數 f 定義後沒有賦值 ,那麼函數 f 就不會被覆蓋了,如下:
var f; function f() { console.log("xx"); } f(); // xx
所以函數提升優先於變數提升,函數提升會把整個函數挪到作用域頂部,變數提升只會把聲明挪到作用域頂部
掌握以上知識,我們看下麵的例子 :
console.log(x); // function x() {console.log(5);} var x = 2; console.log(x); // 2 function x() { console.log(3); } console.log(x); // 2 var x = 3; console.log(x); // 3 function x() { console.log(5); } console.log(x); // 3 x(); // error: x is not a function
以上例子,兩個函數 x 優先提升,所以第二個函數 x 覆蓋第一個函數 x,然後兩個變數 x 提升,由於變數 x 提升後為 undefined,所以第二個函數沒有被覆蓋,第一個輸出 x 結果為第二個函數 function x(){console.log(5);}
隨後 x 被賦值為 2 ,所以第二個輸出 x 結果為 2
因為第一個函數 x 已經被提升到前面去了,所以第三個輸出 x 結果還是 2
隨後為 x 賦值為 3,所以第四,第五輸出 x 結果為 3。最後調用 x,x 因為是數值類型,所以會報錯 x 不是一個函數
我的博客也會同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=e1ebv5s48l8j