[JS奇怪的世界]No.? 閉包番外篇

前言

首先在前面已經有講過閉包這個東西,但是這個東西自己還是似懂非懂的,所以我將閉包這區塊給挪出來在做一個番外篇紀錄學習。

前文回顧:

‘[JS奇怪的世界]No.38 閉包(一)’

‘[JS奇怪的世界]No.39 閉包(二)’

閉包

首先一樣閉包是什麼?一般來講最簡單的解釋就是一個 function 回傳另一個 function 即為閉包,所以這邊我提供一個簡單範例了解閉包怎麼撰寫 ↓

1
2
3
4
5
6
7
8
var a = 'Window';
function sayHi (){
var a = 'Closure';
console.log('now is Function.');
return function () {
console.log('Hello' + ' ' + a);
}
}

那要執行的方式就是 sayHi(),如果要執行閉包中的 function 就是兩個括號 sayHi()(),這邊會出現兩個 console 是正常的,我故意這麼做的 ↓

Closure

為什麼要寫閉包

那為什麼要學習撰寫 Closure?主要原因有這幾個

  1. 可以限定變數私有
  2. 外層的變數可以被內層的 function 給存取
  3. 內層變數不會被釋放

什麼是內層變數不會被釋放

舉例來講,我們可以用這一篇文章的範例來了解 → 鐵人賽:另一種方式介紹 JavaScript 閉包 然後試著寫一個範例,用數字來做了解。

假設今天小明、小美、小王共同持有 1000 元,然後小明花了 100$,小美花了 50$,而小王花了 700$,所以應該會剩下 150$ 以一般程式來講的話,可能會像這樣撰寫 ↓

1
2
3
4
5
6
7
8
9
10
11
function myMoney(price) {
var money = 1000;
return money = money - price;
}
var count = myMoney; // 計算後將結果儲存在 count 變數中
// 小明花了 100$
count(100);
// 小美花了 50$
count(50);
// 小王花了 700$
count(700);

我們會發現一件事情,結果與我們所想的並不一樣 ↓

function

原因是出在每一次執行 count(); 時,JavaScript 都會重新宣告一個新變數叫 money,並且值是 1000,雖然我們有將回傳的變數給做紀錄,但其實當函數執行完畢後,裡面的變數就跟著被釋放掉,所以如果這是我們所希望的結果那這個答案就沒有錯,可是今天我們的題目是大家共同持有 1000$,各自花了錢之後錢包會剩下 150$,所以這時候就要使用 closure 的技巧來解決這個問題,因為我們需要將變數結果給保留下來。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myMoney() {
var money = 1000;
return function(price) {
return money = money - price;
}
}

var count = myMoney();
// 小明花了 100$
count(100);
// 小美花了 50$
count(50);
// 小王花了 700$
count(700);

closure

我們可以發現結果是我們所想要的,所以這邊讓我們試著解釋一下運作原理。

在前面的範例 ↓

1
2
3
4
5
function myMoney(price) {
var money = 1000;
return money = money - price;
}
var count = myMoney;

JavaScript 再運作之後並沒有建立 function myMoney 給變數 count,所以當每次我執行 count(); 都是在建立新的執行環境,這也是為什麼 money 總是等於 1000$。

那閉包呢?前面有講到

閉包就是一個 function 中回傳 function

所以在閉包的範例 ↓

1
2
3
4
5
6
7
8
function myMoney() {
var money = 1000;
return function(price) {
return money = money - price;
}
}

var count = myMoney();

其實在 JavaScript 創造階段時,function myMoney 就被創造執行環境,並儲存在變數 count 中,所以因為這個樣子 myMoney 裡面的變數 money 被儲存在記憶體中,當每次執行 count() 時也就可以更新變數 mone

所以我們可以利用 closure 多做一點來做更多的應用,舉凡將三個各分發一個悠遊卡儲值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function myMoney(storage) {
var money = storage;
console.log(money);
return function(price) {
return { // 使用物件函數的方式來製作功能查詢及扣除餘額
nowMoney: function () {
return console.log(money);
},
count: function (price) {
if(money < price) return console.log('餘額不足,目前餘額: ' + money + ' $'); // 當 price 大於目前 餘額 money 就回傳錯誤。
if (!money <= 0) { // 當 money 等於 0 或是小於 money 就不進入計算。
return money = money - price;
}
return console.log('餘額扣除失敗,目前餘額: ' + money + ' $');
}
}
}
}
// 小明比較窮只儲值 500$
var ming = myMoney(500);
// 小美暴發戶儲值了 5000$
var mei = myMoney(5000);
// 小王不知道哪裡來的錢,儲值了 30000$
var wang = myMoney(30000);

// 小明連三天都花了 500$
ming().count(100);
ming().count(100);
ming().count(300);
//查詢小明目前餘額
ming().nowMoney();
// 小美花了 2300
mei().count(1600);
mei().count(100);
mei().count(600);
//查詢小美目前餘額
mei().nowMoney();
// 小王只花300
wang().count(300);
// 查詢小王目前餘額
wang().nowMoney();

以上作法參考

透過以上作法我們就可以不停地加入餘額,然後刷卡扣除餘額,所以這邊主要結合了兩篇文章的概念組合而成的閉包分別是: 初次參加保哥的《JavaScript 開發實戰:核心概念篇》感想鐵人賽:另一種方式介紹 JavaScript 閉包

結論

閉包的應用看起來非常的強大,但是真的要多練習與實際應用才會知道該怎麼使用,

參考文件

鐵人賽:另一種方式介紹 JavaScript 閉包

初次參加保哥的《JavaScript 開發實戰:核心概念篇》感想

談談JavaScript中closure的概念 – Part 1

談談JavaScript中closure的概念 – Part 2

談談JavaScript中closure的概念 – Part 3

談談JavaScript中closure的概念 – Part 4

0%