JSON & Optional Chaining
JSON & Optional Chaining
JSON (JavaScript Object Notation) is the universal data interchange format for web APIs. Optional chaining and nullish coalescing, added in ES2020, eliminate entire categories of defensive-coding boilerplate.
JSON.stringify
Converts a JavaScript value into a JSON string. Accepts an optional replacer and a spaces argument for pretty-printing.
JSON.stringify({ name: "Alice", age: 30 });
// '{"name":"Alice","age":30}'
JSON.stringify(data, null, 2); // pretty with 2-space indentValues that JSON cannot represent (functions, undefined, Symbol) are silently omitted from objects or replaced with null in arrays.
JSON.parse
Converts a JSON string back into a JavaScript value. Always wrap in try/catch when parsing untrusted input — malformed JSON throws a SyntaxError.
const user = JSON.parse('{"name":"Bob","age":25}');Optional Chaining (?.)
Safely access deeply nested properties. Instead of writing:
const city = user && user.address && user.address.city;You write:
const city = user?.address?.city;If any part of the chain is null or undefined, the entire expression short-circuits to undefined instead of throwing a TypeError.
Optional chaining also works for method calls (obj.method?.()) and bracket notation (arr?.[0]).
Nullish Coalescing (??)
?? returns the right-hand side only when the left-hand side is null or undefined — unlike ||, which also triggers on 0, "", and false.
const port = config.port ?? 3000; // 3000 only if port is null/undefined
const count = 0 ?? 10; // 0 (|| would give 10)Logical Assignment Operators
ES2021 added three compact assignment operators:
| Operator | Equivalent |
|---|---|
| `a | |
a &&= b | a = a && b |
a ??= b | a = a ?? b |
??= is especially useful for setting default values on object properties that might be missing.
Code Examples
const user = {
name: "Alice",
age: 30,
hobbies: ["reading", "coding"],
greet: function() { return "hi"; }, // functions are dropped
};
// Serialize to JSON string
const json = JSON.stringify(user, null, 2);
console.log(json);
// Deserialize back to JS object
const parsed = JSON.parse(json);
console.log(parsed.hobbies[1]);
// Deep clone pattern (simple, no Date/Map support)
const clone = JSON.parse(JSON.stringify({ a: 1, b: { c: 2 } }));
clone.b.c = 99;
console.log("Original c:", 2); // unaffectedJSON.stringify with null, 2 produces human-readable output. Functions are silently dropped. JSON round-tripping is a simple deep-clone technique for plain data objects.
const users = [
{ name: "Alice", address: { city: "London", zip: "EC1" } },
{ name: "Bob" }, // no address
null, // missing user
];
users.forEach((u) => {
const city = u?.address?.city ?? "Unknown";
console.log(u?.name ?? "Guest", "->", city);
});
// Optional method call
const obj = { greet: () => "Hello!" };
console.log(obj.greet?.()); // "Hello!"
console.log(obj.farewell?.()); // undefined — no error
// Optional bracket access
const arr = [10, 20, 30];
console.log(arr?.[1]); // 20
console.log(null?.[0]); // undefinedOptional chaining short-circuits to undefined when encountering null/undefined, preventing TypeErrors. Combining ?. with ?? provides a clean default-value pattern for deeply nested or missing data.
// ?? vs ||
const config = { timeout: 0, retries: null, host: "" };
console.log(config.timeout ?? 5000); // 0 (falsy but not null/undefined)
console.log(config.timeout || 5000); // 5000 (|| treats 0 as falsy)
console.log(config.retries ?? 3); // 3 (null triggers ??)
console.log(config.host ?? "localhost"); // "" (empty string is not null)
// Logical assignment operators
let a = null;
let b = 0;
let c = "hello";
a ??= "default"; // a was null, so assigned
b ||= 42; // b was falsy (0), so assigned
c &&= c.toUpperCase(); // c was truthy, so assigned
console.log(a, b, c);Nullish coalescing (??) only triggers on null/undefined, preserving intentional falsy values like 0 and empty string. Logical assignment operators combine the check and assignment into a single expression.
Quick Quiz
1. What happens to functions when you JSON.stringify an object?
2. What is the output of: console.log(0 ?? 'default')?
3. What does user?.address?.city return if user.address is undefined?
4. Which operator should you use to provide a default only when a value is null or undefined (not when it is 0 or false)?
Was this lesson helpful?