Lexical Scope
認識Closure前,要先知道 Lexical Scope!
Lexical Scope 定義函式與其可使用的變數範圍
若fn2包在fn1內,fn2可以使用fn1的變數,這就是fn2的Lexical Scope。
子層可以拿父層,父層無法拿子層。
Lexical Scope範例:
// global scope
let x = 1;
const parentFunction = ()=>{
//local scope
let y = 2;
console.log(x);
console.log(y);
const childFunction = ()=>{
console.log(x += 5);
console.log(y += 1);
}
childFunction(); //6,3
}
parentFunction();// 1,2
可以說,childFunction在parentFunction中有Closure,因為ChildFunction可以拿取外層(也就是parentFunction)的變數,但是Lexical Scope不是代表Closure,只算是Closure中重要的一部份。
Closure
Closure 在我們定義函式時就已經出現,不是在執行時才出現。
前面說 Lexical Scope 是Closure重要的一部分,那另一部份呢?
Closure: 一個函式可以取得父層的scope,即使父層已經執行結束。
如何讓子層函式可以繼續取得父層scope,就算父層已經結束執行呢?
我們可以修改上一個範例:
將原本執行childFunction的地方,改為回傳 childFunction本身。
// global scope
let x = 1;
const parentFunction = ()=>{
//local scope
let y = 2;
console.log(x);
console.log(y);
const childFunction = ()=>{
console.log(x += 5);
console.log(y += 1);
}
return childFunction;
}
const result = parentFunction();//1,2
console.log(result);
//()=>{console.log(x += 5);console.log(y += 1);}
result(); //6,3
result(); //11,4
console.log(x); //11
console.log(y); //reference error, private variable
Closure 和 IIFE
const increment = (()=>{
let counter = 0;
console.log(`counter ${counter}`);
const credit = (num) => console.log(`I have ${num} credit(s).`);
return ()=>{conter++; credit(counter)}
})();
// counter 0
increment(); // I have 1 credit(s).
increment(); // I have 2 credit(s).
Closure 、 IIFE 和 參數
const credits = ((num)=>{
let credits = num;
console.log(`initial credits value:` ${credits});
return ()=>{
credit -= `;
if(credits > 0){
console.log(`playing game, ${credits} credits remaining`)
};
if(credits <= 0){
console.log(`not enough credits`)
}
}
})(3); // initial credits value: 3
cresits() // playing game,2 credits remaining
cresits() // playing game,1 credits remaining
cresits() // not enough credits