Here’s a detailed, practical explanation of every major ES6 (ECMAScript 2015) feature, with real-world examples, common pitfalls, and when to use each one.
1. let and const
let: Block-scoped, mutable, no hoisting to the top (Temporal Dead Zone)const: Block-scoped, immutable binding (but the object/array itself can change)
// Good
let count = 0;
const API_URL = "https://api.example.com";
// const objects are mutable
const user = { name: "John" };
user.name = "Jane"; // OK
user.age = 30; // OK
// user = {} // TypeError!
// Temporal Dead Zone
console.log(x); // ReferenceError
let x = 10;
Best Practice: Default to const, use let only when reassignment is needed. Never use var.
2. Arrow Functions
- Concise syntax
- Lexical
this(inherits from surrounding scope)
// Syntax variations
param => expression
(param1, param2) => expression
(param) => { statements }
() => { return value }
// Real-world example: this binding
class Timer {
start() {
setInterval(() => {
console.log(this); // Timer instance, not global!
}, 1000);
}
}
// No own arguments object
const fn = (...args) => console.log(arguments); // Error!
When NOT to use arrows:
- Object methods (breaks
this) - Prototype methods
- Functions that need
argumentsornew
3. Template Literals
const name = "Sara", age = 25;
`Hello ${name}, you are ${age + 1} next year!`
// Tagged templates
function highlight(strings, ...values) {
return strings.reduce((result, str, i) =>
`${result}${str}<b>${values[i] || ''}</b>`, '');
}
const result = highlight`Hello ${name}, you have ${age} years`;
4. Destructuring Assignment
// Arrays
const [first, , third] = [1, 2, 3]; // third = 3
const [a, ...rest] = [1, 2, 3, 4]; // rest = [2,3,4]
// Objects
const {name, age, address: {city} = {}} = user;
// Default values
const {settings = {theme: 'dark'}} = config;
// Function parameters
function draw({x, y, color = 'red'}) { ... }
5. Default, Rest, and Spread
// Default parameters
function ajax(url, method = 'GET', data = null) { ... }
// Rest parameter (replaces arguments)
function sum(...numbers) {
return numbers.reduce((a,b) => a + b, 0);
}
// Spread
Math.max(...[1, 5, 3]) // 5
const arr = [...arr1, 10, ...arr2]
const obj = { ...obj1, age: 30, ...obj2 } // later properties win
6. Classes (Syntactic Sugar)
class Animal {
constructor(name) {
this.name = name;
}
static fromJSON(json) { // static method
return new this(JSON.parse(json));
}
speak() { // goes on prototype
console.log(`${this.name} makes noise`);
}
}
class Dog extends Animal {
speak() {
super.speak();
console.log("Woof!");
}
}
// Private fields (ES2022, but often grouped with class features)
class Counter {
#count = 0; // truly private
increment() { this.#count++; }
}
7. Modules (import / export)
// Named exports
export const PI = 3.14;
export function add(a, b) { return a + b; }
// Default export
export default class Calculator { ... }
// Importing
import Calculator from './calc.js';
import { PI, add } from './math.js';
import * as math from './math.js';
import { add as plus } from './math.js';
8. Promises
// Creation
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve("Done!"), 1000);
});
// Chaining
fetch('/users')
.then(res => res.json())
.then(users => console.log(users))
.catch(err => console.error(err))
.finally(() => console.log("Finished"));
// Promise.all, race, allSettled, any
Promise.all([p1, p2, p3]).then(values => ...);
9. Enhanced Object Literals
const name = "API", version = 2;
const api = {
name, // same as name: name
[`${name}_v${version}`]: true,
get latest() { return version; },
fetch() { ... } // method shorthand
};
10. Symbol
const id = Symbol('id');
const secret = Symbol.for('secret'); // global registry
obj[id] = 123; // truly unique key
Symbol.keyFor(secret); // "secret"
Use case: Adding non-enumerable, collision-safe properties.
11. for…of and Iterables
for (let char of "hello") { ... }
for (let key of map.keys()) { ... }
for (let [key, value] of map.entries()) { ... }
// Custom iterable
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let i = this.start;
return {
next: () => ({
value: i,
done: i++ >= this.end
})
};
}
}
for (let n of new Range(1, 5)) console.log(n);
12. Generators
function* generateIds() {
let i = 1;
while (true) yield i++;
}
const gen = generateIds();
gen.next().value; // 1, 2, 3...
// Delegation
function* gen2() {
yield 1;
yield* [10, 20, 30]; // delegate to array
yield 4;
}
Great for: infinite sequences, async flows (before async/await), custom iteration.
13. Map and Set
const map = new Map();
map.set(obj, "data"); // object keys OK!
map.set("key", "value");
const set = new Set();
set.add(1).add(2).add(1); // size === 2
14. WeakMap and WeakSet
- Keys are weakly referenced → no memory leaks
- No iteration, no clear(), no size
- Perfect for private data, caching, DOM metadata
const privateData = new WeakMap();
class User {
constructor(id) {
privateData.set(this, {id});
}
getId() { return privateData.get(this).id; }
}
15. Proxy
const handler = {
get(target, prop) {
if (!(prop in target)) {
console.warn(`Property ${prop} does not exist`);
}
return target[prop];
},
set(target, prop, value) {
if (prop === 'age' && value < 0) {
throw new Error("Age can't be negative");
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy({}, handler);
Use cases: validation, logging, virtual properties, revocable references.
16. Reflect API
- Cleaner alternative to old Object operations
- Works perfectly with Proxy traps
Reflect.defineProperty(obj, 'name', {value: 'John'});
Reflect.get(target, prop, receiver); // matches Proxy get trap
17–19. New Built-in Methods (Quick List)
// Array
Array.from(document.querySelectorAll('div'));
[1,2,3].find(x => x > 1); // 2
[1,2,3].includes(2); // true
// String
"hello".startsWith("he"); // true
"⭐".repeat(5);
// Number & Math
Number.isInteger(42); // true
Math.trunc(4.9); // 4
Math.sign(-5); // -1
Bonus: Most Important ES6 Features in Daily Use (2025)
const/let- Arrow functions
- Destructuring + rest/spread
- Template literals
- Classes +
extends import/export- Promises +
async/await(await came in ES2017 but built on ES6) Map/Set/WeakMap
Master these 8 and you’re writing modern JavaScript like a pro.
ES6 (ECMAScript 2015) and TypeScript
Here’s a clear, practical comparison between ES6 (ECMAScript 2015) and TypeScript — what they actually add to JavaScript, and how they differ in purpose and features.
| Feature | ES6 (Vanilla JavaScript) | TypeScript (Superset of JS) | Winner / When to Use |
|---|---|---|---|
| Core Purpose | Modernizes JavaScript syntax and behavior | Adds static typing + modern features on top of JS | — |
| Runs directly in browsers? | Yes (since ~2016–2017) | No → must be compiled to JS (usually ES6 or ES5) | ES6 |
| Type System | None (dynamic typing only) | Full static typing (interfaces, generics, enums, etc.) | TypeScript |
| let / const | Yes | Yes (identical) | Tie |
| Arrow Functions | Yes | Yes (identical) | Tie |
| Classes | Yes (syntactic sugar) | Yes + access modifiers (public, private, protected) + #private fields | TypeScript |
| Destructuring | Yes | Yes + type annotations in destructuring | TypeScript |
| Spread / Rest | Yes | Yes + typed spreads | TypeScript |
| Template Literals | Yes | Yes | Tie |
| Default / Rest Parameters | Yes | Yes + can type each parameter | TypeScript |
| Promises | Yes | Yes + can type what a Promise resolves to | TypeScript |
| async / await | ES2017 (but built on ES6 Promises) | Yes + full typing (async function(): Promise<User[]>) | TypeScript |
| Modules (import/export) | Yes (native ES modules) | Yes + type-only imports/exports, .d.ts files | TypeScript |
| Decorators | No (only experimental stage 2–3 for years) | Yes (widely used: @Component, @Injectable, etc.) | TypeScript |
| Generics | No | Yes (Array<T>, Promise<T>, custom generics) | TypeScript |
| Interfaces & Types | No | Yes (interface User { name: string }, type ID = string | number) | TypeScript |
| Enums | No (people fake them with objects) | Yes (enum Direction { Up, Down }) | TypeScript |
| Union & Intersection Types | No | Yes (string | number, TypeA & TypeB) | TypeScript |
| Tuple Types | Very limited (no label, no enforcement) | Full support [string, number] with labels | TypeScript |
| Null / Undefined handling | Loose (null == undefined) | Strict null checks (huge bug prevention) | TypeScript |
| Parameter Properties | No | Yesconstructor(public name: string) {} | TypeScript |
| Definite Assignment Assertion | No | Yes (name!: string) | TypeScript |
| Const Assertions | No | Yes (as const → literal types) | TypeScript |
| Mapped & Conditional Types | No | Yes (very powerful for type-level programming) | TypeScript |
Optional Chaining (?.) | ES2020 | Yes (since TS 3.7) + full type narrowing | Tie (now both) |
Nullish Coalescing (??) | ES2020 | Yes + type safe | Tie |
Private Fields (#field) | ES2022 | Yes (via # or private keyword) | Tie |
| Top-level await | ES2022 (only in modules) | Yes (in modules) | Tie |
| Project-wide type safety | No | Yes (catches bugs at compile time) | TypeScript |
| IDE Autocomplete / Refactoring | Basic | Excellent (thanks to types) | TypeScript |
| Learning Curve | Low to medium | Medium to high (especially advanced types) | ES6 easier |
| Build Step Required | No (if targeting modern browsers) | Yes (tsc or Babel + plugin) | ES6 (no build) |
Real-World Summary (2025)
| Scenario | Best Choice | Why |
|---|---|---|
| Small script, learning JS, CodePen | Pure ES6 | No build, instant run |
| Large application, team > 3 people | TypeScript | Fewer runtime bugs, better refactoring |
| React / Angular / NestJS project | TypeScript (required or strongly recommended) | Official support, amazing DX |
| Library you want everyone to use easily | Publish in JS + provide .d.ts (TypeScript declarations) | Users get types without writing TS |
| Need decorators (@Component, @Injectable) | TypeScript | Stable decorators |
| Want maximum type-level magic (Zod, tRPC, etc.) | TypeScript | Only possible here |
| Full-stack with Node.js + Express/Fastify | TypeScript | End-to-end type safety (especially with Prisma, tRPC) |
Bottom Line
- ES6 gave us modern JavaScript syntax (arrows, classes, modules, etc.).
- TypeScript takes ES6 (and newer) syntax + adds a world-class static type system on top.
Today (2025) in professional development:
- Almost all new enterprise/frontend/backend codebases use TypeScript.
- You still need to know ES6+ features — TypeScript is 99% just typed JavaScript.
Recommendation:
Learn ES6+ first → then immediately learn TypeScript.
You’ll write ES6-style code every day, but with TypeScript’s safety net.
