HomeAbout Me

為什麼我們需要閉包(Closure)?它是冷知識還是真有用途?

By Nissen Yeh
Published in Web development
August 28, 2020
1 min read

為什麼我們需要閉包(Closure)?它是冷知識還是真有用途?

  • 本篇適合閱讀者:雖然大概了解閉包的概念,但「不知道為什麼要學閉包?」、「覺得閉包只是冷知識」的人

以下是一個閉包

從開始學 JS 起,我其實覺得閉包(Closure)是一個很奇怪的知識,翻閱各種網路文章,我大概知道了

  • 閉包就是 Function 內的 Function(上面就是兩層function)
  • 閉包內層的變數記憶體不會被釋放,可以被內層函式,但又不會被外面直接讀取(比如 name 就是這樣的變數)

…但又怎麼樣呢?這感覺就是一個JS冷知識。雖然為了面試記住很多閉包的知識,但我卻始終搞不懂「為什麼需要閉包?」

因此這篇文章,我不想深究閉包的細節,主要更想探討的是「為什麼會需要閉包?它的用處或應用情境是什麼?」

從設計按鈕到理解閉包的用處

假設你想要寫一個按鈕<Button>,你會怎麼做呢?

最簡單的方式

首先,你定義了此開關按鈕有「打開/關閉」兩種狀態,那程式碼可能如下:

因此,當按鈕被點擊時

假設想要有兩個按鈕?

假設現在不止一個按鈕,而是想要兩個按鈕,那要怎麼辦呢? 總不能大家都共用「status」一個變數吧?因為這樣會互相干擾,因此可能的寫法就變成如下:

沒錯,我們幫每一個 status 和 clickButton 都加上編號,這樣就不會互相干擾了!

OK,這樣不錯,但假設有100個按鈕呢?

突然的,上面這種寫法就會變得冗余

那就用物件(Object)來管理每個按鈕的狀態吧

從上面的方式我們可以看到這樣的寫法,其實有點冗余

  • 雖然都是 status變數,因為怕互相干擾,所以要取名叫 status_A, status_B, status_C …
  • 雖都是 clickButton() 的函式,卻因為怕互相干擾,要取名叫 clickButtonA, clickButtonB, clickButtonC …

這樣各自命名麻煩,不如直接寫成物件,這樣大家都可以有自己的狀態。

這樣不錯,大家都可以叫做status跟clickButton了,彼此互不干涉!

物件很好,但如果有人不遵從規定好函式亂改狀態呢?

用物件的方式很不錯,但是會有一個問題——就是有人可以不按照規定用clickButton(),就自己去更改按鈕的狀態。比如說

不用規定好的函式操作狀態會怎樣?

  1. 被直接設定狀態,這樣會怎麼樣嗎?

有時候確實不一定會怎樣,然而,很多時候在設計資料結構時,我們會期望大家還是可以照著規範來操作狀態,因為直接操作狀態可能會有許多問題。

比如說,我們就不會希望有人把 status 亂塞值,例如:

或是

我們希望,使用者一率使用 ButtonA.clickButton() 來去更改狀態,我們其實都期待——狀態最好在用規範好的方式被操作

  1. 程式是我寫的,難道還會改錯嗎?

你可能會想,真的會有開發者在 ButtonA.status 亂塞東西嗎?如果這個程式是你寫的,你當然不會這樣做。

但如果是多人合作狀態,或是你是一個套件的開發者。你無法預測他人會如何使用你提供的資料結構,因此這時候就會希望,別人可以遵守制定好的方法去修改狀態,而不是可以直接取得狀態。

想要避免有人不守規矩,就可以使用閉包(Closure)

如果想為了避免使用者直接去更改狀態,並只能透過規定好的方法去更改狀態,這時候就可以使用到閉包

這時候,閉包「內層的變數不會被釋放,可以被重複使用,但又不會被外面讀取」的特性就會發會作用,因此,在這個寫法中

  • 一定要透過 getState 才能獲取 status
  • 一定要透過 clickButton 才能更改 status

因此,你不能用操作物件的方式直接更改

就必須用規定的函式更改

這時候,我們就有一個健康的按鈕系統了!

你其實可能已經用過別人的閉包

閉包的用處在於「狀態管理」以及「強制別人要依據規範(函式)操作狀態,而不能擅自更改

但理解閉包的好處後,開發者可能仍然會想「但我實際上好像沒有這樣寫過耶?」

確實,開發上我們不一定會碰到 —— 但如果你是一個前端框架的使用者,我猜你可能會在不經意的時候就使用別人的閉包。

Redux 的 createStore(範例)

一個閉包例子,可能就是Redux。Redux 本身是 React 的一個狀態管理的系統,簡單來說,開發者可以透過 createStore 來創建一個狀態,以及定義使用者應該遵守什麼方式(函式)才能修改狀態。(就跟我們上面做的事情一樣)

而如果閱讀createStore的原始碼,我們就會發現它就是一個閉包

  • 它不讓currentState直接被讀取,而是規定要用 getState 才能獲得
  • 它不讓currentState能直接被修改,而是規定要用 dispatch 才能操作

結論:什麼時候開發者需要寫到閉包?

  1. 如果你希望「別人要依據你規範好的方法操作狀態」,你需要閉包:

閉包的用處在於,「狀態管理」以及「強制它人依據規範操作狀態,而不能直接更改狀態」,因此假設你有類似的需求,閉包是個好工具。

  1. 如果你希望幫別人寫一些有用的函式庫,你需要閉包:

開發者不一定會是常的寫到閉包,我們平常可能比較是閉包的使用者(例如:Redux)。但如果你想成為一個可以幫別人寫一些有用函式庫的開發者,你就會需要知道如何寫閉包了,因為你會希望你的使用者用你可以預期的方式使用你的資料結構。


Tags

JavaScriptClosure
Previous Article
矽谷之旅(1):如果嚮往,看看就知道了

Nissen Yeh

Software Engineer

Topics

Software philosophy

Web development

Project

Free talk

Related Posts

給初學者的前端 Unit test 教學—— 以 React Testing Library 為例
August 26, 2020
1 min
© 2022, All Rights Reserved.

Quick Links

Advertise with usAbout Us

Social Media