| name | modern-javascript-patterns |
| description | Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications. |
Modern JavaScript Patterns
Comprehensive guide for mastering modern JavaScript (ES6+) features, functional programming patterns, and best practices for writing clean, maintainable, and performant code.
When to Use This Skill
- Refactoring legacy JavaScript to modern syntax
- Implementing functional programming patterns
- Optimizing JavaScript performance
- Writing maintainable and readable code
- Working with asynchronous operations
- Building modern web applications
- Migrating from callbacks to Promises/async-await
- Implementing data transformation pipelines
ES6+ Core Features
1. Arrow Functions
Syntax and Use Cases:
function add(a, b) {
return a + b
}
const add = (a, b) => a + b
const double = (x) => x * 2
const getRandom = () => Math.random()
const processUser = (user) => {
const normalized = user.name.toLowerCase()
return { ...user, name: normalized }
}
const createUser = (name, age) => ({ name, age })
Lexical 'this' Binding:
class Counter {
constructor() {
this.count = 0
}
increment = () => {
this.count++
}
incrementTraditional() {
setTimeout(function () {
this.count++
}, 1000)
}
incrementArrow() {
setTimeout(() => {
this.count++
}, 1000)
}
}
2. Destructuring
Object Destructuring:
const user = {
id: 1,
name: "John Doe",
email: "john@example.com",
address: {
city: "New York",
country: "USA",
},
}
const { name, email } = user
const { name: userName, email: userEmail } = user
const { age = 25 } = user
const {
address: { city, country },
} = user
const { id, ...userWithoutId } = user
function greet({ name, age = 18 }) {
console.log(`Hello ${name}, you are ${age}`)
}
greet(user)
Array Destructuring:
const numbers = [1, 2, 3, 4, 5]
const [first, second] = numbers
const [, , third] = numbers
const [head, ...tail] = numbers
let a = 1,
b = 2
;[a, b] = [b, a]
function getCoordinates() {
return [10, 20]
}
const [x, y] = getCoordinates()
const [one, two, three = 0] = [1, 2]
3. Spread and Rest Operators
Spread Operator:
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const combined = [...arr1, ...arr2]
const defaults = { theme: "dark", lang: "en" }
const userPrefs = { theme: "light" }
const settings = { ...defaults, ...userPrefs }
const numbers = [1, 2, 3]
Math.max(...numbers)
const copy = [...arr1]
const objCopy = { ...user }
const newArr = [...arr1, 4, 5]
const newObj = { ...user, age: 30 }
Rest Parameters:
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0)
}
sum(1, 2, 3, 4, 5)
function greet(greeting, ...names) {
return `${greeting} ${names.join(", ")}`
}
greet("Hello", "John", "Jane", "Bob")
const { id, ...userData } = user
const [first, ...rest] = [1, 2, 3, 4, 5]
4. Template Literals
const name = "John"
const greeting = `Hello, ${name}!`
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`
const price = 19.99
const total = `Total: $${(price * 1.2).toFixed(2)}`
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] || ""
return result + str + `<mark>${value}</mark>`
}, "")
}
const name = "John"
const age = 30
const html = highlight`Name: ${name}, Age: ${age}`
5. Enhanced Object Literals
const name = "John"
const age = 30
const user = { name, age }
const calculator = {
add(a, b) {
return a + b
},
subtract(a, b) {
return a - b
},
}
const field = "email"
const user = {
name: "John",
[field]: "john@example.com",
[`get${field.charAt(0).toUpperCase()}${field.slice(1)}`]() {
return this[field]
},
}
const createUser = (name, ...props) => {
return props.reduce(
(user, [key, value]) => ({
...user,
[key]: value,
}),
{ name }
)
}
const user = createUser("John", ["age", 30], ["email", "john@example.com"])
Asynchronous Patterns
1. Promises
Creating and Using Promises:
const fetchUser = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "John" })
} else {
reject(new Error("Invalid ID"))
}
}, 1000)
})
}
fetchUser(1)
.then((user) => console.log(user))
.catch((error) => console.error(error))
.finally(() => console.log("Done"))
fetchUser(1)
.then((user) => fetchUserPosts(user.id))
.then((posts) => processPosts(posts))
.then((result) => console.log(result))
.catch((error) => console.error(error))
Promise Combinators:
const promises = [fetchUser(1), fetchUser(2), fetchUser(3)]
Promise.all(promises)
.then((users) => console.log(users))
.catch((error) => console.error("At least one failed:", error))
Promise.allSettled(promises).then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value)
} else {
console.log("Error:", result.reason)
}
})
})
Promise.race(promises)
.then((winner) => console.log("First:", winner))
.catch((error) => console.error(error))
Promise.any(promises)
.then((first) => console.log("First success:", first))
.catch((error) => console.error("All failed:", error))
2. Async/Await
Basic Usage:
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
const user = await response.json()
return user
}
async function getUserData(id) {
try {
const user = await fetchUser(id)
const posts = await fetchUserPosts(user.id)
return { user, posts }
} catch (error) {
console.error("Error fetching data:", error)
throw error
}
}
async function sequential() {
const user1 = await fetchUser(1)
const user2 = await fetchUser(2)
return [user1, user2]
}
async function parallel() {
const [user1, user2] = await Promise.all([fetchUser(1), fetchUser(2)])
return [user1, user2]
}
Advanced Patterns:
;(async () => {
const result = await someAsyncOperation()
console.log(result)
})()
async function processUsers(userIds) {
for (const id of userIds) {
const user = await fetchUser(id)
await processUser(user)
}
}
const config = await fetch("/config.json").then((r) => r.json())
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url)
} catch (error) {
if (i === retries - 1) throw error
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)))
}
}
}
async function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms)
)
return Promise.race([promise, timeout])
}
Functional Programming Patterns
Functional programming in JavaScript centers on pure functions, immutability, and composable transformations.
Key topics covered in references/advanced-patterns.md:
- Array methods —
map, filter, reduce, find, findIndex, some, every, flatMap, Array.from
- Higher-order functions — custom
forEach/map/filter, currying, partial application, memoization
- Composition and piping —
compose/pipe utilities with practical data transformation examples
- Pure functions and immutability — immutable array/object operations, deep cloning with
structuredClone
Modern Class Features
ES2022 classes support private fields (#field), static fields, getters/setters, and private methods. See references/advanced-patterns.md for a full example with inheritance.
Modules (ES6)
export const PI = 3.14159
export function add(a, b) {
return a + b
}
export default function multiply(a, b) {
return a * b
}
import multiply, { PI, add } from "./math.js"
const { add } = await import("./math.js")
For re-exports, namespace imports, and conditional dynamic loading see references/advanced-patterns.md.
Iterators and Generators
Generators (function*) and async generators (async function*) enable lazy sequences and async pagination. See references/advanced-patterns.md for custom iterator, range generator, fibonacci, and for await...of examples.
Modern Operators
const city = user?.address?.city
const result = obj.method?.()
const value = null ?? "default"
const zero = 0 ?? "default"
a ??= "default"
obj.count ||= 1
obj.count &&= 2
Performance Optimization
See references/advanced-patterns.md for debounce, throttle, and lazy evaluation with generators.
Best Practices
- Use const by default: Only use let when reassignment is needed
- Prefer arrow functions: Especially for callbacks
- Use template literals: Instead of string concatenation
- Destructure objects and arrays: For cleaner code
- Use async/await: Instead of Promise chains
- Avoid mutating data: Use spread operator and array methods
- Use optional chaining: Prevent "Cannot read property of undefined"
- Use nullish coalescing: For default values
- Prefer array methods: Over traditional loops
- Use modules: For better code organization
- Write pure functions: Easier to test and reason about
- Use meaningful variable names: Self-documenting code
- Keep functions small: Single responsibility principle
- Handle errors properly: Use try/catch with async/await
- Use strict mode:
'use strict' for better error catching
For common pitfalls (this binding, promise anti-patterns, memory leaks), see references/advanced-patterns.md.