【五分鐘學前端】快速上手JavaScript的閉包Closure

布魯斯前端
7 min readMar 27, 2021

--

前言

在現實生活中,閉包closure的可以說是無所不在,或許你現在已經很頻繁的使用它,但你自己卻不知道而已。

今天這篇文章,希望能讓大家簡單理解閉包Closure,所以不會有其他太複雜的內容,讓大家快速上手!

來快速上手囉Yeah!

什麼是閉包Closure? 主要用途是什麼

閉包這個詞,顧名思義就是「封閉」的一個包。

但封閉的主要用途是什麼呢?就是:

讓每個功能相同的function,對應到每個獨立的環境變數,且互不相干擾。

看不懂?沒關係,就讓我們來舉些例子!

銀行帳戶

我們用銀行帳戶來舉例,如果今天A用戶的銀行帳戶程式碼長這樣:

// ----銀行帳戶程式碼 Part1----// A的存款有1000元
let balance = 1000
// 共用的提款卡功能
function withdraw (amount) {
balance = balance - amount
}
// 用戶A提100元
withdraw(100)
// 用戶B提200元
withdraw(200)

A:乾!我的錢怎麼被B提走了?

因為 withdraw function是共用的,所以B也能提走A的錢,但這邏輯肯定是行不通的。

沒人會想讓別人提走自己的錢,所以讓我們來改一下程式碼:

// ----銀行帳戶程式碼 Part2----// A的存款有1000元
let balanceA = 1000
// B的存款有2000元
let balanceB = 2000
// A專屬的提款卡功能
function withdrawA (amount) {
balanceA = balanceA - amount
}
// B專屬的提款卡功能
function withdrawB (amount) {
balanceB = balanceB - amount
}
// 用戶A提100元
withdrawA(100)
// 用戶B提200元
withdrawB(200)

A:耶!把錢Bang回來~

改成這樣的寫法,我們把兩個帳戶(環境)區分開來,讓他們各自提各自的錢,這樣看上去挺完美的…結案!

但我們忽略了一個很重要的問題:

一般的銀行不會只有兩個用戶

所以如果我們幫每個用戶都寫了一份「專屬」的程式碼,那肯定會由臭又長很崩潰…

// ----銀行帳戶程式碼 Part3----// A的存款有1000元
let balanceA = 1000
// B的存款有2000元
let balanceB = 2000
// C的存款有2000元
let balanceC = 2000
// D的存款有2000元
let balanceD = 2000
...
...
..
// A專屬的提款卡功能
function withdrawA (amount) {
balanceA = balanceA - amount
}
// B專屬的提款卡功能
function withdrawB (amount) {
balanceB = balanceB - amount
}
// C專屬的提款卡功能
function withdrawC (amount) {
balanceC = balanceC - amount
}
// D專屬的提款卡功能
function withdrawD (amount) {
balanceD = balanceD - amount
}
....
...

這時候,我們需要一個方法,既可以快速建立帳戶,又可以讓每個帳戶的環境互不相干擾,也就是建立每個「封閉」的帳戶給用戶們。

這時候,我們的救星,閉包Closure登場了!

閉包Closure,一人一戶

我們再次改寫我們的程式碼,這次我們建立一個「提款功能製造機」來幫助我們生產許多帳戶:

// ----銀行帳戶程式碼 Part4----// 帳戶製造機
function accountCreator(initBalance, user) {
let balance = initBalance
// 製造成功後,返回提款功能
return function (amount) {
balance = balance - amount
console.log(user, balance)
}
}
// 快速建立多個帳戶!
const withdrawA = accountCreator(1000, 'A')
const withdrawB = accountCreator(2000, 'B')
const withdrawC = accountCreator(3000, 'C')
const withdrawD = accountCreator(4000, 'D')
// 結果
withdrawA(100) // A 900
withdrawB(200) // B 1800
withdrawC(300) // C 2700
withdrawD(400) // D 3600

上面的邏輯很簡單,我們建立了一個提款功能製造機。

但看起來好像每次調用提款功能都會吃到「一樣」的balance? 錯!

實際上,每次調用這個製造機,我們都是建立一個全新的balance封閉在 accountCreator 裡頭,然後「生產」一個全新的提款function連結對應balance。

所以調用這個function製造提款功能的時候,都會建立每個人「專屬的balance」跟「專屬的提款function」。

對A B C D用戶來說,他們等於是拿到了各自專屬的提款卡,然後對應到各自專屬的餘額(balance變數)。

這就是閉包closure帶給我們的方便性:

讓每個功能相同的function,對應到每個獨立的環境變數,且互不相干擾。

我們再來看看一些現實上的例子。

現實範例,Redux的createStore

我們拿Redux的createStore來當作閉包的例子!這裡是原始碼,大家有興趣可以去看看,我們下面就用簡化版的來講解就好。

有學過Redux的人一定都知道,store本身裡包含了許多功能,這些功能其實也用到了閉包closure的原理來做封裝!

// 簡化版function createStore () {
...
...

function dispatch() {...}
function getState() {...}
...
...
// 關鍵:建立了store物件,然後返回。
const store = { dispatch, getState ... }
// 然後return,dispatch、getState等等功能都被封裝
return store
}

從上面的程式碼來看,如果你今天要建立多個store(雖然應該不會有人這樣做),每個store的環境都會是獨立且分開來的,這也是閉包closure的一個實際例子!

結尾

以上就是對閉包closure的基本教學,雖然沒有涵蓋到更多複雜的東西,但相信足以應付大部分的開發情況。

讓我們再來複習一次~

讓每個功能相同的function,對應到每個獨立的環境變數,且互不相干擾。

希望這篇文章能讓大家快速上手閉包closure!

想持續關注我的話

  • 去Medium追蹤給他按下去~!
  • Facebook粉專按按讚~!
  • Youtube頻道訂閱,每週三都直播聊聊大家想問的問題~!
  • 加入我創的Line社群,群組目前有1300多人,可以一起進來交流分享經驗跟技術。
  • Line社群點我加入:https://reurl.cc/V3WjGZ

--

--