今天想跟大家分享lexical scope
跟closure
。
JavaScript是一個lexical scope language
(語彙範疇),意思是JS的變數作用域是在函數還沒執行前就決定好了。當我們執行一段程式時,JS引擎會先把整段程式碼看過一次,決定哪些函數可以使用哪些變數,接著才執行函數呼叫。
代表著區塊間的包裹關係,被包裹在內層的區塊可以保護自己的變數不被外層取用,相反的外層區塊的變數還是可以被內層區塊使用。
function Counter() {
let count = 0
return ++count;
}
console.log(Counter()); //1
console.log(Counter()); //1
console.log(Counter()); //1
有沒有發現這個結果不是我們想要的會往上遞增!因為count
是區域變數你每一次呼叫就重新被宣告為0一次。
let count = 0
function Counter() {
return ++count;
}
console.log(Counter()); //1
console.log(Counter()); //2
console.log(Counter()); //3
這個結果就可以達成我們要的目的因為這樣count
是一個全域變數。但這會有一個問題存在!
如果今天你是跟同事一起寫程式碼的話你很難保證這個變數命名不會重複。這時候我們就會用一種叫做Closure
的寫法。
我們就會把上方的程式碼改寫成這樣
function counter() {
let count = 0;
function innerCounter() {
return ++count;
}
return innerCounter;
}
let counterFun = counter();
console.log(counterFun()); //1
console.log(counterFun()); //2
console.log(counterFun()); //3
這樣子我們的count就不會是全域物件,就不會受到其他影響。
假設我們想在加一個計數器
function counter() {
let count = 0;
function innerCounter() {
return ++count;
}
return innerCounter;
}
let counterFun = counter();
let counterFun2 = counter();
console.log(counterFun()); //1
console.log(counterFun2()); //1
console.log(counterFun()); //2
console.log(counterFun()); //3
console.log(counterFun2()); //2
console.log(counterFun2()); //3
這樣可以隨便你想怎麼調用這個計數器都不會受到影響。那如果我在外層在定義一個count
let count = 1;
function counter() {
let count = 0;
function innerCounter() {
return ++count;
}
return innerCounter;
}
let counterFun = counter();
let counterFun2 = counter();
console.log(counterFun());
console.log(counterFun2());
console.log(counterFun());
console.log(counterFun());
console.log(counterFun2());
console.log(counterFun2());
結果還是一樣,其實範圍鏈是在函式被定義的當下決定的,不是你呼叫的時候才去做定義。
順帶一提到如果在這個函式內找不到這個變數怎麼辦?
其實這不用擔心,他會在往外層去找看看是否有定義這個變數
所以如果今天不在counter
這個函式內定義count
變數的話結果會不一樣。
let count = 0;
function counter() {
function innerCounter() {
return ++count;
}
return innerCounter;
}
let counterFun = counter();
let counterFun2 = counter();
console.log(counterFun()); //2
console.log(counterFun2()); //3
console.log(counterFun()); //4
console.log(counterFun()); //5
console.log(counterFun2()); //6
console.log(counterFun2()); //7
閉包
的優點是變數僅存在於函式之中,如果匯出的方法沒有提供原始值,將無法用任何方式取得原始變數,如果需要刻意隱藏變數值,避免用戶透過其他工具或方式取得,閉包也會是一個好方法。
重新認識 JavaScript: Day 19 閉包 Closure
閉包,原來這就是閉包啊!