閉包 (Closure)

閉包範例與解釋

閉包是函式被建立時所在的環境,環境由任意的局域變數所組成,這些變數是由在閉包建立的時間點上存在於作用域裡的所有變數,特殊的環境變數--本來以為應該消失,可是卻依然存在的變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function addFn() {
let num = 10

function ClosureAddFn(newNum) {
num = num + newNum
return num
}
//函示內 return 函示就會變成 『閉包』
return ClosureAddFn
}
const useClosure = addFn()

console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function addFn() {
let num = 10

function ClosureAddFn(newNum) {
num = num + newNum
return num
}
//函示內 return 函示就會變成 『閉包』
return ClosureAddFn
}
const useClosure = addFn()
console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40

const useClosure2 = addFn()
console.log(useClosure2(100)) //110
console.log(useClosure2(100)) //210

參考資料

  • 閉包(Closure)的細節:共用與非共用變數
  • 閉包(Closure)的細節(二):模擬私有變數
  • 閉包(Closure)的細節(三):閉包與記憶體洩漏
  • 閉包(Closure)的細節(四):閉包效能與prototype

共用與非共用變數

一般函式在執行完畢時,內部的區域變數會被釋放掉,但如果在執行時產生了閉包。那閉包會保留其所在巢狀函式內的變數。讓他不至於被系統釋放掉。現在有一個問題是,閉包保留下來的變數,會與其他閉包共用嗎?這其實取決於閉包產生的位置與時間點。

同一個呼叫期間內的閉包共享變數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function closuretest(){
x=10;
function changex(cx){
x=cx;
}
function showx(){
console.log(x)
}
f1= changex;
f2= showx;
}
closuretest();
console.log(f2())//顯示10
console.log(f1(5))
console.log(f2())//顯示5

不同呼叫期間的閉包各自擁有變數

1
2
3
4
5
6
7
8
9
10
function closuretest(ox){
var x=ox;
return function(){
console.log(x)
}
}
var f1=closuretest(5);
var f2=closuretest(10);
console.log(f1());//顯示5
console.log(f2());//顯示10

閉包效能與prototype

閉包有效能上的影響,主要是反映在處理速度與記憶體的占用。
以下誤用的的寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPlayer(name,race,sex) {
this.name = name.toString();
this.race = race.toString();
this.sex = sex.toString();
this.getName = function() {
return this.name;
};
this.getRace = function() {
return this.race;
};
this.getSex = function() {
return this.sex;
};
}

不同呼叫期間的閉包各自擁有變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPlayer(name,race,sex) {
this.name = name.toString();
this.race = race.toString();
this.sex = sex.toString();
}
MyPlayer.prototype.getName = function() {
return this.name;
};
MyPlayer.prototype.getRace = function() {
return this.race;
};
MyPlayer.prototype.getSex = function() {
return this.sex;
};

利用原型可以讓物件共享方法,這比上面有效率的多。
這範例說明了prototype與閉包的使用時機,不過嚴格來說上面兩種做法都有點不切實際,因為其實變數本身就是公開屬性了,在為其增加公開方法其實沒必要。比較常的做法是用閉包來模擬並且存取私有變數。用prototype來實作通用方法以及存取通用的私有變數。