Frontend Master

JavaScript Curriculum

Writing JavaScript Like It's Now
+45 XP

Writing JavaScript Like It's Now

medium
~22 min·45 XP

You're reviewing a pull request on the Nexus team. The code uses ?., ??, ??=, ...args, and [key]: value. You understand JavaScript — but this syntax is unfamiliar. This lesson closes that gap. These aren't tricks. They're solutions to real, recurring problems that older syntax handled badly.

The syntax gap

The JavaScript you see in production looks different from the JavaScript in beginner tutorials. Not because it does different things — because the language kept adding better syntax for the same things. Every feature in this lesson replaces a pattern that was awkward, verbose, or silently dangerous.


Optional chaining ?.

The most common runtime error in JavaScript is TypeError: Cannot read properties of null. It happens whenever you assume a nested property exists and it doesn't.

js
// Without ?. — crashes if any link in the chain is null or undefined const city = user.profile.address.city // 💥 if profile is null // With ?. — returns undefined safely at the first missing link const city = user?.profile?.address?.city // undefined — no crash

Toggle any level of the object to null and watch the difference:

optional chaining — toggle null values, see what breaks
const user = {
name: "Alex Chen",
profile: {
bio: "Engineer at Nexus",
address: { city: "London", country: "UK" }
}
}

without ?. (unsafe)

user.profile.address.city
"London"

with ?. (safe)

user?.profile?.address?.city
"London"

never throws — returns undefined if any link is null

user?.profile?.bio

"Engineer at Nexus"

user?.profile?.address?.city

"London"

user?.getPermissions?.()

undefined

?. also works on method calls and bracket notation:

js
user?.getPermissions() // only calls if user exists user?.roles?.['admin'] // bracket notation arr?.[0] // safe array access
💡?. short-circuits — the whole chain stops
As soon as ?. encounters null or undefined, the entire expression short-circuits to undefined. Nothing to the right is evaluated. This means user?.getPermissions() won't throw even if getPermissions doesn't exist on user — it just returns undefined.

Nullish coalescing ??

When you want a fallback value, || seems natural — but it treats 0, false, and "" as missing, which causes silent data corruption:

js
const score = 0 const display = score || 'No score' // "No score" ← wrong! 0 is valid const display = score ?? 'No score' // 0 ← correct

Select each value below and watch || and ?? diverge:

?? vs || — pick a value, see both operators evaluate

value of username

||⚠ falsy check
username || "Guest"
"Guest"

Falls back on any falsy value: false, 0, "", null, undefined, NaN

0 is falsy — you lost a valid zero

??nullish coalescing
username ?? "Guest"
0

Falls back only on null or undefined — 0, false, "" are kept as-is

Use ?? when the value might legitimately be 0, false, or "" and you only want to fall back on true absence. Use || when any falsy value should trigger the default.

The rule is precise: ?? only falls back on null and undefined — the two values that mean "truly not here". Everything else, including 0, false, and "", is kept as-is.

js
// Common patterns const name = user?.name ?? 'Anonymous' const timeout = config.timeout ?? 3000 const env = process?.env?.NODE_ENV ?? 'development'

Modern syntax: shorthand, spread, rest, and more

Six patterns you'll see daily — each one with its old form and its modern replacement:

Object shorthand properties & methods

When a property name matches the variable holding its value, you can omit the value. Functions in objects can drop the colon and function keyword.

before (old way)

const name = "Alex", role = "admin"
const user = {
name: name,
repetitive
role: role,
repetitive
greet: function() { ... }
}

after (modern syntax)

const name = "Alex", role = "admin"
const user = {
name,
shorthand ✓
role,
shorthand ✓
greet() { ... }
method shorthand ✓
}

Shorthand properties are everywhere in modern JS — especially when building objects from function parameters or destructured values.


Destructuring — a deeper look

Destructuring was introduced in lesson-07, but there are a few patterns worth seeing together:

js
// Array destructuring with rest const [first, second, ...remaining] = [1, 2, 3, 4, 5] // first = 1, second = 2, remaining = [3, 4, 5] // Object destructuring with rename and default const { name: userName = 'Guest', role = 'viewer' } = user ?? {} // userName = user.name (or 'Guest'), role = user.role (or 'viewer') // Destructuring in function parameters — very common in React function Card({ title, description, onClick }) { return `<div onclick="${onClick}">${title}: ${description}</div>` } // Nested destructuring const { profile: { address: { city = 'Unknown' } = {} } = {} } = user ?? {} // Safe deep destructuring — each level has a fallback

Logical assignment ??= ||= &&=

These combine a check with assignment. They only assign if the condition is met:

js
// ??= assign only if null or undefined (most useful) config.timeout ??= 3000 // sets to 3000 only if timeout is null/undefined user.name ??= 'Anonymous' // ||= assign only if falsy user.role ||= 'viewer' // sets if role is "", 0, null, undefined, false // &&= assign only if truthy (transform if it exists) user.email &&= user.email.toLowerCase() // only lowercases if email exists

Before ES2021 this required:

js
if (config.timeout == null) config.timeout = 3000 // ??= equivalent if (!user.role) user.role = 'viewer' // ||= equivalent if (user.email) user.email = user.email.toLowerCase() // &&= equivalent

Template literal types

Beyond basic string interpolation, template literals have two advanced forms:

js
// Multi-line strings — newlines are real const html = ` <div class="card"> <h2>${user.name}</h2> <p>${user.role}</p> </div> ` // Nesting expressions — any JS expression works inside ${} const msg = `${items.length} item${items.length === 1 ? '' : 's'} in cart` const url = `${BASE_URL}/users/${userId}/posts?page=${page}&limit=${limit}` // Tagged templates — a function processes the literal const query = sql`SELECT * FROM users WHERE id = ${userId}` // sql() receives: ['SELECT * FROM users WHERE id = ', ''], [userId] // and can safely escape the value before building the string

Putting it together — the buildConfig challenge

All five patterns meet in one function:

js
// Defaults object — used as spread base const DEFAULTS = { timeout: 3000, retries: 3, debug: false } function buildConfig(overrides) { // spread merges defaults with overrides (overrides win on conflict) const config = { ...DEFAULTS, ...overrides } // ??= sets version only if truly absent config.version ??= '1.0.0' // ?. and ?? safely read a nested override with a fallback config.env = overrides?.env ?? 'production' return config } buildConfig({ timeout: 5000 }) // { timeout: 5000, retries: 3, debug: false, version: '1.0.0', env: 'production' } buildConfig({ version: '2.0.0', env: 'staging' }) // { timeout: 3000, retries: 3, debug: false, version: '2.0.0', env: 'staging' } buildConfig(null) // overrides?.env safely returns undefined → 'production' // { timeout: 3000, retries: 3, debug: false, version: '1.0.0', env: 'production' }

Three features, one small function, real behaviour. This is what modern JavaScript looks like.

Challenge

Write a function called buildConfig(overrides) that takes an optional overrides object and returns a config object. Use object spread to merge a set of defaults ({ timeout: 3000, retries: 3, debug: false }) with overrides. Use ??= to ensure the result always has a version property (default: '1.0.0') if it wasn't in overrides. Use optional chaining to safely read overrides?.env and set it as the env property (default via ?? to 'production').

optional-chainingnullish-coalescinglogical-assignmentrest-paramsspreadobject-shorthandcomputed-propertiestagged-templates
Writing JavaScript Like It's Now | Nexus Learn