- What is the value of
text? Answer: [1,null,3]
When working with JSON.parse and JSON.stringify together, you need to understand two separate behaviors: how the reviver function in JSON.parse handles undefined, and how JSON.stringify handles sparse arrays.
The reviver function in JSON.parse is called for each key-value pair. When the key is "1" (note: array indices become string keys), returning undefined tells the parser to delete that element from the result. This doesn't shift remaining elements — it creates a hole in the array, producing [1, empty, 3] internally (a sparse array). The array still has length 3, but index 1 is missing entirely.
Now, when JSON.stringify serializes this sparse array, it converts holes and undefined values to null, because JSON has no concept of undefined or empty slots. So text becomes "[1,null,3]", making A correct.
B is wrong because JSON.stringify does not collapse holes or shift elements — the array length is preserved, not compacted. C is wrong because undefined cannot exist in valid JSON; JSON.stringify replaces it with null, not the string "undefined". D is wrong because JSON arrays don't get converted to objects during serialization, even if they're sparse — JSON.stringify keeps the array format intact.
A useful rule of thumb: whenever you return undefined from a JSON.parse reviver on an array element, think "hole, not removal." And remember that JSON.stringify always converts undefined and empty slots to null in arrays — this pairing is a classic exam trap.
- What is the value of
text? Answer: {"b":{"c":30}}
When you see JSON.parse with a second argument, you're dealing with a reviver function — a callback that transforms each key-value pair bottom-up as the JSON is parsed. Understanding the execution order and what return undefined does is the key to this question.
The reviver processes inner nodes before outer ones. Here's what happens step by step: the parser reaches "a":1 (inside the root), and since key === "a", it returns undefined — which deletes that property entirely. Then it hits "b"'s nested object: "a":2 returns undefined (deleted again), and "c":3 passes the number check, returning 3 * 10 = 30. The nested object {"c":30} is returned as-is (it's not a number). Finally, the outer "b" key survives, giving you {"b":{"c":30}}. Stringifying that gives **B) {"b":{"c":30}}**.
A is wrong because it assumes the reviver never runs, or that undefined returns are kept — but returning undefined from a reviver removes the key entirely. C is tempting but incorrect: it keeps "a":20 inside "b", forgetting that the key === "a" check fires for all keys named "a" regardless of nesting depth. D reflects a misconception that undefined becomes null in JSON — that's only true inside JSON.stringify, not when a reviver returns undefined.
Study tip: Remember that returning undefined from a reviver removes the property — it's not the same as returning null. Also, the reviver applies globally to every matching key at any depth, not just the top level.
- What does the final statement log?
Answer: {"y":3} |y
When JSON.stringify receives a replacer function, it calls that function repeatedly — first for the root object itself (with an empty string "" as the key), then for each property of whatever value you return. This two-phase behavior is what makes this question tricky.
Here's the step-by-step walk-through: The replacer is first called with key = "" and value = { x: 1 }. Because the key is "", the function pushes "" to keys and returns { y: 2 } — swapping the entire root object. JSON.stringify then processes that returned object, so it calls the replacer again with key = "y" and value = 2. This pushes "y" to keys, and since typeof 2 === "number", it returns 2 + 1 = 3. The final serialized result is {"y":3}, and keys.join("|") gives "|y" (the empty string, then "y", joined by |). So the correct answer is C.
A ({"x":2}) is wrong because it assumes the original { x: 1 } object is used, ignoring that the "" branch replaces it with { y: 2 } entirely.
B ({"y":2}) is wrong because it correctly identifies the root swap but misses that the number 2 is then incremented by the typeof value === "number" branch.
D (undefined) would only occur if the replacer returned undefined for the root, which it doesn't.
As a study tip, remember that the replacer function in JSON.stringify always fires first with an empty key representing the root — any value you return there becomes the new object being serialized.
- What is the value of
text? Answer: {"c":5,"a":1,"b":{"c":3,"a":2}}
When you see JSON.stringify called with a second argument that's an array, that array acts as a replacer — it works like a whitelist, telling JSON.stringify exactly which property keys to include, and in what order they appear in the output.
Here's what's crucial to understand: the order of keys in the resulting JSON string follows the order of keys in the replacer array, not their order in the original object. In this question, the replacer is ["c", "a", "b"], so the output will list c first, then a, then b. Any key not in the array — like d — is completely excluded. This filtering applies recursively: when JSON.stringify processes the nested object b, it still uses the same replacer array, so only c and a are kept from { a: 2, c: 3, d: 4 }, again in the order c → a. That gives you {"c":3,"a":2} for the nested object.
Putting it together, the full result is {"c":5,"a":1,"b":{"c":3,"a":2}}, which is B.
A is wrong because it uses the original object's key order (a, b, c) rather than the replacer's order. C is wrong because it includes d:4 inside the nested object — the replacer filters out d at every level. D is wrong for two reasons: it includes the top-level d:6, and it uses the wrong key order for the nested object.
As a study tip, remember: a replacer array controls both inclusion (whitelist) and ordering of keys, and it applies at every level of nesting — not just the top level.
- Which expression evaluates to the number
100 without throwing an exception? Answer: JSON.parse('{"n":1e+2}').n
When working with JSON.parse(), you need to remember that JSON has its own strict syntax rules — it's not the same as JavaScript. Valid JSON numbers follow a specific format, and many JavaScript-style numeric literals are simply illegal in JSON.
Option C, JSON.parse('{"n":1e+2}').n, is the correct choice. Scientific notation like 1e+2 (which equals 100) is perfectly valid in JSON. The JSON specification explicitly allows numbers in scientific notation using e or E followed by an optional sign and digits, so this parses successfully and returns 100.
Here's why each other option fails. A uses 01, an octal-style integer literal. JSON does not allow leading zeros on numbers (except for 0 itself), so this throws a SyntaxError. B uses single quotes around the key ('n'), but JSON strictly requires double quotes for all strings and keys — single quotes are a JavaScript convenience that JSON simply does not support, causing another SyntaxError. D uses +100, but JSON numbers cannot begin with a + sign. Unlike JavaScript, which treats +100 as a valid numeric expression, JSON's number grammar only allows an optional leading minus sign.
A useful mental rule: JSON is stricter than JavaScript in almost every way. When evaluating JSON strings, ask yourself — does this follow the JSON spec, not just JavaScript syntax? Watch out for single quotes, leading + signs, leading zeros, and unquoted keys. These are classic traps because they're all valid in JavaScript but illegal in JSON.
- What is the value of
text? Answer: {"item":{"n":30,"sourceKey":"item"}}
When you see JSON.stringify combined with a toJSON method, you need to track two separate serialization steps carefully: toJSON runs first, then the replacer function processes the result.
Here's the key insight: when toJSON is called on item, the key argument it receives is the property name used to access that object in the parent — in this case "item". So toJSON("item") returns { n: 3, sourceKey: "item" } (since this.n + 1 = 3). The toJSON method always fires before the replacer, so the replacer never sees the original object — it only sees this transformed result.
Next, the replacer function runs over every key-value pair in the output. When it encounters n: 3, since 3 is a number, it multiplies by 10, giving 30. The sourceKey value "item" is a string, so it passes through unchanged. This produces {"item":{"n":30,"sourceKey":"item"}} — answer D.
Answer A is wrong because it shows n: 20, which would mean toJSON returned n: 2 (the original value) before the replacer multiplied it — but toJSON actually increments it to 3 first. Answer B makes two errors: it uses n: 20 (forgetting the increment) and shows sourceKey: "" (wrong key). Answer C gets the n: 30 math right but uses sourceKey: "", incorrectly assuming toJSON receives an empty string key — that would only happen at the root level, not a nested property.
Remember: toJSON runs before the replacer, and the key it receives is its own property name in the parent object — not an empty string.
- Which expression returns exactly the JSON string
{"name":"root"}? Answer: JSON.stringify(node, (key, value) => key === "self" ? undefined : value)
When working with JSON.stringify, two concepts are critical: circular reference handling and how the replacer function filters values. Here, node.self = node creates a circular reference — node points to itself — which causes JSON.stringify without a replacer to throw a TypeError. That's why you need a replacer function to skip the "self" key.
The replacer function receives each key-value pair as the object is traversed. The key insight is what you return matters enormously: returning undefined tells JSON.stringify to omit that key entirely from the output, while returning null serializes it as the JSON value null.
Option C is correct because when key === "self", it returns undefined, which signals to the serializer "exclude this property." The remaining property name passes through unchanged, producing exactly {"name":"root"}.
Option A throws a TypeError because the circular reference (node.self → node) cannot be serialized without a replacer — this is one of the most common JSON.stringify pitfalls to know.
Option B is the sneaky trap: returning null doesn't omit the key — it includes it with a null value, producing {"name":"root","self":null}. Many students confuse null and undefined here.
Option D is logically flawed in a different way. The empty string "" is the key used for the root object itself during traversal. Returning undefined for key === "" would discard the entire object, producing undefined (not a JSON string at all).
Study tip: In a JSON.stringify replacer, undefined = omit, null = include as null. Memorize this distinction — exam questions love exploiting it.
- What are the values of
outerIndent and valueIndent, respectively? Answer: 10 and 20
When working with JSON.stringify, the third argument controls indentation — but there's a critical cap: the maximum allowed indent is 10 spaces, regardless of what number you pass. So even though 20 is provided here, JavaScript internally clamps it to 10.
With that in mind, let's trace through the output. JSON.stringify({ outer: { value: 1 } }, null, 10) produces something like:
{
"outer": {
"value": 1
}
}
Each nesting level adds 10 spaces. lines[1] is " "outer": {...}" — it has 10 leading spaces, so outerIndent = 10. lines[2] is the "value" key, which is nested one level deeper inside outer, giving it 20 leading spaces, so valueIndent = 20. That confirms D) 10 and 20.
A) 0 and 0 would only occur if no indentation were used (i.e., the third argument were 0, null, or omitted entirely) — the output would be a single-line string with no newlines at all.
B) 10 and 10 is the most tempting trap: you might correctly recall the 10-space cap but forget that nesting accumulates indentation. Each additional level adds another 10 spaces.
C) 20 and 40 ignores the cap entirely, treating 20 as the literal indent width and doubling it for the nested level — both mistakes at once.
Study tip: Always remember the two rules together — the indent cap is 10, and each nesting level adds one full indent unit cumulatively. Exam questions love testing both at the same time.
- What is the value of
text after this code runs? Answer: {"items":[null,null,null],"score":null}
When you pass an object to JSON.stringify(), JavaScript applies specific serialization rules that silently drop or transform certain values — and knowing these rules is exactly what this question tests.
Here's what JSON.stringify does with "unsupported" types: when a value like undefined, a function, or a Symbol appears as an object property, the entire key-value pair is omitted from the output entirely. That's why a, b, and c vanish completely — they don't become null, they just disappear. However, when those same types appear inside an array, they can't be omitted (that would corrupt array indexing), so they are replaced with null instead. Finally, NaN is serialized as null because it isn't a valid JSON number.
Applying all of this to the object: a, b, and c are dropped; items survives but each of its three elements becomes null; and score: NaN becomes score: null. That gives you {"items":[null,null,null],"score":null} — confirming B is correct.
A is wrong because it assumes undefined, functions, and Symbols become null in object properties, but they're actually removed entirely. C is wrong in the opposite direction — it correctly removes the object-level keys but then incorrectly removes the array elements instead of converting them to null. D is wrong because it assumes NaN serializes as the string "NaN", but JSON.stringify converts it to null.
A handy rule to memorize: *object properties with unsupported types disappear; array elements with unsupported types become null.*
- Which statement about
copy is correct? Answer: copy.when is a string, count is absent, and both array values are null.
Whenever you see JSON.parse(JSON.stringify(...)), you need to think carefully about how JSON serialization handles special JavaScript values — because several types don't survive the round-trip intact.
Here's what happens step by step. JSON.stringify converts the Date object into an ISO string ("2025-06-01T12:00:00.000Z"), so when parsed back, copy.when is just a plain string, not a Date instance. Object properties with undefined values are completely omitted from JSON output, so count simply doesn't exist on copy. Inside arrays, undefined and NaN can't be represented in JSON, so they become null — arrays must preserve their length, so JSON replaces invalid values with null rather than dropping them. This makes A correct: copy.when is a string, count is absent, and both array values are null.
B is wrong because it claims copy.when remains a Date. JSON.parse returns a string, not a Date object — you'd need new Date(copy.when) to restore it. C is wrong on two counts: it says count equals null (it's actually absent entirely) and that array values are "absent" (they become null, not removed, because removing them would change the array's length). D is wrong because it suggests no transformation occurs at all — JSON.parse(JSON.stringify(...)) is explicitly lossy for these types.
As a study tip, memorize the "JSON lossy types": undefined (dropped from objects, nullified in arrays), NaN/Infinity (become null), and Date (becomes a string). These edge cases appear frequently in JavaScript interview and exam questions.