JSON — JavaScript Object Notation
JSON is a text format for representing structured data. It's the universal format for data exchange on the web — APIs return JSON, databases store JSON, config files use JSON. It looks like JavaScript objects but has stricter rules.
// JSON rules:
// - Keys MUST be in double quotes
// - Values can be: string, number, boolean, null, array, object
// - No functions, no undefined, no comments, no trailing commas
// Valid JSON:
const jsonString = '{"name":"Alice","age":30,"active":true,"scores":[95,87,92]}';
// Parse JSON string → JavaScript object:
const user = JSON.parse(jsonString);
console.log(user.name); // "Alice"
console.log(user.scores[0]); // 95
// Stringify JavaScript object → JSON string:
const obj = { name: "Bob", age: 25, hobbies: ["coding", "music"] };
const json = JSON.stringify(obj);
console.log(json); // {"name":"Bob","age":25,"hobbies":["coding","music"]}
// Pretty print with indentation (for logging):
console.log(JSON.stringify(obj, null, 2));
// {
// "name": "Bob",
// "age": 25,
// ...
// }
// What gets lost in JSON serialization:
const complex = {
fn: function() {}, // functions → undefined (omitted)
sym: Symbol("id"), // symbols → undefined (omitted)
und: undefined, // undefined values → omitted
inf: Infinity, // Infinity → null
date: new Date() // Date → string (ISO format)
};
Object Cloning
// Shallow copy methods:
const original = { a: 1, b: { c: 2 } };
const spread = { ...original };
const assign = Object.assign({}, original);
// Both are SHALLOW — nested objects are still shared:
spread.a = 99; // doesn't affect original
spread.b.c = 99; // DOES affect original! (same reference)
// Deep clone with structuredClone (modern, ES2022):
const deepClone = structuredClone(original);
deepClone.b.c = 99; // doesn't affect original!
console.log(original.b.c); // 2 — safe!
// structuredClone handles: objects, arrays, Date, Map, Set, etc.
// Does NOT handle: functions, DOM nodes, class instances' methods
// Old deep clone trick (JSON):
const jsonClone = JSON.parse(JSON.stringify(original));
// Works for simple data but loses functions, Dates, etc.
// Arrays:
const arr = [1, [2, 3], { a: 4 }];
const shallowArr = [...arr]; // shallow
const deepArr = structuredClone(arr); // deep
JSON in Practice
// Saving to localStorage (requires JSON):
const preferences = { theme: "dark", fontSize: 16 };
localStorage.setItem("prefs", JSON.stringify(preferences));
// Reading back:
const saved = localStorage.getItem("prefs");
const prefs = saved ? JSON.parse(saved) : {};
// API responses are JSON:
const response = await fetch("https://api.example.com/data");
const data = await response.json(); // auto-parses JSON
// Error handling for malformed JSON:
function safeParseJSON(str) {
try {
return JSON.parse(str);
} catch (e) {
console.error("Invalid JSON:", e.message);
return null;
}
}
// Custom serialization with replacer:
const withDate = { name: "Alice", created: new Date() };
JSON.stringify(withDate, (key, value) => {
if (value instanceof Date) return value.toISOString();
return value;
});
// Custom parsing with reviver:
JSON.parse(jsonStr, (key, value) => {
if (key === "created") return new Date(value);
return value;
});
⚡ Key Takeaways
- JSON keys must be double-quoted — no functions, undefined, or comments
JSON.parse()converts JSON string to JS valueJSON.stringify()converts JS value to JSON string- Spread (
{ ...obj }) creates shallow copies — nested objects are shared structuredClone()creates deep clones (ES2022) — the modern solution- Always wrap
JSON.parse()in try/catch — malformed JSON throws
🎯 Practice Exercises
EXERCISE 1
Create a user preferences system using localStorage: save preferences as JSON, load them on startup, and update individual settings without losing others.
EXERCISE 2
Write a function that takes any JS value and returns a "safe JSON" version — converting Dates to ISO strings, removing functions, and replacing undefined with null.