JavaScript ES6+ Features Cheat Sheet
Quick-reference for modern JavaScript features from ES6 through ES2024. Each section includes copy-ready snippets with inline output comments.
Object Destructuring
Extract properties from objects into variables. Supports renaming, defaults, and nesting.
const user = { name: 'Alice', age: 30, role: 'admin' };
const { name, age } = user;
name // => 'Alice'
age // => 30const { name: userName, email = 'N/A' } = { name: 'Bob' };
userName // => 'Bob'
email // => 'N/A'const config = { db: { host: 'localhost', port: 5432 } };
const { db: { host, port } } = config;
host // => 'localhost'
port // => 5432const { id, ...rest } = { id: 1, name: 'Alice', age: 30 };
id // => 1
rest // => { name: 'Alice', age: 30 }Array Destructuring
Extract elements by position. Supports skipping, defaults, rest, and swapping.
const [a, b, c] = [1, 2, 3];
a // => 1
b // => 2
c // => 3const [, , third] = ['a', 'b', 'c'];
third // => 'c'const [head, ...tail] = [1, 2, 3, 4, 5];
head // => 1
tail // => [2, 3, 4, 5]let x = 1, y = 2;
[x, y] = [y, x];
x // => 2
y // => 1function getRange() {
return [0, 100];
}
const [min, max] = getRange();Spread and Rest Operators
Spread (...) expands iterables into individual elements. Rest (...) collects remaining elements into an array or object.
const a = [1, 2];
const b = [3, 4];
[...a, ...b] // => [1, 2, 3, 4]
[0, ...a, 5] // => [0, 1, 2, 5]const defaults = { theme: 'dark', lang: 'en' };
const prefs = { lang: 'fr', fontSize: 14 };
const merged = { ...defaults, ...prefs };
// => { theme: 'dark', lang: 'fr', fontSize: 14 }Later spreads overwrite earlier ones. Spread only makes a shallow copy — nested objects are still shared references.
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3) // => 6const args = [1, 5, 3, 9, 2];
Math.max(...args) // => 9Template Literals
Backtick strings support interpolation, multiline text, and tagged templates.
const name = 'Alice';
const greeting = `Hello, ${name}!`; // => 'Hello, Alice!'
`Total: $${(9.99 * 3).toFixed(2)}` // => 'Total: $29.97'const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`;function highlight(strings, ...values) {
return strings.reduce((acc, str, i) =>
acc + str + (values[i] !== undefined ? `<b>${values[i]}</b>` : ''), ''
);
}
const name = 'Alice';
highlight`Hello, ${name}!`
// => 'Hello, <b>Alice</b>!'Tags receive string parts and interpolated values separately. They do NOT auto-escape — your tag function must handle sanitization.
String.raw`\n\t` // => '\\n\\t' (literal backslashes)
String.raw`C:\Users\docs` // => 'C:\\Users\\docs'Arrow Functions
Concise function syntax with lexical this binding. No own this, arguments, or prototype.
const double = (x) => x * 2;
const add = (a, b) => a + b;
const greet = () => 'hello';const process = (items) => {
const filtered = items.filter(Boolean);
return filtered.map(x => x.toUpperCase());
};const makeUser = (name) => ({ name, role: 'user' });
makeUser('Alice') // => { name: 'Alice', role: 'user' }Without parentheses, { } is a block body, not an object. Wrap in () to return an object literal.
class Timer {
constructor() {
this.seconds = 0;
// Arrow inherits `this` from constructor
setInterval(() => {
this.seconds++; // `this` is the Timer instance
}, 1000);
}
}Arrow functions inherit this from the enclosing scope. Regular functions create their own this based on how they are called.
Default Parameters
Set fallback values for function parameters. Defaults are evaluated at call time, not definition time.
function greet(name = 'World') {
return `Hello, ${name}!`;
}
greet() // => 'Hello, World!'
greet('Alice') // => 'Hello, Alice!'function createUser({ name = 'Anonymous', role = 'user' } = {}) {
return { name, role };
}
createUser() // => { name: 'Anonymous', role: 'user' }
createUser({ name: 'Alice' }) // => { name: 'Alice', role: 'user' }function show(x = 10) {
return x;
}
show(undefined) // => 10 (default used)
show(null) // => null (NOT default)
show(0) // => 0 (NOT default)Only undefined triggers the default. null, 0, and "" are valid values that bypass the default.
Optional Chaining (?.) and Nullish Coalescing (??)
Safely access nested properties and provide defaults for null/undefined without falsy confusion.
const user = { address: { city: 'NYC' } };
user?.address?.city // => 'NYC'
user?.profile?.avatar // => undefined (no error)
user?.getEmail?.() // => undefined (safe method call)const data = { items: ['a', 'b'] };
data?.items?.[0] // => 'a'
data?.missing?.[0] // => undefinednull ?? 'default' // => 'default'
undefined ?? 'default' // => 'default'
0 ?? 'default' // => 0 (not nullish!)
'' ?? 'default' // => '' (not nullish!)
false ?? 'default' // => false (not nullish!)?? only checks null/undefined. || checks all falsy values (0, "", false, null, undefined, NaN). Use ?? when 0 or "" are valid values.
const port = config?.server?.port ?? 3000;
const name = user?.profile?.name ?? 'Anonymous';Logical Assignment (||=, &&=, ??=)
Shorthand for assigning values based on logical conditions. ES2021.
let config = { timeout: null };
config.timeout ??= 5000;
config.timeout // => 5000
let port = 8080;
port ??= 3000;
port // => 8080 (not nullish, keeps value)let name = '';
name ||= 'Anonymous';
name // => 'Anonymous' (empty string is falsy)
let count = 0;
count ||= 10;
count // => 10 (0 is falsy — probably a bug!)Use ??= when 0, false, or "" are valid values. Use ||= when you want to replace any falsy value.
let user = { name: 'Alice' };
user.name &&= user.name.toUpperCase();
user.name // => 'ALICE'
let empty = '';
empty &&= 'replaced';
empty // => '' (falsy, not assigned)Modules: import/export
ES modules use static import/export for code organization. Named exports allow multiple values; default exports provide a single main export.
// math.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
// main.js
import { PI, add } from './math.js';// logger.js
export default function log(msg) {
console.log(msg);
}
// main.js
import log from './logger.js';import { add as sum } from './math.js';
import * as math from './math.js';
math.add(1, 2) // => 3const { default: Chart } = await import('./chart.js');
// Module loaded on demand, not at startupDynamic import() returns a Promise and can be used anywhere. Static import must be at the top level.
Symbol and Iterators
Symbols create unique, non-string property keys. Built-in symbols like Symbol.iterator power the for...of loop.
const id = Symbol('id');
const user = { [id]: 123, name: 'Alice' };
user[id] // => 123
Object.keys(user) // => ['name'] (symbols hidden)// Symbol.iterator makes an object iterable
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { done: true };
}
};
}
};
[...range] // => [1, 2, 3, 4, 5]const s1 = Symbol.for('app.id');
const s2 = Symbol.for('app.id');
s1 === s2 // => true (same global symbol)WeakMap and WeakSet
Weak collections hold object references without preventing garbage collection. Keys must be objects, and entries are not enumerable.
const privateData = new WeakMap();
class User {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
// When a User instance is GC'd, its WeakMap entry is tooconst cache = new WeakMap();
function expensiveCompute(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = /* heavy work */ obj;
cache.set(obj, result);
return result;
}const visited = new WeakSet();
function traverse(node) {
if (visited.has(node)) return; // prevent cycles
visited.add(node);
// process node...
}WeakMap/WeakSet cannot be iterated (no forEach, no size). Use Map/Set if you need enumeration.
More Modern Features
Additional ES2020+ features that improve everyday code.
const name = 'Alice';
const age = 30;
const user = { name, age }; // => { name: 'Alice', age: 30 }
const obj = {
greet() { return 'hello'; }, // shorthand method
['key' + 1]: 'dynamic', // computed property
};for (const char of 'hello') {
console.log(char); // h, e, l, l, o
}
for (const [key, val] of new Map([['a', 1], ['b', 2]])) {
console.log(`${key}: ${val}`);
}const original = { a: 1, nested: { b: 2 } };
const deep = structuredClone(original);
deep.nested.b = 99;
original.nested.b // => 2 (unaffected)structuredClone handles nested objects, arrays, Date, Map, Set, and more. Does not clone functions or DOM nodes.
const { promise, resolve, reject } = Promise.withResolvers();
// Expose resolve/reject outside the Promise constructor
setTimeout(() => resolve('done'), 1000);
await promise // => 'done'