---
# System prepended metadata

title: JavaScript 閉包（closure）
tags: [六角JS 直播班 - 2021 秋季班]

---

###### tags: `六角JS 直播班 - 2021 秋季班`

JavaScript 閉包（closure）
===


閉包（Closure）是函式以及該函式被宣告時所在的作用域環境（lexical environment）的組合。（取自MDN）

有點饒口，簡單來說，**閉包就是function內包著一個function，看到一個function內 return 了另一個 function，通常都是閉包的應用！**
<br>

## 為什麼需要用到閉包？

**先來看一個例子！**

如果今天需要寫一個計算水果攤上蘋果數量的函式，我們直覺上會這樣寫：
```javascript=
let appleNum = 0;

function appleCounter() {
    appleNum+=1;
    console.log(`有${appleNum}顆蘋果`);
}

appleCounter() //1
appleCounter() //2
```

看起來沒有問題，但如果今天除了要計算蘋果，還要計算橘子呢？那我們可能會這樣寫：
```javascript=
//蘋果計算器
let appleNum = 0;

function appleCounter() {
    appleNum+=1;
    console.log(`有${appleNum}顆蘋果`);
}

//橘子計算器
let orangeNum = 0;

function orangeCounter() {
    orangeNum+=1;
    console.log(`有${orangeNum}顆橘子`);
}

appleCounter() //有1顆蘋果
appleCounter() //有2顆蘋果
orangeCounter()//有1顆橘子 
orangeCounter()//有2顆橘子 
appleCounter()//有3顆蘋果 
```
看起來也沒問題，但如果今天水果攤上有100種水果呢？

這種寫法的話，不同的水果不能共用一個變數，會互相干擾，因此要為每種水果宣告一個獨一無二的變數，程式碼會變得冗長也會寫得很崩潰～
<br>


### 這時候就需要透過「閉包」來解決這個問題！
**「內層的變數不會被釋放，可以被重複使用，但又不會被外層讀取，因此就不會出現互相干擾的問題！」**

```javascript=
//蘋果計算器
function appleCounter() {
    let num = 0
    function countApple() {
       num+=1
       console.log(`有${num}顆蘋果`);
    }
    return countApple;
}


//橘子計算器
function orangeCounter() {
    let num = 0
    function countOrange() {
       num+=1
       console.log(`有${num}顆橘子`);
    }
    return countOrange; //把這個函式回傳出來，讓我們可以在外層使用它
}

const appleResult = appleCounter()
const orangeResult = orangeCounter()

appleResult()//有1顆蘋果
appleResult()//有2顆蘋果
orangeResult()//有1顆橘子
orangeResult()//有2顆橘子
```

可以看到兩個函式中的變數num因為在不同的環境，所以不會互相干擾！

**在外層宣告變數appleResult並呼叫appleCounter()，事實上會執行的是appleCounter()中的countApple()這一個function!**

這個寫法我覺得有點美中不足，好像還是很冗長，它甚至可以再更精簡一點：
```javascript=
function fruitCounter(name) {
    let num = 0
    
    //將countApple跟countOrange改成匿名函式
    return function () {
        num++
        console.log(`有${num}顆${name}`);
    }
}

const appleResult = fruitCounter("蘋果")
const orangeResult = fruitCounter("橘子")

appleResult() //有1顆蘋果
appleResult() //有2顆蘋果
orangeResult() //有1顆橘子
orangeResult() //有2顆橘子
```
我們只需要寫一個水果計算函示，帶入不同的參數即可計算每一種水果，不必再為了每種水果去寫一個專屬的函示，是不是簡潔又省事多了～～～
<br>
閉包是個很重要又有點不好懂的概念，如果有看到更多範例會再整理上來，感謝看到這邊😻
<br>

>**參考資料：**
[為什麼我們需要閉包(Closure)？它是冷知識還是真有用途？
](https://nissentech.org/why-do-we-need-closure/)[[JS] 深入淺出 JavaScript 閉包（closure）](https://pjchender.dev/javascript/js-closure/)