ES6 (ECMAScript 2015) features were major upgrades that made JavaScript cleaner, safer, and more powerful. These features modernized the language by addressing long-standing issues like scope leakage and "callback hell."
Block Scope: let and const
Before ES6, all variables used var, which is function-scoped. This often led to bugs where variables "leaked" out of if statements or loops. let and const are block-scoped, meaning they only exist within the curly braces {} where they are defined.
| Feature | Pre-ES6 (var) | Post-ES6 (let/const) |
|---|---|---|
| Scope | Function | Block |
| Leakage | Leaks out of if/for blocks | Contained within the block |
| Accessed before declaration | Returns undefined silently | Throws ReferenceError |
Example:
if (true) {
let name = "Wariz";
const age = 17;
}
console.log(name); // ❌ ReferenceError: name is not defined
Arrow Functions () => {}
Arrow functions provide a shorter syntax for writing functions. They allow for concise one-liners and handle the this keyword differently than traditional functions.
- Before ES6:
function greet(name) { return "Hello " + name; } - ES6 Arrow Function:
const greet = name => `Hello ${name}`;
Template Literals
Template literals replaced messy string concatenation (using +) with backticks (`) and placeholders (${}). This makes building strings much more readable.
const name = "Wariz";
// With ES6 Template Literals
const greeting = `Hello ${name}!`;
console.log(greeting); // Hello Wariz!
Destructuring
Destructuring allows you to "unpack" values from arrays or properties from objects into distinct variables instantly.
- Object Destructuring:
const user = { name: "Wariz", age: 17 }; const { name, age } = user; - Array Destructuring:
const numbers = [10, 20, 30]; const [first, second] = numbers;
Rest and Spread Operators (...)
While they use the same syntax (...), they serve opposite purposes:
1. Rest Operator
Collects multiple individual elements into a single array. Great for functions with unknown numbers of arguments.
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
2. Spread Operator
Expands or "spreads" an array or object into a new one.
const nums = [1, 2, 3];
const newNums = [...nums, 4, 5]; // [1, 2, 3, 4, 5]
Modules (import / export)
Modules allow you to organize code into independent, reusable files. Instead of one massive file, you can export specific pieces of code from one file and import them into another.
- Exporting (math.js):
export const add = (a, b) => a + b; - Importing (app.js):
import { add } from './math.js';
Promises and async/await
Promises were introduced to handle Asynchronous JavaScript. Before Promises, developers used callbacks, which often led to "callback hell" (nested, unreadable code).
A Promise represents a task that will finish in the future. It can be in one of three states:
- Pending: Initial state.
- Resolved (Fulfilled): The task completed successfully.
- Rejected: The task failed.
const fetchData = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("Data loaded");
} else {
reject("Error occurred");
}
});
fetchData
.then(result => console.log(result)) // Runs on success
.catch(error => console.log(error)); // Runs on failure
async/await
While Promises solved callback hell, chaining multiple .then() calls can still become difficult to read. async/await is the modern syntax built on top of Promises that makes asynchronous code read more like synchronous code — cleaner and easier to follow.
async— Marks a function as asynchronous. Anasyncfunction always returns a Promise.await— Pauses execution inside anasyncfunction until the Promise resolves, then returns the result.
async function loadData() {
try {
const result = await fetchData;
console.log(result); // "Data loaded"
} catch (error) {
console.log(error); // Runs if the Promise is rejected
}
}
loadData();
The try/catch block replaces .then() and .catch() — try handles the success case and catch handles the failure. All three approaches (callbacks → Promises → async/await) exist in JavaScript today, which is why you will encounter all of them in real codebases.