請聽題:
var c = 1;
function c(c) {
console.log(c);
var c = 3;
}
c(2);
複製程式碼
執行結果:
// 直接給你報錯
TypeError: c is not a function
複製程式碼
解析:
-
第一個坑(預處理):
js語言本身具有預處理機制,js引擎在預處理期對所有宣告的變數和函式進行處理,就是先把變數進行宣告並讀到記憶體裡。也就是收集用var宣告的變數資訊和函式宣告資訊。變數和函式的優先順序:先變數後函式。當變數名和函式名一致時後者會覆蓋前者,來個小?:
function b() { }; var b ; console.log(typeof b) // function 複製程式碼
看啥看,就是
function
服不服? 接著再來個小?:function b() { }; var b = 1 ; console.log(typeof b) // number 複製程式碼
-
第二個坑(作用域域預處理):
js中存在兩種作用域,
全域性作用域
和函式作用域
, 預處理過程中的變數和函式提升都是在作用域上下文內進行。廢話不多說,再來吃個?:var a = 2 function fn() { console.log(a) var a = 3 } fn() // undefined 複製程式碼
為什麼是
undefined
呢? 全域性作用域和fn
函式作用域內變數、函式分別進行提升。當fn
呼叫時,外層全域性作用域的變數a
值未2
但是這並沒什麼卵用,因為在fn
的函式作用域內也宣告瞭一個變數a
,函式作用域內的變數a
也會進行提升,當console.log(a)
語句執行的時候,fn
作用域內的a
變數宣告但未賦值,所以輸出undefined
。 -
回到本文?: 當本文中的例子
預解析
各種提升
完成以後,實際執行時的環境是這樣的:var c ; c = function (c) { console.log(c); var c = 3; }; c = 1 ; c(2); // TypeError: c is not a function 複製程式碼
補充:
在
es6+
中,還存在塊作用域
,結合let
、const
等關鍵字進行變數的生命能夠避免很多問題,在本文中不再進一步討論,有興趣的同學可以去查一下。話又說回來,如果你沒在ES6 前踩過這些坑,其實很難理解ES6 為什麼會那麼搞。