實際運用
把狀態「關」在函式裡,做出安全又好用的介面——從封裝概念走向 HOF、debounce 與 memoize 的設計思維
函式結束後,內部狀態為何仍存在
把資料藏起來,只暴露合法入口
HOC、debounce、throttle 的設計思維
同樣的輸入不要重算
在 run 裡宣告 a,把內層函式 logA 回傳出去而不是直接呼叫——
run() 明明已執行完,fn() 仍能印出 a。這代表 a 沒有消失,而是被 logA 「帶著走」。
JAVASCRIPT
包含函式執行時的所有變數與參數
即使函式本體移到外部,引用關係仍然成立
只要內層函式還存在,那段環境就繼續活著
無法直接觸碰環境裡的變數,只能走開的入口
假設有一個 balance,提供 deduct 扣款且一次最多扣 10 元——
問題不在 deduct 寫得不好,而是 balance 在外面是公開的。任何人都可以直接改到它。
把 balance 放進 getWallet 裡,只回傳對外操作的方法。外界根本拿不到 balance,只能走 wallet.deduct()。
balance -= 999;
// ReferenceError: balance is not defined
讓資料不在外部直接可見,外界無法偶然改動它
回傳一組你允許的操作方法,作為唯一的互動介面
所有合法操作都經過你設計的入口,規則不可繞過
在模組化還不普及的年代,用 IIFE 把程式包起來,避免把太多東西掛到 window。外面拿不到 secret,但可以用 window.myLib.api() 操作它。
你平常用 webpack 打包,把輸出拆開來看,底層結構大致是:
modules 的對照表require 做載入與快取整套機制之所以能運作,核心原因是:closure 讓 modules、cache、require 這些內部狀態被保留,且不會污染外部環境。
從 React HOC 到 debounce、throttle 與 memoize——把既有的東西包起來,然後加一層行為
Class component 年代,「到處都要加一樣的檢查或行為」時:
更好的方式:把「權限檢查」抽成一個可重複使用的包裝層,讓元件本身只專注在顯示內容。
原本的元件,只專注在顯示內容
權限檢查、紀錄、埋點、錯誤處理
原元件邏輯不變,外層統一管理橫切邏輯
HOC 想解決的核心:不要讓橫切需求侵入每個元件內部。它本質上只是 Higher-Order Function 的一種應用。
使用者快速打字時,每打一字就呼叫 API 很浪費。設定 300ms:只要還在輸入就重設計時器,停止後才真的執行。
有些事件需要持續回饋(resize、scroll),但不希望每個事件都觸發昂貴計算。設定 50ms:不管多密集,固定間隔最多執行一次。
只要事件還在持續,就先暫停等待。停止後才執行一次。適合只需要最後結果的情境。
不管事件多密集,固定時間內最多只執行一次,保持穩定節奏。適合需要持續回饋的情境。
控制的不是「什麼時候執行」,而是「同樣的輸入不要重算」。用參數當 key,把結果存在 Map,下次命中直接回傳快取。
加入權限等額外行為
控制觸發策略
加入快取機制
在外面加一層,附加新能力
當你想在程式裡劃出一塊「只屬於我、外界別亂碰」的小天地時,closure 幾乎是最直接、也最可靠的選項。
橫切需求不要散落,集中在包裝層處理