Skip to main content
Web DevelopmentES2024

JavaScript ES6+ Cheat Sheet

Comprehensive reference guide for modern JavaScript (ES6 and beyond) including destructuring, async/await, functional programming, and professional development best practices.

Quick Reference

Most commonly used ES6+ features at a glance

FeatureOld SyntaxNew Syntax (ES6+)
Variable Declarationvar name = 'John';const name = 'John';
Arrow Functionfunction(x) { return x * 2; }(x) => x * 2
String Interpolationname + ' is ' + age + ' years old'`${name} is ${age} years old`
Destructuringconst name = obj.name;const { name } = obj;
Rest Parametersfunction(...args) {}(...args) => {}
Default Parametersfunction(x) { x = x || 10; }const fn = (x = 10) => {}
Promise Chain.then().then()async/await
Object Shorthand{ name: name, age: age }{ name, age }
Spread Operator[...arr1, ...arr2]const arr = [...arr1, ...arr2]
Nullish Coalescingx || 'default'x ?? 'default'
About ES6+
Modern JavaScript (ES6/ES2015 and beyond) introduces powerful features that dramatically improve code readability and productivity. These features are supported in all modern browsers and Node.js environments.

Variable Declarations

const, let, and var with best practices

const vs let vs var
Choose the right declaration keyword
// ✓ BEST: Use const by default
const name = 'John';
const age = 30;

// ✓ GOOD: Use let when reassignment needed
let counter = 0;
counter++;

// ✗ AVOID: var has function scope issues
var oldVar = 'value'; // Don't use!

// Block scope demonstration
if (true) {
  const blockScoped = 'only in block';
  let alsoBlockScoped = 'also in block';
  var functionScoped = 'leaks out'; // Bad!
}

// Const doesn't prevent mutation
const user = { name: 'John' };
user.name = 'Jane'; // ✓ Allowed
user = {}; // ✗ Error: reassignment not allowed

// const with arrays
const numbers = [1, 2, 3];
numbers.push(4); // ✓ Allowed
numbers = []; // ✗ Error
Best Practice
Always prefer const by default. Use let only when you need reassignment. Never use var in modern JavaScript.

Arrow Functions

Modern function syntax

Arrow Function Syntax
Concise function declarations with lexical this
// Basic arrow function
const add = (a, b) => a + b;
add(2, 3); // 5

// Single parameter (parentheses optional)
const square = x => x * x;
square(4); // 16

// No parameters
const greeting = () => 'Hello';

// Multi-line body (need braces and return)
const calculate = (a, b) => {
  const sum = a + b;
  const product = a * b;
  return sum + product;
};

// Implicit return of object
const createUser = (name, age) => ({ name, age });

// Array methods with arrows
const numbers = [1, 2, 3, 4, 5];
numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
numbers.filter(n => n > 2); // [3, 4, 5]

// Lexical this binding
const person = {
  name: 'John',
  greet: function() {
    const arrow = () => {
      console.log(this.name); // 'John' ✓
    };
    arrow();
  }
};
Use Function Expressions for Methods
Don't use arrow functions as object methods - use regular functions or shorthand so this refers to the object, not the parent scope.

Destructuring

Extract values from arrays and objects

Object Destructuring
Extract properties by name
// Basic destructuring
const user = { name: 'John', age: 30, city: 'NYC' };
const { name, age } = user;

// Rename variables
const { name: fullName, age: years } = user;

// Default values
const { name, role = 'user' } = user;
// role = 'user' if not in object

// Nested destructuring
const person = {
  name: 'John',
  address: { city: 'NYC', zip: '10001' }
};
const { name, address: { city, zip } } = person;

// Rest operator (remaining properties)
const { name, ...rest } = user;
// rest = { age: 30, city: 'NYC' }

// In function parameters
function displayUser({ name, age }) {
  console.log(`${name} is ${age}`);
}
displayUser(user);

// Computed property names
const key = 'email';
const { [key]: userEmail } = { email: 'john@example.com' };
Array Destructuring
Extract elements by position
// Basic array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second] = colors;
// first = 'red', second = 'green'

// Skip elements
const [, , third] = colors;
// third = 'blue'

// Rest operator
const [primary, ...others] = colors;
// primary = 'red', others = ['green', 'blue']

// Default values
const [a, b, c, d = 'yellow'] = colors;
// d = 'yellow'

// Swapping variables
let x = 1, y = 2;
[x, y] = [y, x];
// x = 2, y = 1

// Destructuring in loops
const users = [
  { name: 'John', age: 30 },
  { name: 'Jane', age: 25 }
];
for (const { name, age } of users) {
  console.log(`${name}: ${age}`);
}

// Nested array destructuring
const nested = [1, [2, 3], 4];
const [a, [b, c], d] = nested;
// a=1, b=2, c=3, d=4
Pro Tip
Use destructuring in function parameters to make it clear what properties/elements you're using. It improves code readability and serves as documentation.

Template Literals & String Methods

Modern string handling

Template Literals
String interpolation with backticks
// Basic interpolation
const name = 'John';
const age = 30;
const greeting = `Hello, ${name}! You are ${age} years old.`;

// Expressions in templates
const result = `Total: ${10 + 20}`;
const formatted = `${name.toUpperCase()} is ${age > 25 ? 'adult' : 'young'}`;

// Multi-line strings
const html = `
  <div>
    <h1>${title}</h1>
    <p>${description}</p>
  </div>
`;

// Tagged template literals (advanced)
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) => 
    acc + str + (values[i] ? `<mark>${values[i]}</mark>` : ''), '');
}

const result = highlight`Hello ${name}, you have ${count} messages`;
// Returns: Hello <mark>John</mark>, you have <mark>5</mark> messages

// String methods
const text = '  Hello World  ';
text.trim(); // 'Hello World'
text.toUpperCase(); // '  HELLO WORLD  '
text.includes('World'); // true
text.startsWith('  '); // true
text.replace(/o/g, '0'); // '  Hell0 W0rld  '
text.split(' '); // ['', '', 'Hello', 'World', '', '']
text.padStart(20, '*'); // '***  Hello World  '
Migration Tip
Replace all CONCATENATE-style string building with template literals for cleaner, more readable code.

Spread & Rest Operators

... for spreading and collecting values

Spread Operator
Expand iterables into individual elements
// Spread with arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// [1, 2, 3, 4, 5, 6]

// Insert in middle
const result = [1, 2, ...arr1, 3];
// [1, 2, 1, 2, 3, 3]

// Copy arrays
const copy = [...arr1]; // Shallow copy

// Spread with objects
const user = { name: 'John', age: 30 };
const updated = { ...user, age: 31 };
// { name: 'John', age: 31 }

// Merge objects
const defaults = { theme: 'light' };
const config = { ...defaults, ...userSettings };

// Remove property while copying
const { age, ...rest } = user;
const newUser = { ...rest }; // Without age

// Spread in function calls
const numbers = [1, 2, 3];
Math.max(...numbers); // 3

// Spread strings
const chars = [...'hello'];
// ['h', 'e', 'l', 'l', 'o']

Rest Parameters

Collect remaining arguments

Rest in Functions
Accept variable number of arguments
// Rest parameters
function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4, 5); // 15

// Mix regular and rest params
function greet(greeting, ...names) {
  names.forEach(name => 
    console.log(`${greeting}, ${name}!`));
}
greet('Hello', 'John', 'Jane', 'Bob');

// Rest must be last parameter
function process(first, second, ...rest) {
  console.log(first, second, rest);
}
process(1, 2, 3, 4, 5);
// Output: 1 2 [3, 4, 5]

// In destructuring
const [first, ...rest] = [1, 2, 3, 4];
// first = 1, rest = [2, 3, 4]

const { name, ...others } = user;
// Collects all properties except name

// Rest in arrow functions
const multiply = (...nums) => 
  nums.reduce((a, b) => a * b, 1);

multiply(2, 3, 4); // 24

Default & Computed Parameters

Smart parameter handling

Default Parameters & Computed Values
Set defaults and compute values in parameters
// Default parameters
function greet(name = 'Guest', greeting = 'Hello') {
  console.log(`${greeting}, ${name}!`);
}
greet(); // Hello, Guest!
greet('John'); // Hello, John!
greet('Jane', 'Hi'); // Hi, Jane!

// Computed defaults
function createConfig(env = process.env.NODE_ENV || 'development') {
  // ...
}

// Function as default
function withCallback(data, callback = (x) => console.log(x)) {
  callback(data);
}

// Destructuring with defaults
function displayUser({ 
  name = 'Anonymous', 
  age = 0, 
  role = 'user' 
} = {}) {
  console.log(`${name} (${age}) - ${role}`);
}

displayUser(); // Anonymous (0) - user
displayUser({ name: 'John', age: 30 });

// Default with conditional logic
const config = {
  timeout: options?.timeout ?? 5000,
  retries: options?.retries ?? 3,
  debug: options?.debug ?? false
};

// Using nullish coalescing
const port = process.env.PORT ?? 3000;
const theme = userSettings?.theme ?? 'light';
Default Values
Use ?? (nullish coalescing) for defaults instead of ||to avoid false positives with falsy values like 0 or empty string.

Modern Operators

Optional chaining and nullish coalescing

Optional Chaining (?.)
Safe property access without errors
// Optional chaining for properties
const user = { name: 'John', address: { city: 'NYC' } };
user?.name; // 'John'
user?.address?.city; // 'NYC'
user?.contact?.phone; // undefined (no error!)

// With arrays
const items = null;
items?.[0]; // undefined (no error)

// With functions
const callback = null;
callback?.(); // undefined (no error)

// In conditional checks
if (user?.profile?.verified) {
  // Safe to proceed
}

// With method calls
const result = user?.getData?.();

// Combining with nullish coalescing
const city = user?.address?.city ?? 'Unknown';

// Short-circuit evaluation
const depth = obj?.level1?.level2?.level3 ?? 'default';

// In loops
users?.forEach(user => {
  console.log(user.name);
});

Nullish Coalescing (??)

Provide defaults for null/undefined

Nullish Coalescing vs OR
Choose the right operator for defaults
// Nullish coalescing
const port = process.env.PORT ?? 3000;
// 8080 (if set), 3000 (if not set)

// Difference from OR operator
const value1 = 0 || 10; // 10 (0 is falsy)
const value2 = 0 ?? 10; // 0 (0 is not nullish)

const name1 = '' || 'Anonymous'; // 'Anonymous'
const name2 = '' ?? 'Anonymous'; // '' (empty is not nullish)

// When to use ??
const config = {
  timeout: userSettings?.timeout ?? 5000,
  retries: userSettings?.retries ?? 3,
  debug: userSettings?.debug ?? false
};

// Chaining nullish coalescing
const result = val1 ?? val2 ?? val3 ?? 'default';

// In destructuring
const { name = 'Guest' } = user; // Default value
const { name = user?.name ?? 'Anonymous' } = options;

// NOT with logical AND
const result = condition && (value ?? 'default');

// Safe API responses
const data = response?.data ?? {};
const items = response?.items ?? [];
const status = response?.status ?? 'unknown';

Object Methods & Shorthand

Modern object creation and manipulation

Object Shorthand
Concise object syntax
// Property shorthand
const name = 'John';
const age = 30;
const email = 'john@example.com';

// Old way
const user1 = {
  name: name,
  age: age,
  email: email
};

// Shorthand
const user2 = { name, age, email };

// Method shorthand
const obj = {
  // Old way
  oldMethod: function() {
    return 'result';
  },
  
  // Shorthand
  newMethod() {
    return 'result';
  },
  
  // Arrow function (use sparingly)
  arrowMethod: () => {
    // Note: this won't refer to obj
  }
};

// Computed property names
const propName = 'dynamicKey';
const dynamic = {
  [propName]: 'value',
  [Math.random()]: 'random',
  [`key_${id}`]: 'computed'
};

// Getters and setters
const person = {
  _age: 0,
  get age() {
    return this._age;
  },
  set age(value) {
    if (value >= 0) this._age = value;
  }
};
person.age = 25; // Calls setter
Object Methods
Built-in object utilities
// Object.keys() - get property names
const user = { name: 'John', age: 30 };
Object.keys(user); // ['name', 'age']

// Object.values() - get property values
Object.values(user); // ['John', 30]

// Object.entries() - get [key, value] pairs
Object.entries(user);
// [['name', 'John'], ['age', 30]]

// Iterate entries
for (const [key, value] of Object.entries(user)) {
  console.log(`${key}: ${value}`);
}

// Object.assign() - merge objects
const defaults = { theme: 'light', lang: 'en' };
const config = Object.assign({}, defaults, userSettings);
// Or use spread: { ...defaults, ...userSettings }

// Object.freeze() - make immutable
const frozen = Object.freeze({ x: 1 });
frozen.x = 2; // Silent fail

// Object.seal() - prevent adding/removing
const sealed = Object.seal({ x: 1 });
sealed.y = 2; // Fails
sealed.x = 2; // OK

// Object.create() - set prototype
const parent = { greet() { return 'hi'; } };
const child = Object.create(parent);

// Object.defineProperty() - define properties
Object.defineProperty(obj, 'prop', {
  value: 'test',
  writable: false,
  enumerable: true
});

Array Methods

Functional array operations

Transformation Methods
map, filter, reduce and others
// map() - transform elements
const numbers = [1, 2, 3, 4, 5];
numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]

// filter() - select elements
numbers.filter(n => n > 2);
// [3, 4, 5]

// reduce() - accumulate value
numbers.reduce((sum, n) => sum + n, 0);
// 15

// Complex reduce - group by property
const users = [
  { role: 'admin', name: 'John' },
  { role: 'user', name: 'Jane' },
  { role: 'admin', name: 'Bob' }
];

const byRole = users.reduce((groups, user) => {
  groups[user.role] = groups[user.role] || [];
  groups[user.role].push(user.name);
  return groups;
}, {});

// find() - get first match
users.find(u => u.role === 'admin');

// findIndex() - get index of match
users.findIndex(u => u.name === 'Jane');

// some() - check if any match
users.some(u => u.role === 'admin'); // true

// every() - check if all match
numbers.every(n => n > 0); // true

// includes() - simple existence check
numbers.includes(3); // true
Advanced Array Methods
flat, flatMap, sort, and more
// flat() - flatten nested arrays
const nested = [1, [2, 3], [4, [5, 6]]];
nested.flat(); // [1, 2, 3, 4, [5, 6]]
nested.flat(2); // [1, 2, 3, 4, 5, 6]

// flatMap() - map then flatten
const sentences = ['hello world', 'foo bar'];
sentences.flatMap(s => s.split(' '));
// ['hello', 'world', 'foo', 'bar']

// sort() - sort array in place
const words = ['zebra', 'apple', 'banana'];
words.sort(); // ['apple', 'banana', 'zebra']

// Numeric sort (ascending)
numbers.sort((a, b) => a - b);

// Sort objects by property
users.sort((a, b) => 
  a.name.localeCompare(b.name));

// reverse() - reverse order
numbers.reverse();

// join() - convert to string
numbers.join(', '); // '1, 2, 3, 4, 5'

// slice() - create copy/subset
numbers.slice(1, 3); // [2, 3]

// concat() - combine arrays
[1, 2].concat([3, 4]);
// or use spread: [...[1,2], ...[3,4]]

// indexOf() & lastIndexOf()
numbers.indexOf(3); // 2
numbers.lastIndexOf(3); // 2
Pro Tip
Master the functional array methods - they are fundamental to modern JavaScript and used extensively in frameworks like React.

Promises

Handle asynchronous operations

Promise Basics
Create and handle promises
// Create a promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
  }, 1000);
});

// Handle promise
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('Complete'));

// Promise methods
Promise.all([p1, p2, p3])
  .then(results => {
    // All promises resolved
  });

Promise.race([p1, p2])
  .then(result => {
    // First promise resolved
  });

Promise.allSettled([p1, p2])
  .then(results => {
    // All settled (resolved or rejected)
  });

// Promise.any() - first successful
Promise.any([p1, p2])
  .then(result => {
    // First successful promise
  });

// Chaining promises
fetch('/api/user')
  .then(res => res.json())
  .then(data => {
    console.log(data);
    return fetch(`/api/posts/${data.id}`);
  })
  .then(res => res.json())
  .then(posts => console.log(posts))
  .catch(err => console.error(err));

Async/Await

Cleaner promise handling

Async/Await Syntax
Write async code like synchronous
// Basic async function
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

// Arrow function async
const getData = async () => {
  const response = await fetch('/api/data');
  return response.json();
};

// Multiple awaits
async function processUser(id) {
  try {
    const user = await getUser(id);
    const posts = await getPosts(user.id);
    const comments = await getComments(posts[0].id);
    return { user, posts, comments };
  } catch (error) {
    console.error(error);
  }
}

// Parallel execution (don't wait sequentially)
async function parallel() {
  // Bad - sequential (slow)
  const user = await getUser();
  const posts = await getPosts();
  
  // Good - parallel (fast)
  const [user, posts] = await Promise.all([
    getUser(),
    getPosts()
  ]);
}

// Using in loops
async function processAll(items) {
  for (const item of items) {
    await processItem(item);
  }
}

// Map with async
const results = await Promise.all(
  items.map(item => processItem(item))
);

Classes & OOP

Object-oriented programming with classes

Class Syntax & Features
Modern OOP in JavaScript
// Basic class
class User {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
  
  // Getter
  get isAdult() {
    return this.age >= 18;
  }
  
  // Setter
  set age(value) {
    if (value >= 0) {
      this._age = value;
    }
  }
  
  // Static method
  static compare(user1, user2) {
    return user1.age - user2.age;
  }
  
  // Private field (with #)
  #secret = 'private';
  
  // Private method
  #privateMethod() {
    return this.#secret;
  }
}

// Create instance
const user = new User('John', 30);
user.greet(); // Hello, I'm John

// Inheritance
class Admin extends User {
  constructor(name, age, role) {
    super(name, age);
    this.role = role;
  }
  
  // Override method
  greet() {
    super.greet();
    console.log(`I'm an ${this.role}`);
  }
}

// Check instance
user instanceof User; // true

// Static method call
User.compare(user1, user2);
Best Practice
Use private fields (#) to truly hide data. Regular properties are accessible. Modern JavaScript makes OOP safe and intuitive.

Modules (Import/Export)

Code organization and reusability

Export Variations
Different ways to export code
// Default export
export default class Calculator {
  add(a, b) { return a + b; }
}

// or
const config = { API_URL: '...' };
export default config;

// Named exports
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

export class Utils {
  static capitalize(str) {
    return str[0].toUpperCase() + str.slice(1);
  }
}

// Export all from module
export * from './utils';

// Export with alias
export { add as sum, subtract as minus };

// Mixed default + named
export default Database;
export { User, Post };
Import Variations
Different ways to import code
// Default import
import Calculator from './calculator';
const calc = new Calculator();

// Named imports
import { add, subtract } from './math';

// Import everything
import * as math from './math';
math.add(1, 2);

// Mix default and named
import Database, { User, Post } from './db';

// Import with alias
import { add as sum } from './math';
sum(1, 2);

// Dynamic import (runtime)
const module = await import('./utils');
const result = module.doSomething();

// Conditional imports
if (isDevelopment) {
  const debug = await import('./debug');
}

// Side-effect imports (run code)
import './styles.css';
import './setup'; // Runs setup.js code
Module Best Practice
Use named exports for utilities and functions. Use default exports for classes or main functionality. Keep modules focused and single-responsibility.

DOM Manipulation

Work with the DOM efficiently

Selecting & Modifying Elements
Query and update the DOM
// Select elements
const element = document.getElementById('my-id');
const elements = document.querySelectorAll('.my-class');
const first = document.querySelector('.my-class');

// Iterate selections
elements.forEach(el => {
  el.textContent = 'New text';
  el.style.color = 'red';
});

// Modify attributes
element.setAttribute('data-id', '123');
element.getAttribute('data-id'); // '123'
element.removeAttribute('disabled');

// Class operations
element.classList.add('active');
element.classList.remove('disabled');
element.classList.toggle('hidden');
element.classList.contains('active'); // true

// Create elements
const div = document.createElement('div');
div.textContent = 'Hello';
div.className = 'container';

// Add to DOM
element.appendChild(div);
element.insertBefore(div, element.firstChild);
element.replaceChild(newEl, oldEl);

// Remove from DOM
element.remove();
element.parentElement?.removeChild(element);

// Get parent/siblings
const parent = element.parentElement;
const prev = element.previousElementSibling;
const next = element.nextElementSibling;
Event Handling
Handle user interactions
// Add event listener
button.addEventListener('click', (event) => {
  console.log('Clicked!');
});

// Multiple events
['click', 'touchend'].forEach(event => {
  button.addEventListener(event, handleClick);
});

// Event delegation (efficient)
document.addEventListener('click', (event) => {
  if (event.target.matches('.delete-btn')) {
    deleteItem(event.target.dataset.id);
  }
});

// Remove listener
button.removeEventListener('click', handler);

// Event object properties
button.addEventListener('click', (e) => {
  e.preventDefault(); // Stop default
  e.stopPropagation(); // Stop bubbling
  e.target; // Element clicked
  e.currentTarget; // Element with listener
  e.type; // 'click'
  e.key; // Key pressed (keyboard)
  e.clientX; // Mouse position
});

// Common events
// - click, dblclick, mousedown, mouseup
// - focus, blur, input, change
// - keydown, keyup, keypress
// - submit, reset
// - load, unload, beforeunload
// - scroll, resize
// - dragstart, drop, dragover
Performance
Use event delegation for dynamically added elements instead of attaching listeners to each element.

Modern Best Practices

Write professional, maintainable code

PracticeDescriptionExample
Use const by defaultPrevent accidental reassignmentconst value = 10;
Prefer const over letSignal immutabilityconst user = { name: 'John' };
Use async/awaitCleaner than .then() chainsconst data = await fetch(url).then(r => r.json());
Destructure parametersMake dependencies clearfunction({ name, age }) {}
Use optional chainingSafe property accessuser?.address?.city
Use nullish coalescingBetter defaultsvalue ?? 'default'
Arrow functionsConcise syntax, lexical thisconst sum = (a, b) => a + b;
Template literalsReadable string interpolation`Hello, ${name}!`
Avoid callbacksUse promises or async/awaitawait fetchData()
Error handlingAlways use try/catch with asynctry {} catch {} finally {}
Use Array methodsFunctional programmingarray.map().filter().reduce()
Pure functionsNo side effectsconst add = (a, b) => a + b;

Code Style:

// ✓ Consistent formatting
const calculateTotal = (items) => {
  return items.reduce((sum, item) => 
    sum + item.price * item.quantity, 0);
};

// ✓ Clear variable names
const getUserByEmail = (email) => {
  return users.find(u => u.email === email);
};

// ✓ Single responsibility
const validateEmail = (email) => 
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);

// ✗ Avoid inline complexity
const x = a.filter(i=>i>0).map(i=>i*2);

// ✓ Extract to named function
const positive = (num) => num > 0;
const double = (num) => num * 2;
const result = a.filter(positive).map(double);

Error Handling:

// ✓ Try-catch with async
async function fetchUser(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}`);
    }
    return await res.json();
  } catch (error) {
    console.error('Failed to fetch user:', error);
    throw error;
  }
}

// ✓ Error handling in promises
fetchUser(1)
  .then(user => processUser(user))
  .catch(error => handleError(error))
  .finally(() => cleanup());

// ✓ Graceful fallbacks
const data = await fetchData().catch(() => 
  getDefaultData());

Performance Tips

Optimize JavaScript execution

IssueProblemSolution
Large loopsSlow DOM updatesUse batch updates, requestAnimationFrame
Memory leaksEvent listeners not removedAlways removeEventListener
Callback hellUnreadable nested codeUse async/await or promises
Blocking operationsUI freezesUse Web Workers for heavy computation
Bundle sizeLarge JavaScript filesTree-shaking, code splitting, minification
Inefficient queriesMultiple DOM traversalsCache selectors, use event delegation
Synchronous I/OBlocks executionUse async/await, promises
Memory allocationToo many object creationsReuse objects, object pooling
Regex in loopsRecompile every iterationMove regex outside loop
Array searchesLinear search is slowUse Set or Map for lookups
Tools
Use Chrome DevTools Performance tab, Lighthouse, and WebPageTest to identify bottlenecks. Profile before optimizing.

Common Pitfalls & Solutions

Avoid these JavaScript gotchas

PitfallProblemSolution
Type coercion'5' + 3 === '53'Use strict equality (===), CONV/typeof checks
this bindingArrow functions lose contextUse regular function for methods
Callback hellDeep nesting in callbacksUse async/await or promises
var hoistingUnexpected global scopeUse const/let instead
Array mutation.sort() modifies originalUse [...arr].sort()
Shallow copySpread doesn't deep copyUse structuredClone() for deep copy
NaN comparisonNaN === NaN is falseUse Number.isNaN() instead
Promise then returnForgot to return in .then()Always return or await
Async loopAwaiting in forEachUse for...of loop instead
Race conditionsMultiple async updatesUse locks or Promise.all()

Modern ES2024 Features

Latest JavaScript additions

Recent Additions
ES2023-ES2024 features
// Array grouping (ES2024)
const items = [
  { category: 'A', value: 10 },
  { category: 'B', value: 20 },
  { category: 'A', value: 30 }
];

const grouped = Object.groupBy(items, item => 
  item.category);
// { A: [...], B: [...] }

// findLast() and findLastIndex()
const lastEven = numbers.findLast(n => 
  n % 2 === 0);
const lastIdx = numbers.findLastIndex(n => 
  n % 2 === 0);

// Array.from with mapping
const range = Array.from({ length: 5 }, 
  (_, i) => i + 1);
// [1, 2, 3, 4, 5]

// at() for array access
const last = arr.at(-1); // Last element
const first = arr.at(0); // First element

// Promise.withResolvers() (ES2024)
const { promise, resolve, reject } = 
  Promise.withResolvers();
// Cleaner promise creation
Useful Modern Patterns
Recently adopted best practices
// Using ?? and ?. together
const city = user?.address?.city ?? 'Unknown';

// Logical assignment operators
let timeout = 0;
timeout ||= 5000; // Assign if falsy

let data = null;
data ??= getDefaultData(); // Assign if nullish

let config = {};
config &&= mergeConfig(config); // Assign if truthy

// replaceAll() for strings
const safe = userInput.replaceAll('<', '&lt;');

// structuredClone() for deep copy
const deepCopy = structuredClone(obj);

// Using Promise.allSettled()
const results = await Promise.allSettled([
  fetch1(), fetch2(), fetch3()
]);
results.forEach(result => {
  if (result.status === 'fulfilled') {
    console.log(result.value);
  } else {
    console.error(result.reason);
  }
});

// WeakMap for private data
const privateData = new WeakMap();
privateData.set(obj, 'secret');

Migration Checklist

Converting legacy code to modern JavaScript

Replace These Patterns:

✗ var name = 'John';
✓ const name = 'John';

✗ function(x) { return x * 2; }
✓ (x) => x * 2;

✗ 'Hello ' + name
✓ `Hello ${name}`;

✗ READ TABLE ... then use work area
✓ const item = items[ key ];

✗ .then().then().then()
✓ async/await

✗ if (!user) user = {}; 
✓ const user = userInput ?? {};

✗ setTimeout(cb, 1000)
✓ await new Promise(r => 
    setTimeout(r, 1000));

✗ Regular functions as methods
✓ Arrow functions for callbacks

Key Improvements:

  • 30-40% less code with modern syntax
  • Better readability and clarity
  • Improved type safety with strict equality
  • Safer property access with optional chaining
  • Cleaner async code with async/await
  • Functional programming patterns
  • Built-in error handling
  • Modern tooling support (linters, formatters)
Browser Support
Always check caniuse.com for feature support. Use transpilers (Babel) for older browser compatibility.

Quick Debugging Tips

Troubleshoot JavaScript issues quickly

Debugging Techniques
Essential debugging tools and methods
// Console methods
console.log('Message'); // Standard log
console.error('Error:'); // Error message
console.warn('Warning:'); // Warning message
console.table(data); // Tabular format
console.group('Section'); // Group logs
console.groupEnd();

// Conditional logging
console.assert(value > 0, 'Value must be positive');

// Performance timing
console.time('myTimer');
expensiveOperation();
console.timeEnd('myTimer');

// Debugger statement
debugger; // Pauses execution in DevTools

// Breakpoints in DevTools
// Click line number to set breakpoint
// Conditional breakpoint: right-click line number

// Inspect element
const element = document.querySelector('#my-id');
console.log(element);
console.dir(element); // All properties

// Stack trace
console.trace('Here');

// Debug async
async function debug() {
  const result = await fetchData();
  console.log('Result:', result);
}

// Logging with context
const logger = {
  info: (msg) => console.log(`[INFO] ${msg}`),
  error: (msg) => console.error(`[ERROR] ${msg}`),
  warn: (msg) => console.warn(`[WARN] ${msg}`)
};
Pro Debugging
Use the Sources tab in Chrome DevTools to set breakpoints, watch variables, and step through code. Use console to evaluate expressions while paused.
Additional Resources
For more JavaScript learning resources, visit our JavaScript Documentation and practice with our JavaScript Exercises.