本文同步分享於個人部落格:Liwen Chiou | Digital Architect & Full-Stack Engineer
孩子,在踏上今日的領地前,先坐下來喝杯酒吧。
修煉的路很長,別急著奔跑,先聽聽這份卷軸裡的古老叮嚀。我在每一站的盡頭都為你鋪設了 「
實戰演武場 CodePen」。那不只是程式碼的拼湊,而是你在這場暴風雨中,試圖用邏輯點燃的一顆星火。
成功突圍後,別忘了帶著你的戰利品回到 「QuestBoard 佈告欄」。在那裡,你會發現自己從不孤單,公會的夥伴們都正舉杯等待你的歸來。記住...
「 導師看重的從來不是你的劍法有多華麗,而是當你被邏輯擊潰、滿身泥濘後,依然選擇握緊理性的劍柄,再次向真相發起衝鋒的勇氣。 」
「導師,我實作的搜尋框出事了!」
一名年輕的冒險者氣喘吁吁地跑進大廳。「我串接了昨天學的氣象站 API,但我發現使用者每打一個字,信鴿(Fetch)就飛出去一次。如果使用者打字很快,一秒鐘就會飛出十幾隻信鴿... 氣象伺服器的守衛已經快把我拉黑了!」
我放下手中的鵝毛筆,示意他冷靜。
「孩子,這就是為什麼你需要 『技能冷卻 (Cooldown)』。在真實世界中,如果你對著怪物不停地按普通攻擊,你的身體也不可能一秒鐘揮劍一千次,對吧?我們需要一種機制,來過濾那些『無意義的頻繁觸發』。」
今天,我要教你兩大神兵利器:防抖 (Debounce) 與 節流 (Throttle)。
這兩個觀念雖然聽起來很專業,但其實就是我們在 Day 07 學過的「閉包 (Closure)」 與 Day 17 學過的「計時器 (setTimeout)」 的完美結合。
想像你在施放一個「回城捲軸」,需要 3 秒鐘的讀條時間。
如果你在讀條到第 2 秒時,不小心被怪物碰了一下(或者你又按了一次按鍵),讀條就會重頭開始。只有當你「完全安靜下來」並維持 3 秒後,傳送才會成功。
想像你在揮舞一把重劍,揮一下需要 1 秒鐘的冷卻時間。
在這 1 秒內,不論你瘋狂點擊多少次滑鼠,劍都只會在那 1 秒結束後才揮出下一發。
🏹 導師的辨析圖:防抖 vs 節流
| 特性 | 防抖 (Debounce) | 節流 (Throttle) |
|---|---|---|
| 目標焦點 | 等待最後一次 | 維持穩定頻率 |
| 觸發時機 | 停止動作後才執行 | 定期執行 |
| 比喻 | 影印回城讀條 | 技能冷卻 CD |
導師的圖解心法: 為什麼這需要閉包?因為我們需要一個「記憶空間」來儲存計時器的狀態 (timer),而這個狀態不能被外圍干擾。

// 每按一個鍵就發送一次請求
input.addEventListener("input", (e) => {
console.log("📡 發送信鴿獲獲取資料...", e.target.value);
});
// 實作一個簡易防抖大師
function debounce(fn, delay) {
let timer = null; // 這是閉包住的秘密記憶
return function (...args) {
clearTimeout(timer); // 只要被戳,就重來
timer = setTimeout(() => {
fn.apply(this, args); // 💡 確保傳回原本的主人與參數 (Day 10 複習)
}, delay);
};
}
const smartSearch = debounce((val) => {
console.log("✨ [精準發射] 獲取資料:", val);
}, 500);
input.addEventListener("input", (e) => smartSearch(e.target.value));
導師講評: 注意看
let timer = null的位置。它被鎖在debounce函數中,卻能被回傳的那個匿名函數持續存取。這就是我們在 Day 07 埋下的伏筆 —— 閉包的真正威力。
在真實專案中,我們很少自己從頭寫防抖與節流。職人們通常會使用像是 Lodash 這樣的工具庫。
// 職人的作法
import { debounce } from "lodash";
const search = debounce(fetchData, 300);
但在轉職前,你必須親手寫過一次。因為當你理解了 timer 是如何在閉包中被 clear 與 set 的,你才算真正掌握了 JS 的時序控制。
記得我第一次接捲軸事件 (Scroll) 時,為了做一個導覽列變色的效果,我直接在
scroll事件裡跑了一個沉重的計算。結果網頁滑起來就像卡在泥淖裡的牛車。後來前輩告訴我:「導師啊,瀏覽器一秒鐘可以偵測到幾百次捲軸移動,但你的螢幕更新率才 60Hz,你運算那麼多次給誰看呢?」
那天我學會了 Throttle。從此,我的代碼滑順得像抹了凡士林。
天色微亮,營火雖已燃盡,但你眼中卻閃爍著領悟的光芒。在前往演武場挑戰關卡前,我已經幫你把靈魂碎片精煉成了這份「戰術錦囊」。拿好它,今日的任務不再是負擔,而是你證明自我的舞台。去吧,公會的英雄榜在等著你的戰報。
timer 狀態,利用 setTimeout 控制執行時機。delay 為 1000ms,快速點擊按鈕 5 次(每次間隔 200ms),Debounce 與 Throttle 分別會執行幾次?