Your objects live in memory, alive and full of behaviour — but the moment they need to leave your program, whether down a network cable to an API or into a file for later, they have to become text. The text format the web agreed on is JSON (JavaScript Object Notation), and it is deliberately tiny: just strings, numbers, booleans, null, arrays, and plain objects built from those.
JavaScript gives you two functions to cross that boundary. JSON.stringify turns a value into a JSON string, and JSON.parse turns that string back into a fresh value. Together they are how you send a shopping cart to a server, save a user's settings, or cache a result. The catch — and there is one — is that JSON is a strict subset of JavaScript, so not everything in your object makes it across.
flowchart LR
O["object<br/>{ name: #quot;Ada#quot;, age: 36 }"] -->|"JSON.stringify"| S["string<br/>{#quot;name#quot;:#quot;Ada#quot;,#quot;age#quot;:36}"]
S -->|"JSON.parse"| O2["object (a fresh copy)"]
style S fill:#e0900b,color:#fff
style O2 fill:#1e7f3d,color:#fff
The two directions
Serialising is a single call. JSON.stringify(person) returns a string — note the quotes inside it, because the whole object is now text. Parsing reverses it: JSON.parse(text) reads that string and hands back a brand-new object with the same data. Every call to parse builds a separate object, so two parses of the same text give two independent objects that merely hold equal values.
A useful extra: stringify takes a third argument for pretty-printing. Pass null (no replacer) and a number, and the output gains newlines and indentation so a human can read it — JSON.stringify(obj, null, 2). The compact form is for the wire; the indented form is for log files and debugging.
const person = { name: "Ada", age: 36, skills: ["js", "sql"], active: true };
const text = JSON.stringify(person);
console.log(text);
console.log(typeof text);
const back = JSON.parse(text);
console.log(back.name);
console.log(back.skills[1]);
What survives, what gets dropped
Because JSON is a subset, stringify has to leave things behind. Numbers, strings, booleans, null, arrays, and plain objects all cross safely. Functions, undefined, and Symbol keys or values do not — they vanish silently, with no error and no warning. An object can quietly shrink as it serialises, which is the first surprise.
The second is type reshaping: a Date has no JSON representation, so it is converted to its ISO string, and NaN or Infinity become null. After a round trip, what was a Date object is now a string, so typeof back.created is "string", not "object". Always assume a value that left the JSON boundary may have changed type on the way back.
flowchart TD K["survives: numbers, strings, booleans, arrays, plain objects, null"] --> G["stays in JSON"] D["dropped: functions, undefined, Symbol"] --> X["vanishes silently"] style G fill:#1e7f3d,color:#fff style X fill:#b91c1c,color:#fff
const mixed = {
count: 3,
name: "Kit",
skip: undefined, // dropped
run: function () {}, // dropped
created: new Date(0) // becomes an ISO string
};
const text = JSON.stringify(mixed);
console.log(text);
const back = JSON.parse(text);
console.log(back.skip);
console.log(typeof back.run);
console.log(typeof back.created);
flowchart TD N["node.self = node"] -->|"JSON.stringify"| T["TypeError: circular structure"] T --> F["guard with try/catch"] style T fill:#b91c1c,color:#fff style F fill:#e0900b,color:#fff
const cfg = { host: "localhost", port: 8080, flags: { debug: true } };
console.log(JSON.stringify(cfg, null, 2)); // pretty, 2-space indent
const node = { name: "loop" };
node.self = node; // now circular
let result = "ok";
try {
JSON.stringify(node);
} catch (e) {
result = "circular caught";
}
console.log(result);
What does this print? JSON.stringify returns compact text with quoted keys.
const obj = { a: 1, b: [2, 3] };
console.log(JSON.stringify(obj));
stringify produces a string with double-quoted keys and no spaces.
Nested arrays stay as JSON arrays: [2,3].
Write totalScores(jsonText) that takes a JSON string holding an array of numbers, parses it with JSON.parse, and returns the sum. So totalScores('[10, 20, 5]') returns 35.
function totalScores(jsonText) {
// parse the JSON text and return the sum of the array
}
Call JSON.parse(jsonText) to get the array back.
Sum it with reduce: arr.reduce((sum, n) => sum + n, 0).
An empty array sums to 0.
function totalScores(jsonText) {
const arr = JSON.parse(jsonText);
return arr.reduce((sum, n) => sum + n, 0);
}
This object has a greet method, and the code round-trips it through JSON expecting back.greet() to still work — but JSON.stringify drops functions, so after the round trip greet is gone and the call throws TypeError. Fix buildGreeting so it returns the greeting after a round trip: compute the greeting string before serialising (store it as a plain string on the object), so nothing depends on a method surviving JSON.
function buildGreeting(person) {
const original = {
name: person,
greet() { return `Hello, ${this.name}`; }
};
const text = JSON.stringify(original);
const back = JSON.parse(text);
return back.greet(); // BUG: greet was dropped by JSON.stringify
}
Functions do not survive JSON.stringify, so back.greet is undefined.
Store the finished greeting as a plain string on the object before serialising.
Return back.greeting (the string) instead of calling a method.
function buildGreeting(person) {
const original = {
name: person,
greeting: `Hello, ${person}`
};
const text = JSON.stringify(original);
const back = JSON.parse(text);
return back.greeting;
}
What does this print? Nested arrays and objects survive a round trip intact.
const data = { users: [{ name: "Ada" }, { name: "Grace" }] };
const text = JSON.stringify(data);
const back = JSON.parse(text);
console.log(back.users.length);
console.log(back.users[0].name);
console.log(typeof text);
The nested array of users survives the round trip with both entries.
back.users[0].name is "Ada".
text is a string, the output of JSON.stringify.
You run JSON.stringify({ a: 1, b: undefined, c: function(){} }). What is in the resulting string?
JSON has no representation for functions or undefined, so stringify omits both silently. Only a survives, giving {"a":1} with no error.
Recap
JSON.stringify(value)turns a value into compact JSON text;JSON.parse(text)turns it back into a fresh object.- Pass a third argument (
null, 2) to pretty-print for logs and debugging. - Surviving types: numbers, strings, booleans,
null, arrays, plain objects. Dropped: functions,undefined,Symbol. Reshaped:Date→ ISO string,NaN/Infinity→null. - A circular reference makes
stringifythrow — break the loop or wrap it intry/catch. - A JSON round trip is not a clone: it loses methods and edge-case values. Use it for transport and storage; use
structuredClonefor an exact copy.
That completes the module. You can now model data with objects and classes, share behaviour through prototypes, pick the right collection for the job, pull values apart with destructuring, and move it all across the network as JSON.