Day 16: 原理篇 —— 瀏覽器小帳本:幫使用者存下他的個人設定(LocalStorage)
在單純由記憶體驅動的前端程式中,每當使用者重新整理網頁,原本設定的深色模式、填寫中的表單或購物車清單都會瞬間煙消雲散。本章節將帶領冒險者解鎖瀏覽器的「永久存檔點」—— LocalStorage。我們將剖析 Web Storage 的底層儲存機制,橫向對比 LocalStorage、SessionStorage 與 Cookie 在保存期限與容量上的本質差異,並掌握 setItem、getItem 等基礎增刪改查指令。同時,我們將深入探討如何運用 JSON.stringify() 與 JSON.parse() 實現複雜物件的打包與拆箱序列化,並建立職人必備的 XSS 防禦與資料容錯防呆思維。
本文同步分享於2026鐵人賽:JS 核心重構:勇者轉職傳說
孩子,在踏上今日的領地前,先坐下來喝杯酒吧。 修煉的路很長,別急著奔跑,先聽聽這份卷軸裡的古老叮嚀。
我在每一站的盡頭都為你鋪設了 「
實戰演武場 CodePen」。那不只是程式碼的拼湊,而是你在這場暴風雨中,試圖用邏輯點燃的一顆星火。 成功突圍後,別忘了帶著你的戰利品回到 「QuestBoard 佈告欄」。在那裡,你會發現自己從不孤單,公會的夥伴們都正舉杯等待你的歸來。記住...
「 導師看重的從來不是你的劍法有多華麗,而是當你被邏輯擊潰、滿身泥濘後,依然選擇握緊理性的劍柄,再次向真相發起衝鋒的勇氣。 」
🛡️ 【公會大廳:導師的修煉導讀】
「導師,為什麼我辛苦幫梅大叔設定好的『黑夜模式』介面,只要他一重新整理網頁,整個公會大廳就又變回白茫茫一片,差點閃瞎他的眼啊!」
我拍掉手上的公文塵埃,指著櫃檯後面的一個皮革小帳本說:「冒險者,那是因為你的程式碼只存在於當前的『執行泡泡(記憶體)』中。一旦泡泡破了(頁面重整),一切都會歸零。
今天,我要教你如何動用瀏覽器的 『自動存檔點 (Save Point)』。我們會把使用者的心聲,一字一句刻在瀏覽器的皮革帳本上。即便勇者關掉電腦去睡覺,明天醒來,他的偏好依然會在。這就是 LocalStorage 的持久化魔法。」
💡 【導師講義:底層真相探究】
1. 儲存三兄弟大 PK
在瀏覽器的世界,我們有三種方式可以存檔,但它們的「保存期限」與「職責範圍」完全不同:
| 特性 | LocalStorage | SessionStorage | Cookie |
|---|---|---|---|
| 保存期限 | 永久存檔 (手動清才消失) | 關閉當前分頁即消失 | 依設定的過期時間消失 |
| 容量大小 | 約 5MB (容量最大) | 約 5MB | 極小(約 4KB) |
| 傳輸行為 | 僅留在本機,不隨請求發送 | 僅留在本機,不隨請求發送 | 每次 HTTP 請求自動攜帶 |
| 主要用途 | 個人喜好設定、待辦清單、快取資料 | 暫存表單輸入、單次流程狀態 | 身分認證 Token、廣告追蹤 |
在現代前端開發中,除非你要處理跟後端伺服器通訊的驗證憑據(Cookie),否則 90% 的本機客戶端存檔需求,我們都優先指派 LocalStorage 出動!
2. 存檔指令:增、刪、改、查
LocalStorage 的操作非常直觀,就像在用一把四段式機關鑰匙:
// 1. 存入 (setItem):傳入 Key(鍵)與 Value(值)
localStorage.setItem("heroName", "辛爾梅");
// 2. 讀取 (getItem):直接拿鑰匙去取回數值
const myHero = localStorage.getItem("heroName"); // "辛爾梅"
// 3. 刪除 (removeItem):指定特定標籤單項排除
localStorage.removeItem("heroName");
// 4. 清空 (clear):整個帳本全部重設燒毀
localStorage.clear();
3. 資料打包魔法:JSON 序列化
這是新手最容易踩到的致命陷阱:LocalStorage 只能儲存「字串」!如果你直接把一個物件或陣列存進去,瀏覽器會自動調用 .toString() 將其轉化為無效的 [object Object]。
為了解決這個問題,我們需要職人的「打包拆箱術」:
JSON.stringify()(打包):將 JavaScript 物件/陣列幻化為 JSON 字串,才能安全塞入儲存槽。JSON.parse()(拆箱):將 JSON 字串召喚回原生物件/陣列,才能重新使用物件屬性點符號(.)或陣列方法操作。
const heroData = { name: "辛爾梅", level: 99 };
// ❌ 錯誤做法:直接存物件,會被強制轉成無效字串
localStorage.setItem("save", heroData); // 裡面會變成 "[object Object]"
// ✅ 正確做法:先打包成 JSON 字串再存入
localStorage.setItem("save", JSON.stringify(heroData));
// 從帳本取出時,記得拆箱還原為物件
const getSave = JSON.parse(localStorage.getItem("save"));
console.log(getSave.name); // "辛爾梅"
4. 邏輯流向圖解
想像 LocalStorage 是一個巨大的壁掛櫃,每個櫃子都有一個專屬標籤(Key)。你存進去的是文字(Value),拿出來的也是文字。打包拆箱魔法就像是把立體的勇者裝備折疊收納成扁平捲軸,放入櫃中;需要使用時,再展開還原成生動的實體。

⚔️ 【戰術對抗:學長與學弟的代碼對決】
招式示範:持久化黑夜模式 (Dark Mode)
❌ 冒險學弟:只管當下,不管未來
- 負能量評級:🔴 重整即歸位 / 🔴 被使用者投訴閃瞎眼
const btn = document.querySelector("#theme-toggle");
btn.addEventListener("click", () => {
document.body.classList.toggle("dark-mode");
// 致命傷:沒有持久化存檔,使用者重新整理網頁後又回到預設白色
});
✅ 勇者學長:帳本紀錄,重啟無憂
- 利潤評級:🟢 體驗極佳 / 🟢 專業級雙向狀態同步
const btn = document.querySelector("#theme-toggle");
// 1. 初始化:頁面載入時先翻閱帳本恢復狀態
const currentTheme = localStorage.getItem("theme");
if (currentTheme === "dark") {
document.body.classList.add("dark-mode");
}
// 2. 切換時:介面變更與帳本寫入雙向同步
btn.addEventListener("click", () => {
const isDark = document.body.classList.toggle("dark-mode");
localStorage.setItem("theme", isDark ? "dark" : "light");
});
學長寫法的關鍵在於 「雙向同步」:操作介面時同步寫入帳本,頁面載入時優先讀取帳本並應用。這正是構建專業級 Web 應用的標準架構思維。
🏰 【勇者精英課:邁向職人的進階架構】
為什麼我們絕對不把敏感資料放 LocalStorage?
雖然 LocalStorage 非常便利,但它存在一項重大的資安特性:在同一網域下的任何 JavaScript 腳本都能直接讀取它(容易遭受 XSS 跨站腳本攻擊)。
因此,專業職人的第一鐵律:絕對不要在 LocalStorage 儲存密碼、信用卡號或未受保護的高敏感 Token。帳本是用來記錄「使用者偏好設定」的,絕不是用來當「金庫密碼箱」。
此外,LocalStorage 的 5MB 容量上限雖然日常夠用,但若嘗試將大量高解析度圖片轉成 Base64 字串硬塞進去,將會迅速耗盡配額並拋出 QuotaExceededError 異常。優雅的工程師必須懂得權衡與節制。
孩子,導師當年第一次寫
JSON.parse時,因為沒考慮到「如果帳本裡根本沒這筆資料」的情況,導致程式直接拋錯崩潰。記住,當
localStorage.getItem("key")找不到對應的標籤時,它會回傳null。如果你直接對null做屬性存取(例如JSON.parse(null).name),程式就會當場炸裂。養成習慣:在操作取回的資料前,善用可選串連(
?.)或條件判斷預先驗證資料是否存在,並提供合理的預設回退值。
天色微亮,營火雖已燃盡,但你眼中卻閃爍著領悟的光芒。在前往演武場挑戰關卡前,我已經幫你把靈魂碎片精煉成了這份「戰術錦囊」。拿好它,今日的任務不再是負擔,而是你證明自我的舞台。去吧,公會的英雄榜在等著你的戰報。
📝 【夥伴筆記:今日修煉精華】
這份筆記是你的隨身護身符,卡關時看一眼,真相就在裡面。
setItem / getItem:鍵值對(Key-Value)存取核心指令。JSON.stringify():將物件與陣列魔法打包為字串的唯一通道。JSON.parse():將字串存檔拆箱還原為可操作的物件結構。- 資安防禦第一:LocalStorage 不具備加密隔離性,嚴禁存放機密憑據。
null防呆處理:讀取不存在的鍵值會回傳null,取值前務必先做安全檢查。
🎯 【實戰演武場】
⚔️ 任務鑑定條件:
- 完成 📜 本日實戰任務 (CodePen)。
- 將 CodePen 網址貼至 QuestBoard,並回填鑑定報告:
- 初心者:存檔術啟動 (成就感發掘):
-
- 你是否成功打開 DevTools > Application 找到你的帳本並親手修改了數值?發現它也會同步改變網頁畫面的驚喜了嗎?
-
- 冒險者:打包拆箱的真相 (理性挑戰):[⚡ 觀念辨析]
-
- 如果我們不用
JSON.stringify直接把一個陣列存入 LocalStorage,拿出來時它會變什麼樣子?還能用forEach嗎?
- 如果我們不用
-
- 初心者:存檔術啟動 (成就感發掘):
- 任務完成後,你的名字將永遠標記在公會的英雄榜上!
📚 【圖書館卷軸與前後站連結】
- 前導冒險: Day 00: 勇者村的入村儀式
- 上一站冒險: Day 15:原理篇 —— 事件委派:一人領信,全家收件的高效管理術
- 下一站冒險: Day 17:原理篇 —— 時間管理大師:Timeout 與 Interval 的實作應用
- MDN 官方文獻: MDN Window.localStorage 使用指南
- MDN 官方文獻: MDN JSON.stringify() 詳解
- MDN 官方文獻: MDN JSON.parse() 詳解