JSON.parse / JSON.stringify
The Translator With a Limited Dialect
LinkedIn Hook
JSON.stringify({ a: undefined, b: () => {}, c: NaN })returns"{"c":null}".Two of your three properties silently vanished. The third turned into
null. No error. No warning. Just quietly broken data on its way to your API.These are the JSON serialization edge cases that cause real production bugs — Maps and Sets become empty objects, Dates become strings that don't auto-revive, BigInts throw, circular references throw, and the replacer/reviver hooks that would save you are things most developers have never used.
In this lesson you'll learn which values JSON can't represent, how arrays and objects disagree on missing values, why
structuredCloneis the modern cloning answer, and how to usetoJSON, replacer, and reviver to keep serialization under your control.If you've ever logged an object to an API and seen data missing on the other side — this lesson fixes it.
Read the full lesson -> [link]
#JavaScript #InterviewPrep #JSON #Serialization #Frontend #CodingInterview #WebDevelopment
What You'll Learn
- Which JavaScript values JSON silently drops, mutates, or throws on
- How to use replacer, reviver, and
toJSONto control serialization - When to prefer
structuredCloneover a JSON roundtrip
The Translator With a Limited Dialect
Think of JSON.stringify as a translator who only speaks a limited dialect. Hand it a regular object and it does great. But hand it something exotic — a function, a Symbol, Infinity — and it either silently drops it or converts it to something you didn't expect. This is the source of countless production bugs.
What JSON Does NOT Support
| Input Value | JSON.stringify Output | Surprise Level |
|---|---|---|
undefined | Omitted (in object) / null (in array) | High |
function() {} | Omitted (in object) / null (in array) | High |
Symbol("x") | Omitted (in object) / null (in array) | High |
Infinity | "null" | High |
-Infinity | "null" | High |
NaN | "null" | High |
new Date() | "2026-04-10T..." (string) | Medium |
new Map() | "{}" (empty object) | High |
new Set() | "{}" (empty object) | High |
RegExp | "{}" (empty object) | Medium |
BigInt | Throws TypeError | Critical |
| Circular ref | Throws TypeError | Critical |
const edgeCases = {
a: undefined, // disappears
b: function() {}, // disappears
c: Symbol("test"), // disappears
d: NaN, // becomes null
e: Infinity, // becomes null
f: new Date(), // becomes string
g: /regex/gi, // becomes {}
h: new Map([["k", 1]]), // becomes {}
i: new Set([1, 2, 3]) // becomes {}
};
console.log(JSON.stringify(edgeCases, null, 2));
// {
// "d": null,
// "e": null,
// "f": "2026-04-10T00:00:00.000Z",
// "g": {},
// "h": {},
// "i": {}
// }
// a, b, c completely GONE!
Arrays Handle Missing Values Differently
const arr = [undefined, function(){}, Symbol("x"), NaN, Infinity];
console.log(JSON.stringify(arr));
// [null, null, null, null, null]
// In arrays, unsupported values become null (not omitted)
Date Becomes String (Cannot Auto-Restore)
const obj = { created: new Date("2026-04-10") };
const json = JSON.stringify(obj);
const parsed = JSON.parse(json);
console.log(typeof parsed.created); // "string" — NOT a Date object!
console.log(parsed.created instanceof Date); // false
Circular Reference Throws
const a = {};
const b = { ref: a };
a.ref = b; // circular!
try {
JSON.stringify(a);
} catch (e) {
console.log(e.message); // "Converting circular structure to JSON"
}
BigInt Throws
try {
JSON.stringify({ value: 42n });
} catch (e) {
console.log(e.message); // "Do not know how to serialize a BigInt"
}
Replacer Function
The second argument to JSON.stringify is a replacer — a filter/transformer for values:
const data = {
name: "Rakibul",
password: "secret123",
age: 25,
role: "admin"
};
// Filter out sensitive fields
const safe = JSON.stringify(data, (key, value) => {
if (key === "password") return undefined; // omit
return value;
}, 2);
console.log(safe);
// {
// "name": "Rakibul",
// "age": 25,
// "role": "admin"
// }
// Or pass an array of allowed keys
const filtered = JSON.stringify(data, ["name", "role"]);
console.log(filtered); // {"name":"Rakibul","role":"admin"}
Reviver Function
The second argument to JSON.parse is a reviver — a transformer for parsed values:
const json = '{"name":"Rakibul","created":"2026-04-10T00:00:00.000Z","age":25}';
const obj = JSON.parse(json, (key, value) => {
// Auto-convert ISO date strings back to Date objects
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
});
console.log(obj.created instanceof Date); // true
toJSON Method
Objects can define a custom toJSON method to control their serialization:
class User {
constructor(name, password) {
this.name = name;
this.password = password;
this.createdAt = new Date();
}
toJSON() {
return {
name: this.name,
createdAt: this.createdAt.toISOString()
// password intentionally omitted
};
}
}
const user = new User("Rakibul", "secret");
console.log(JSON.stringify(user));
// {"name":"Rakibul","createdAt":"2026-04-10T00:00:00.000Z"}
Space Parameter for Pretty Printing
const data = { name: "Rakibul", skills: ["JS", "React"] };
// Compact
JSON.stringify(data);
// {"name":"Rakibul","skills":["JS","React"]}
// 2-space indent
JSON.stringify(data, null, 2);
// Tab indent
JSON.stringify(data, null, "\t");
// Custom string (up to 10 chars)
JSON.stringify(data, null, "-> ");
structuredClone as Alternative
For deep cloning objects that JSON can't handle:
const original = {
date: new Date(),
regex: /test/gi,
map: new Map([["key", "value"]]),
set: new Set([1, 2, 3]),
nested: { deep: { value: 42 } }
};
// JSON roundtrip — loses types
const jsonClone = JSON.parse(JSON.stringify(original));
console.log(jsonClone.date instanceof Date); // false
console.log(jsonClone.map); // {}
// structuredClone — preserves types
const clone = structuredClone(original);
console.log(clone.date instanceof Date); // true
console.log(clone.map.get("key")); // "value"
console.log(clone.set.has(2)); // true
// But structuredClone still can't handle:
// - Functions
// - DOM nodes
// - Symbols
Common Mistakes
- Assuming a
JSON.parse(JSON.stringify(obj))roundtrip is a safe deep clone. It silently drops functions,undefined, and Symbols, turnsNaN/Infinityintonull, and converts Dates to strings that no longerinstanceof Date. - Forgetting that arrays and objects disagree on missing values. In an object,
undefined/ functions / Symbols are OMITTED entirely. In an array, they becomenull— which shifts nothing but also looks nothing like the input. - Trying to
JSON.stringifyaBigInt, a circular reference, or a Map/Set full of data and expecting something reasonable. BigInt and cycles throw; Maps and Sets stringify to"{}".
Interview Questions
Q: What happens when you JSON.stringify an object that contains undefined, a function, and a Symbol?
All three are silently omitted from the output. In arrays, they become
null. This is because JSON is a data format — it has no concept of functions, Symbols, or undefined. Onlystring,number,boolean,null,object, andarrayare valid JSON types.
Q: How do you handle Date objects with JSON.parse/stringify?
Dates become ISO strings during stringify. To restore them, use a reviver function:
JSON.parse(json, (key, val) => isDateString(val) ? new Date(val) : val). Alternatively, usestructuredClonewhich preserves Date objects natively.
Q: What is the toJSON method?
If an object has a
toJSON()method,JSON.stringifycalls it and uses the return value instead of the object itself. This lets you control exactly what gets serialized — useful for omitting sensitive data or transforming values.
Q: Can you JSON.stringify a Map or Set?
No — they both serialize to
"{}". To serialize them, convert first:JSON.stringify([...myMap])for Map entries, orJSON.stringify([...mySet])for Set values. Then use a reviver to reconstruct.
Q: What does JSON.stringify do with NaN and Infinity?
Both become
null. This is because JSON has no concept of NaN or Infinity — they're not valid JSON number values.
Q: What does structuredClone handle that JSON.parse(JSON.stringify()) doesn't?
structuredClonecorrectly handles: Date, RegExp, Map, Set, ArrayBuffer, Blob, File, ImageData, and circular references. JSON roundtrip loses all of these (Dates become strings, others become{}).
Quick Reference — Cheat Sheet
JSON EDGE CASES — QUICK MAP
undefined in object -> property OMITTED
undefined in array -> becomes null
function in object -> property OMITTED
Symbol -> OMITTED (object) / null (array)
NaN / Infinity -> becomes null
Date -> becomes ISO string (not revived!)
Map / Set / RegExp -> becomes {}
BigInt -> throws TypeError
Circular reference -> throws TypeError
Signatures:
JSON.stringify(val, replacer, space)
JSON.parse(str, reviver)
obj.toJSON() -> custom serialization hook
Better deep-clone:
structuredClone(val) -> preserves Date, Map, Set,
RegExp, cycles (no functions/DOM)
Previous: setTimeout vs setInterval -> The Kitchen Timer and the Drifting Metronome Next: Polyfills -> Write Your Own map, bind, and Promise
This is Lesson 14.2 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.