← JavaScript Learning Path

JAVASCRIPT V2 · EVERYDAY · 60

JSON / localStorage:把 Memory State 轉成可保存的 Data Contract

JavaScript object 活在目前 runtime memory;reload、關頁或換裝置後不會自動存在。要保存或傳輸,必須先 serialization。這一課把 JSON、localStorage、schema/version、parse failure 與 trust boundary 放在同一個完整資料生命週期裡。

Learning outcomes

1. Why now:Memory state 不會因為「看起來存在」就永久保存

const students = [
  { name: "Amy", score: 92 }
];

這份 array 在 page runtime 裡存在。Reload 後 JavaScript 重新啟動,必須從某個 persistent representation 恢復。

2. JavaScript object ≠ JSON text

const student = {
  name: "Amy",
  score: 92
};

const raw =
  JSON.stringify(student);

console.log(typeof raw); // string
JS valuestringifyJSON textparseJS value

3. Worked Example A:Round trip

const source = {
  name: "Amy",
  score: 92
};

const raw =
  JSON.stringify(source);

const restored =
  JSON.parse(raw);

restored 是新的 JavaScript object,不是 source 的同一 reference。

4. JSON 支援的資料模型有限

JSON 很適合 object/array/string/number/boolean/null,但 JavaScript 的 function、undefined、Date instance 等不會完整保留原語意。

const payload = {
  createdAt: new Date(),
  helper() {}
};

const raw =
  JSON.stringify(payload);

Date 會變成字串 representation;function 不會以可執行 function 的形式被保存。

5. localStorage 只直接保存 string

localStorage.setItem(
  "gradeTracker",
  JSON.stringify(students)
);

const raw =
  localStorage.getItem("gradeTracker");

getItem 找不到 key 時回 null。不要直接把 null 丟進假設有資料的流程。

6. Worked Example B:Safe load

function loadStudents(raw) {
  if (raw === null) return [];

  try {
    const value = JSON.parse(raw);

    return Array.isArray(value)
      ? value
      : [];
  } catch {
    return [];
  }
}

這裡把 parse failure 轉成產品選擇:回空資料而不是整頁 crash。不同產品也可以選擇顯示 repair/error UI。

7. Schema validity ≠ JSON syntax validity

JSON.parse('{"version":1}')
// syntax valid

// 但如果 app 需要 students array,
// schema 仍不符合。

成功 parse 只代表 representation 合法,不能證明資料符合你的 domain contract。

8. Versioning:Persistent data 也會有 migration 問題

const payload = {
  version: 1,
  students
};

之後 schema 改變時,version 讓 loader 有機會辨認舊資料並 migration,而不是猜。

9. Worked Example C:Versioned decoder

function decodePayload(raw) {
  if (raw === null) {
    return { version: 1, students: [] };
  }

  try {
    const payload = JSON.parse(raw);

    if (
      payload.version !== 1
      || !Array.isArray(payload.students)
    ) {
      return { version: 1, students: [] };
    }

    return payload;
  } catch {
    return { version: 1, students: [] };
  }
}

10. localStorage scope:它不是 server database

所以「存在 localStorage」不能拿來證明後端資料已保存。

11. Security boundary:不要把 privileged secret 當一般 client state

同 origin JavaScript 可以存取 localStorage,因此它不是「對 Browser JavaScript 保密」的地方。Token 是否適合保存也要看具體 threat model;高權限 server secret 不應放進 client storage。

12. Common mistakes

A. 把 object 直接 setItem

localStorage 會把 value 當 string;應明確 serialize。

B. parse 成功就相信 schema

還要檢查 version/shape。

C. 以為 Date round trip 後仍是 Date instance

通常要重新 parse/construct domain type。

D. broken JSON 讓整個 app 啟動 crash

Persistent boundary 必須有 failure strategy。

13. Debug evidence:把 raw 與 parsed 分開看

console.log({
  raw,
  rawType: typeof raw
});

const parsed = JSON.parse(raw);

console.log({
  parsed,
  isArray: Array.isArray(parsed)
});

不要把「storage 有字串」直接等同「domain object 正確」。

14. Guided exercise:Round trip 後哪些東西改變?

const value = {
  createdAt: new Date("2026-01-01"),
  score: 90
};

const restored =
  JSON.parse(
    JSON.stringify(value)
  );

判斷 restored.createdAt 的 type 與語意。

15. Independent exercise:實作 Versioned Decoder

完成 readStudentCount(raw):null、broken JSON、wrong version、students 非 array 都回 0;合法 v1 payload 回 students.length。

16. Challenge:設計 version 1 → version 2 migration

v1 student 只有 name/score;v2 新增 id。你會在 load 時 migration,還是另做 migration step?需要哪些 deterministic rules 才能安全產生 id?

Project checkpoint:Grade Tracker Persistence Contract

const storageKey =
  "gradeTrackerV1";

function saveStudents(students) {
  const payload = {
    version: 1,
    students
  };

  localStorage.setItem(
    storageKey,
    JSON.stringify(payload)
  );
}

現在 persistence 有明確 representation/version,而不是隨便把 memory object 丟進 storage。

17. Vocabulary / Summary

18. Further reading

Knowledge check

  1. 區分 JS object 與 JSON text。
  2. 說明 parse success 為何不代表 schema valid。
  3. 解釋 Date round trip 的語意損失。
  4. 說明 localStorage 與 server database 的 boundary 差異。
  5. 完成 versioned decoder mastery tests。