閉包
閉包(Closure)算是面試必考題吧?
當初聽到這個概念的時候也是一頭霧水,
它運用的機制其實不多也不難,只是要先一一理解這些機制,
才會對閉包的運作順序慢慢了解。
靜態作用域
靜態作用域(Lexical Scope)是指程式碼在撰寫時就決定好作用位置,
不論是函式定義或是變數宣告,都是由我們擺放的位置決定它們的作用範圍,
在執行時會依照規則取值:
let a = 0;
function fn() {
let b = "我是在 fn 裡面的 b";
console.log(a); // 0
console.log(b); // '我是在 fn 裡面的 b'
}
fn();
fn
執行時裡面取到的 a 是在外面宣告好的,因為在「撰寫」時就已經確定 a 是外層,
fn
函式定義則是創造了一個內層的作用域,內層宣告的 b 則沒有任何手段可以從外層拿到它。
看起來沒什麼問題,所以我們 可以得知「內層可以取外層,外層不能取內層」。
有了這個結論,我們就不會被下面的範例誤導了:
let message = "Hi 這是外層捏";
function fn1() {
console.log(message);
}
function fn2() {
let message = "Hi 這是內層歐";
fn1();
}
fn2();
執行 fn2
時會印出外層的 message,因為對於 fn1
來說,
它的外層並不是執行階段呼叫它的 fn2
,
所以查找 message
時一定是往定義它的外層去找!
因此把 fn2
內部宣告的 message 註解掉會發現對執行結果沒什麼影響。
如果要把 fn2
的 message 帶過去,只要改寫 fn1
可以帶入的參數即可:
function fn1(message) {
console.log(message);
}
function fn2() {
let message = "Hi 這是內層歐";
fn1(message);
}
fn2();
這也是為什麼初學函式時都會強調盡量定義參數,用帶入參數的方式取值,
因為在不知道靜態作用域的概念時很容易取錯值。