By WebNest Studio Engineering Team
JavaScript Tutorial
Modern ES Features
Since ECMAScript 2015 (ES6), JavaScript has received a steady stream of quality-of-life features that make everyday code shorter, safer, and easier to read. Four of the most commonly used are template literals, optional chaining, the nullish coalescing operator, and destructuring.
These features do not change what JavaScript can compute — everything they do could be written with older syntax — but they remove significant boilerplate and reduce a whole category of bugs related to accessing missing or undefined data.
Why it matters
Code written since 2015 looks very different from older JavaScript. Template literals, optional chaining and nullish coalescing remove whole categories of clumsy checks, and they appear on almost every line of a current codebase. Without them you can neither read modern code nor write it concisely.
Template Literals
Template literals use backticks (`) instead of quotes, and allow embedded expressions using ${expression} syntax, along with real multi-line strings without concatenation. This replaces older, more error-prone string concatenation with +.
Optional Chaining (?.)
Optional chaining lets you safely access a deeply nested property without manually checking every level for null or undefined first. If any part of the chain before ?. is null or undefined, the whole expression short-circuits and evaluates to undefined instead of throwing a TypeError. It also works with method calls (obj.method?.()) and array-style access (obj?.[key]).
Nullish Coalescing (??)
The nullish coalescing operator returns its right-hand side only when the left-hand side is null or undefined — unlike ||, which also falls through for other falsy values like 0, "", or false. This makes ?? the correct tool for supplying a default value while still allowing legitimate falsy values to be used as-is.
Array and Object Destructuring (Recap in Context)
Destructuring, covered earlier alongside arrays and objects, is itself a modern ES feature that pairs naturally with the others here — for example, combining destructuring with default values and optional chaining lets you pull deeply nested, possibly missing data into clean local variables in a single expression.
Examples
Template literals versus string concatenation
const name = "Maria";
const orders = 3;
const oldStyle = "Hello, " + name + "! You have " + orders + " orders.";
const newStyle = `Hello, ${name}! You have ${orders} orders.`;
console.log(oldStyle);
console.log(newStyle);
console.log(`Multi-line:
Second line here.`);
Hello, Maria! You have 3 orders.
Hello, Maria! You have 3 orders.
Multi-line:
Second line here.
Optional chaining and nullish coalescing preventing crashes on missing data
const user = {
name: "Diego",
address: {
city: "Madrid",
},
};
console.log(user.address?.city);
console.log(user.contact?.email); // no "contact" property, no crash
console.log(user.getFullProfile?.()); // no such method, no crash
const settings = { volume: 0, brightness: null };
console.log(settings.volume ?? 50); // 0 is a valid value, not replaced
console.log(settings.brightness ?? 50); // null is replaced by the default
console.log(settings.volume || 50); // || incorrectly treats 0 as "missing"
Madrid
undefined
undefined
0
50
50
Common Mistakes
- Using "||" to provide a default value for a number or boolean, which incorrectly replaces legitimate falsy values like 0 or false — "??" should be used instead.
- Chaining "?." past the point where a crash was actually expected behavior, silently hiding bugs that should have surfaced as errors during development.
- Forgetting that optional chaining short-circuits the entire expression, so a chained assignment or function call after "?." may not run at all if an earlier link is missing.
- Mixing template literal backticks with regular quotes inside the same string, forgetting that only backtick-delimited strings support ${...} interpolation.
Exercise
Using modern syntax: read the city of a user whose address is missing, falling back to Unknown, without the code throwing; read a count that is legitimately 0, falling back to 10 only when it is null or undefined; and build a greeting with a template literal.
Expected output: Unknown, 0, Hello, Asha! (one per line)
const user = { name: "Asha" }; // no address property
const settings = { count: 0 };
// TODO: the user's city, or "Unknown" (optional chaining and ??)
// TODO: settings.count, or 10 only if it is null or undefined
// TODO: "Hello, Asha!" using a template literal
Show hints
user.address?.citygivesundefinedinstead of throwing whenaddressis missing.??falls back only fornullandundefined, while||would also replace0.
Show solution
const user = { name: "Asha" }; // no address property
const settings = { count: 0 };
console.log(user.address?.city ?? "Unknown");
console.log(settings.count ?? 10);
console.log(`Hello, ${user.name}!`);
Quick quiz
1. What does user?.profile?.name return when user is null?
- A. It throws a TypeError
- B. undefined
- C. null
- D. An empty string
Show answer
B. Optional chaining stops and yields undefined as soon as it meets null or undefined.
2. What is the difference between 0 || 5 and 0 ?? 5?
- A. Both give 5
- B. Both give 0
- C. The first gives 5 and the second gives 0
- D. The first gives 0 and the second gives 5
Show answer
C. || replaces any falsy value; ?? replaces only null and undefined.
3. Which characters delimit a template literal?
- A. Angle brackets
- B. Single quotes
- C. Double quotes
- D. Backticks
Show answer
D. Inside it, an expression is written as ${...}, and the text may span several lines.
Interview questions
What is the difference between || and ?? for default values?
Show answer
a || b returns b whenever a is falsy, which includes 0, an empty string and false as well as null and undefined. a ?? b returns b only when a is null or undefined. Use ?? when zero, an empty string or false are valid values that should be kept.
Name some important features added in ES6 and later.
Show answer
let and const, arrow functions, template literals, destructuring, default and rest parameters, the spread operator, classes, modules with import and export, promises, and Map and Set came with ES6 in 2015. Later versions added async and await, optional chaining, nullish coalescing, and array methods such as includes, flat and at.
Key Points to Remember
- Template literals (backticks) support embedded expressions with ${...} and real multi-line strings.
- Optional chaining (?.) safely short-circuits to undefined when accessing a property on null or undefined, avoiding manual checks.
- Nullish coalescing (??) supplies a default only for null/undefined, unlike || which also replaces other falsy values like 0 and "".
- These features reduce boilerplate and a whole class of "cannot read property of undefined" errors.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering