JavaScript Roadmap — Day 18: ES6+ Modern JavaScript Features

 

JavaScript · Day 18 of 180 · Beginner Phase

ES6+ Modern JavaScript Features

ES6 was released in 2015 and completely transformed how JavaScript is written. Every modern codebase — React, Node, Vue, Next.js — uses these features constantly. Master them and your code will be cleaner, shorter, and more professional.

Day 18 / 180
Beginner Phase
🕐 15 min read
💻 8 code examples
🎯 3 practice tasks

Look at these two pieces of code. Both do the exact same thing:

Old JS vs Modern ES6+
// ❌ Old JavaScript — ES5 (2009)
var name = user.name || "Guest";
var city = user.address && user.address.city;
var msg  = "Hello " + name + " from " + city + "!";
var arr2 = arr1.concat([4, 5]);

// ✅ Modern JavaScript — ES6+ (2015+)
const name = user?.name ?? "Guest";
const city = user?.address?.city;
const msg  = `Hello ${name} from ${city}!`;
const arr2 = [...arr1, 4, 5];

The modern version is shorter, safer, and far more readable. Every React codebase, every Node.js API, every professional JavaScript project uses these features on every single line. Today you learn all of them — in one complete lesson.

1. What is ES6+ — A Brief History

JavaScript gets updated every year by the TC39 committee. ES6 (ECMAScript 2015) was the biggest update in the language's history. Here are the most important versions and what they added:

Version Year Key Features
ES6 2015 let/const, arrow functions, classes, modules, destructuring, spread, template literals, Promises
ES2017 2017 async/await, Object.entries(), Object.values()
ES2019 2019 Array.flat(), Object.fromEntries(), optional catch binding
ES2020 2020 Optional chaining ?., Nullish coalescing ??, BigInt, Promise.allSettled()
ES2021+ 2021+ Logical assignment (??=, ||=, &&=), String.replaceAll(), Array.at()

2. Template Literals — Clean String Building

Template literals use backticks instead of quotes and allow embedded expressions with ${}. They also support multi-line strings natively — no more \n:

ES6 — Template Literals
const name   = "Waheed";
const age    = 22;
const salary = 150000;

// ❌ Old way — string concatenation
const msg1 = "Hello, " + name + "! You are " + age + " years old.";

// ✅ Template literal
const msg2 = `Hello, ${name}! You are ${age} years old.`;

// Expressions inside ${}
console.log(`Salary after tax: Rs${salary * 0.9}`);
console.log(`Status: ${age >= 18 ? "Adult" : "Minor"}`);

// Multi-line strings
const html = `
  <div class="card">
    <h2>${name}</h2>
    <p>Age: ${age}</p>
    <p>Salary: Rs${salary}</p>
  </div>
`;

// Nested template literals
const items = ["HTML", "CSS", "JS"];
const list = `Skills: ${items.join(" · ")}`;
console.log(list); // Skills: HTML · CSS · JS

3. Destructuring — Extract Values Cleanly

Destructuring lets you unpack values from arrays and objects into variables in one line. You have seen this before in Day 14 and 15 — but here is the complete picture with all patterns:

ES6 — Destructuring
// ── Array Destructuring ──
const colors = ["red", "green", "blue", "yellow"];

const [first, second]        = colors;      // "red", "green"
const [, , third]            = colors;      // skip → "blue"
const [head, ...tail]        = colors;      // "red", ["green","blue","yellow"]
const [a = "default"]        = [];          // "default"

// Swap variables
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2, 1 ← swapped!

// ── Object Destructuring ──
const user = { name: "Waheed", age: 22, city: "Faisalabad" };

const { name, age }          = user;        // basic
const { name: devName }      = user;        // rename
const { salary = 0 }         = user;        // default value
const { name: n, ...rest }   = user;        // rest → { age, city }

// Function parameter destructuring
function greet({ name, city = "Pakistan" }) {
  return `${name} from ${city}`;
}
console.log(greet(user)); // "Waheed from Faisalabad"

// Nested destructuring
const { address: { city: userCity } } =
  { address: { city: "Lahore" } };
console.log(userCity); // "Lahore"

4. Spread & Rest — The Three Dots 🔥

The three dots ... do two different things depending on where they are used. As spread they expand an array or object. As rest they collect remaining values. Same syntax — completely different behavior:

ES6 — Spread & Rest
// ── SPREAD — expands ──

// Spread arrays
const a = [1, 2, 3];
const b = [4, 5, 6];
const combined = [...a, ...b];        // [1,2,3,4,5,6]
const copy      = [...a];             // safe copy
const withExtra = [...a, 99, ...b];   // [1,2,3,99,4,5,6]

// Spread objects
const defaults  = { theme: "dark", lang: "en" };
const overrides = { theme: "light" };
const settings  = { ...defaults, ...overrides };
// { theme: "light", lang: "en" } ← overrides wins

// Spread in function calls
const nums = [3, 1, 4, 1, 5, 9];
console.log(Math.max(...nums)); // 9
console.log(Math.min(...nums)); // 1

// ── REST — collects ──

// Rest in function params
function sum(...numbers) {
  return numbers.reduce((t, n) => t + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15

function first(a, b, ...rest) {
  console.log(a, b, rest);
}
first(1, 2, 3, 4, 5); // 1  2  [3,4,5]

5. Optional Chaining ?. — Safe Property Access

Optional chaining ?. was added in ES2020 and immediately became one of the most used operators. It safely accesses nested properties without crashing when something is null or undefined:

ES2020 — Optional Chaining
const user = {
  name: "Waheed",
  address: null,
  getCity: null
};

// ❌ Without ?. — crashes!
// console.log(user.address.city); // TypeError!

// ✅ With ?. — returns undefined safely
console.log(user?.address?.city);    // undefined
console.log(user?.social?.twitter); // undefined

// ?. with arrays
const arr = null;
console.log(arr?.[0]); // undefined — no crash

// ?. with function calls
console.log(user.getCity?.()); // undefined — no crash

// Real world — API response
const response = {
  data: { users: [{ name: "Waheed" }] }
};
console.log(response?.data?.users?.[0]?.name); // "Waheed"
console.log(response?.data?.posts?.[0]?.title); // undefined

// DOM — check element before using
const el = document.querySelector("#maybe-missing");
el?.classList.add("active"); // safe — no crash if null

6. Nullish Coalescing ?? — Better Default Values

The ?? operator returns the right side only when the left side is null or undefined. This is a critical difference from || which also triggers on 0, "", and false:

ES2020 — Nullish Coalescing ??
// || returns fallback for ANY falsy value
console.log(0     || "default"); // "default" ← 0 is falsy! BUG
console.log(""    || "default"); // "default" ← "" is falsy! BUG
console.log(false || "default"); // "default" ← false is falsy! BUG

// ?? returns fallback ONLY for null/undefined
console.log(0     ?? "default"); // 0       ← correct ✅
console.log(""    ?? "default"); // ""      ← correct ✅
console.log(false ?? "default"); // false   ← correct ✅
console.log(null  ?? "default"); // "default" ← null triggers ✅

// Real world — user settings
const settings = { volume: 0, darkMode: false, username: "" };

// ❌ Wrong — || treats 0, false, "" as missing
const vol1  = settings.volume   || 50;     // 50 — WRONG! user set 0
const dark1 = settings.darkMode || true;   // true — WRONG! user set false

// ✅ Correct — ?? only triggers on null/undefined
const vol2  = settings.volume   ?? 50;     // 0    — correct ✅
const dark2 = settings.darkMode ?? true;   // false — correct ✅

// Combine with ?.
const city = user?.address?.city ?? "Unknown";
console.log(city); // "Unknown" if address is null

7. Logical Assignment & More Modern Features

ES2021 added logical assignment operators and several other small but powerful features that clean up common patterns significantly:

ES2021+ — Logical Assignment & Modern Features
// ── Logical Assignment Operators ──

let a = null;
let b = "existing";

// ??= assign only if null/undefined
a ??= "default";  // a = "default" ← was null
b ??= "default";  // b = "existing" ← unchanged

let count = 0;
let name  = "Waheed";

// ||= assign only if falsy
count ||= 10;   // count = 10 ← was 0 (falsy)
name  ||= "Guest"; // name = "Waheed" ← unchanged

// &&= assign only if truthy
let user = { name: "Waheed" };
user &&= { ...user, verified: true }; // updates if user exists

// ── Array.at() — negative indexing ──
const arr = [1, 2, 3, 4, 5];
console.log(arr.at(0));   // 1  ← first
console.log(arr.at(-1));  // 5  ← last
console.log(arr.at(-2));  // 4  ← second to last

// ── String.replaceAll() ──
const str = "cat and cat and cat";
console.log(str.replaceAll("cat", "dog"));
// "dog and dog and dog"

// ── Object shorthand ──
const city = "Faisalabad";
const age  = 22;

// Old way
const obj1 = { city: city, age: age };
// Shorthand — when key = variable name
const obj2 = { city, age }; // ← same result!

// ── Computed property keys ──
const key  = "name";
const obj3 = { [key]: "Waheed" };
console.log(obj3.name); // "Waheed"
Pro Tip #1 — Use ?? over || for defaults
Whenever you are providing a fallback for a value that might be missing — always prefer ?? over ||. The || operator treats 0, empty string, and false as "missing" which is almost never what you want in real applications with user data.
Pro Tip #2 — Chain ?. with ??
The combination user?.address?.city ?? "Unknown" is the gold standard for safely reading nested API data. Read it as: "get the city if it exists, otherwise return Unknown". You will write this pattern hundreds of times in React apps.
Pro Tip #3 — Object shorthand in React
Object shorthand { name, age } is used constantly in React — when passing props, creating state objects, and returning data from functions. When a variable name matches the key name you want — skip the repetition and use shorthand.

8. Try It Yourself

Edit the code and click Run Code. Try all the ES6+ features!

✏️ Try it Yourself — ES6+ Features
OUTPUT
// Click Run Code to see output
💡 Edit the code and click Run!

9. Practice Tasks

Task 1 — Easy: Profile Card Builder

Create a buildCard(user) function using destructuring in the parameters. Use template literals to build an HTML card string with name, city, salary, and skills. Use ?? for defaults and ?.skills?.join() safely. Return the HTML string.

Task 2 — Medium: Config Merger

Create a mergeConfig(defaults, userConfig) function. Use spread to merge objects. Use ??= to set any missing required fields to defaults. Use ?? for optional values. Handle nested objects safely with ?.. Return the final merged config object.

Task 3 — Hard: API Response Processor

Simulate an API response object with nested data — some properties may be null or undefined. Write a processUser(apiResponse) function that safely extracts: name (or "Anonymous"), email (or "Not provided"), city from nested address (or "Unknown"), first skill from skills array (or "No skills"), verified status (default false). Use ?., ??, destructuring, and template literals together. Log a formatted profile summary.

Next Lesson
Day 19 — Promises & Async/Await — Deep Dive
View Full Roadmap →
Enjoying this roadmap?
Follow Muhammad Waheed Asghar for daily JavaScript tips and updates!

Popular Posts