JSON and Objects
ทุกอย่างคือ string
หัวข้อที่มีชื่อว่า “ทุกอย่างคือ string”Web Storage เก็บได้เฉพาะ string เท่านั้น หากคุณส่งอย่างอื่นเข้าไปให้ setItem browser จะแปลงเป็น string ให้อย่างเงียบ ๆ:
localStorage.setItem('demo:num', 42);console.log(localStorage.getItem('demo:num')); // "42" — a string, not a numbertypeof localStorage.getItem('demo:num'); // "string"
localStorage.setItem('demo:bool', true);console.log(localStorage.getItem('demo:bool')); // "true" — a string, not a boolean
localStorage.setItem('demo:obj', { key: 'value' });console.log(localStorage.getItem('demo:obj')); // "[object Object]" — useless!นี่หมายความว่าคุณ ไม่สามารถ เก็บ object, array, number หรือ boolean ได้โดยตรงแล้วคาดหวังว่าจะอ่านกลับมาในชนิดเดิม
การเก็บ object ด้วย JSON.stringify
หัวข้อที่มีชื่อว่า “การเก็บ object ด้วย JSON.stringify”แพตเทิร์นมาตรฐานคือ serialise ด้วย JSON.stringify ตอนเขียน และ deserialise ด้วย JSON.parse ตอนอ่าน:
const user = { name: 'Ada', age: 36, admin: false };
// WritelocalStorage.setItem('demo:user', JSON.stringify(user));
// Read backconst raw = localStorage.getItem('demo:user');const restored = raw !== null ? JSON.parse(raw) : null;console.log(restored.name); // 'Ada'console.log(restored.age); // 36 (number, not string)console.log(restored.admin); // false (boolean, not string)JSON.stringify รองรับ object ซ้อน, array, number, boolean และ null ได้ แต่ ไม่ เก็บรักษาสิ่งเหล่านี้:
- ค่า
undefined(จะถูกละทิ้ง) - Function (จะถูกละทิ้ง)
- object
Date(ถูกแปลงเป็น ISO string — ใช้new Date(str)เพื่อกู้คืน) Map,Set,RegExp,BigInt(ไม่สามารถ serialise เป็น JSON ได้หากไม่มี replacer ที่กำหนดเอง)
helper แบบ typed ขนาดจิ๋ว
หัวข้อที่มีชื่อว่า “helper แบบ typed ขนาดจิ๋ว”แทนที่จะเขียนแพตเทิร์น stringify/parse ซ้ำไปทุกที่ helper เล็ก ๆ ตัวหนึ่งจะช่วยให้โค้ดสะอาด:
function storageGet(key, fallback) { try { const raw = localStorage.getItem(key); return raw !== null ? JSON.parse(raw) : fallback; } catch { return fallback; // Guard against corrupt/non-JSON values }}
function storageSet(key, value) { localStorage.setItem(key, JSON.stringify(value));}try/catch รอบ ๆ JSON.parse เป็นเรื่องสำคัญ: หากค่าที่เก็บไว้ถูกเขียนโดยโค้ดเก่า (plain string), สคริปต์จาก third-party หรือถูกแก้ไขด้วยมือใน DevTools ค่านั้นอาจไม่ใช่ JSON ที่ถูกต้อง และ JSON.parse จะโยน error
รันได้: object round-trip
หัวข้อที่มีชื่อว่า “รันได้: object round-trip”ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
JSON.stringify/JSON.parse | เก็บ object ซ้อนกันหลายชั้นและ array ได้ครบ ใช้ pattern เดียวกันทุกที่ | ต้อง parse ใหม่ทุกครั้งที่อ่าน เสีย CPU เพิ่มขึ้นตามขนาดข้อมูล |
| เก็บเป็น string ดิบ (ไม่ stringify) | เร็วกว่า ไม่มี overhead ของการ serialise | ได้ค่ากลับมาผิดชนิดเสมอ (number/boolean กลายเป็น string) และ object กลายเป็น "[object Object]" |
helper storageGet/storageSet แบบ typed | โค้ดสั้นลง มี try/catch ป้องกัน corrupt data ในตัว | เพิ่ม layer เล็ก ๆ ที่ทีมต้องรู้จักและดูแลรักษา |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ส่ง object เข้า
setItemตรง ๆ โดยไม่ stringify — ได้"[object Object]"กลับมา ให้JSON.stringifyก่อนเขียนเสมอ - เรียก
JSON.parseโดยไม่มีtry/catch— ถ้าค่าที่เก็บไว้ไม่ใช่ JSON ที่ถูกต้อง (ถูกเขียนด้วยมือใน DevTools หรือมาจากโค้ดเวอร์ชันเก่า) แอปจะ crash ทันที - คาดหวังว่า
Dateจะกลับมาเป็นDateobject หลัง parse —JSON.stringifyแปลงDateเป็น ISO string เสมอ ต้องเรียกnew Date(str)เองเพื่อกู้คืนชนิดเดิม
💡 ตัวอย่างจากของจริง
Notion / Google Docs — ฟีเจอร์ autosave ฉบับร่างจะ
JSON.stringifystate ทั้งฟอร์ม (title, blocks, cursor position) แล้วเก็บลงlocalStorageเป็นระยะ เพื่อกู้คืนงานได้หากแท็บถูกปิดโดยไม่ตั้งใจWeb app ที่มี dark mode / theme settings — เก็บ object การตั้งค่าทั้งก้อน (
{ theme, fontSize, locale }) ไว้ใน key เดียวด้วยJSON.stringifyแล้วอ่านกลับมา parse ตอนแอป bootstrap เพื่อ apply ค่าก่อน render หน้าแรก