git:20260307.47a5dbc to git:20260326.db33dbc

24 added, 448 removed. Audit A to A.

---
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:**
```javascript
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
// Single parameter (parentheses optional)
const double = (x) => x * 2;
// No parameters
const getRandom = () => Math.random();
// Multiple statements (need curly braces)
const processUser = (user) => {
const normalized = user.name.toLowerCase();
return { ...user, name: normalized };
};
// Returning objects (wrap in parentheses)
const createUser = (name, age) => ({ name, age });
```
**Lexical 'this' Binding:**
```javascript
class Counter {
constructor() {
this.count = 0;
}
// Arrow function preserves 'this' context
increment = () => {
this.count++;
};
// Traditional function loses 'this' in callbacks
incrementTraditional() {
setTimeout(function () {
this.count++; // 'this' is undefined
}, 1000);
}
// Arrow function maintains 'this'
incrementArrow() {
setTimeout(() => {
this.count++; // 'this' refers to Counter instance
}, 1000);
}
}
```
### 2. Destructuring
**Object Destructuring:**
```javascript
const user = {
id: 1,
name: "John Doe",
email: "john@example.com",
address: {
city: "New York",
country: "USA",
},
};
// Basic destructuring
const { name, email } = user;
// Rename variables
const { name: userName, email: userEmail } = user;
// Default values
const { age = 25 } = user;
// Nested destructuring
const {
address: { city, country },
} = user;
// Rest operator
const { id, ...userWithoutId } = user;
// Function parameters
function greet({ name, age = 18 }) {
console.log(`Hello ${name}, you are ${age}`);
}
greet(user);
```
**Array Destructuring:**
```javascript
const numbers = [1, 2, 3, 4, 5];
// Basic destructuring
const [first, second] = numbers;
// Skip elements
const [, , third] = numbers;
// Rest operator
const [head, ...tail] = numbers;
// Swapping variables
let a = 1,
b = 2;
[a, b] = [b, a];
// Function return values
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();
// Default values
const [one, two, three = 0] = [1, 2];
```
### 3. Spread and Rest Operators
**Spread Operator:**
```javascript
// Array spreading
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// Object spreading
const defaults = { theme: "dark", lang: "en" };
const userPrefs = { theme: "light" };
const settings = { ...defaults, ...userPrefs };
// Function arguments
const numbers = [1, 2, 3];
Math.max(...numbers);
// Copying arrays/objects (shallow copy)
const copy = [...arr1];
const objCopy = { ...user };
// Adding items immutably
const newArr = [...arr1, 4, 5];
const newObj = { ...user, age: 30 };
```
**Rest Parameters:**
```javascript
// Collect function arguments
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4, 5);
// With regular parameters
function greet(greeting, ...names) {
return `${greeting} ${names.join(", ")}`;
}
greet("Hello", "John", "Jane", "Bob");
// Object rest
const { id, ...userData } = user;
// Array rest
const [first, ...rest] = [1, 2, 3, 4, 5];
```
### 4. Template Literals
```javascript
// Basic usage
const name = "John";
const greeting = `Hello, ${name}!`;
// Multi-line strings
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`;
// Expression evaluation
const price = 19.99;
const total = `Total: $${(price * 1.2).toFixed(2)}`;
// Tagged template literals
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}`;
// Output: "Name: <mark>John</mark>, Age: <mark>30</mark>"
```
### 5. Enhanced Object Literals
```javascript
const name = "John";
const age = 30;
// Shorthand property names
const user = { name, age };
// Shorthand method names
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
},
};
// Computed property names
const field = "email";
const user = {
name: "John",
[field]: "john@example.com",
[`get${field.charAt(0).toUpperCase()}${field.slice(1)}`]() {
return this[field];
},
};
// Dynamic property creation
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:**
```javascript
// Creating a promise
const fetchUser = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "John" });
} else {
reject(new Error("Invalid ID"));
}
}, 1000);
});
};
// Using promises
fetchUser(1)
.then((user) => console.log(user))
.catch((error) => console.error(error))
.finally(() => console.log("Done"));
// Chaining promises
fetchUser(1)
.then((user) => fetchUserPosts(user.id))
.then((posts) => processPosts(posts))
.then((result) => console.log(result))
.catch((error) => console.error(error));
```
**Promise Combinators:**
```javascript
// Promise.all - Wait for all promises
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 - Wait for all, regardless of outcome
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 - First to complete
Promise.race(promises)
.then((winner) => console.log("First:", winner))
.catch((error) => console.error(error));
// Promise.any - First to succeed
Promise.any(promises)
.then((first) => console.log("First success:", first))
.catch((error) => console.error("All failed:", error));
```
### 2. Async/Await
**Basic Usage:**
```javascript
// Async function always returns a Promise
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// Error handling with try/catch
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;
}
}
// Sequential vs Parallel execution
async function sequential() {
const user1 = await fetchUser(1); // Wait
const user2 = await fetchUser(2); // Then wait
return [user1, user2];
}
async function parallel() {
const [user1, user2] = await Promise.all([fetchUser(1), fetchUser(2)]);
return [user1, user2];
}
```
**Advanced Patterns:**
```javascript
// Async IIFE
(async () => {
const result = await someAsyncOperation();
console.log(result);
})();
// Async iteration
async function processUsers(userIds) {
for (const id of userIds) {
const user = await fetchUser(id);
await processUser(user);
}
}
// Top-level await (ES2022)
const config = await fetch("/config.json").then((r) => r.json());
// Retry logic
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)));
}
}
}
// Timeout wrapper
async function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms),
);
return Promise.race([promise, timeout]);
}
```
## Functional Programming Patterns
- ### 1. Array Methods
-
- **Map, Filter, Reduce:**
-
- ```javascript
- const users = [
- { id: 1, name: "John", age: 30, active: true },
- { id: 2, name: "Jane", age: 25, active: false },
- { id: 3, name: "Bob", age: 35, active: true },
- ];
-
- // Map - Transform array
- const names = users.map((user) => user.name);
- const upperNames = users.map((user) => user.name.toUpperCase());
-
- // Filter - Select elements
- const activeUsers = users.filter((user) => user.active);
- const adults = users.filter((user) => user.age >= 18);
-
- // Reduce - Aggregate data
- const totalAge = users.reduce((sum, user) => sum + user.age, 0);
- const avgAge = totalAge / users.length;
-
- // Group by property
- const byActive = users.reduce((groups, user) => {
- const key = user.active ? "active" : "inactive";
- return {
- ...groups,
- [key]: [...(groups[key] || []), user],
- };
- }, {});
-
- // Chaining methods
- const result = users
- .filter((user) => user.active)
- .map((user) => user.name)
- .sort()
- .join(", ");
- ```
-
- **Advanced Array Methods:**
-
- ```javascript
- // Find - First matching element
- const user = users.find((u) => u.id === 2);
-
- // FindIndex - Index of first match
- const index = users.findIndex((u) => u.name === "Jane");
-
- // Some - At least one matches
- const hasActive = users.some((u) => u.active);
-
- // Every - All match
- const allAdults = users.every((u) => u.age >= 18);
-
- // FlatMap - Map and flatten
- const userTags = [
- { name: "John", tags: ["admin", "user"] },
- { name: "Jane", tags: ["user"] },
- ];
- const allTags = userTags.flatMap((u) => u.tags);
-
- // From - Create array from iterable
- const str = "hello";
- const chars = Array.from(str);
- const numbers = Array.from({ length: 5 }, (_, i) => i + 1);
-
- // Of - Create array from arguments
- const arr = Array.of(1, 2, 3);
- ```
-
- ### 2. Higher-Order Functions
-
- **Functions as Arguments:**
-
- ```javascript
- // Custom forEach
- function forEach(array, callback) {
- for (let i = 0; i < array.length; i++) {
- callback(array[i], i, array);
- }
- }
-
- // Custom map
- function map(array, transform) {
- const result = [];
- for (const item of array) {
- result.push(transform(item));
- }
- return result;
- }
-
- // Custom filter
- function filter(array, predicate) {
- const result = [];
- for (const item of array) {
- if (predicate(item)) {
- result.push(item);
- }
- }
- return result;
- }
- ```
-
- **Functions Returning Functions:**
-
- ```javascript
- // Currying
- const multiply = (a) => (b) => a * b;
- const double = multiply(2);
- const triple = multiply(3);
-
- console.log(double(5)); // 10
- console.log(triple(5)); // 15
-
- // Partial application
- function partial(fn, ...args) {
- return (...moreArgs) => fn(...args, ...moreArgs);
- }
-
- const add = (a, b, c) => a + b + c;
- const add5 = partial(add, 5);
- console.log(add5(3, 2)); // 10
-
- // Memoization
- function memoize(fn) {
- const cache = new Map();
- return (...args) => {
- const key = JSON.stringify(args);
- if (cache.has(key)) {
- return cache.get(key);
- }
- const result = fn(...args);
- cache.set(key, result);
- return result;
- };
- }
-
- const fibonacci = memoize((n) => {
- if (n <= 1) return n;
- return fibonacci(n - 1) + fibonacci(n - 2);
- });
- ```
-
- ### 3. Composition and Piping
-
- ```javascript
- // Function composition
- const compose =
- (...fns) =>
- (x) =>
- fns.reduceRight((acc, fn) => fn(acc), x);
-
- const pipe =
- (...fns) =>
- (x) =>
- fns.reduce((acc, fn) => fn(acc), x);
-
- // Example usage
- const addOne = (x) => x + 1;
- const double = (x) => x * 2;
- const square = (x) => x * x;
-
- const composed = compose(square, double, addOne);
- console.log(composed(3)); // ((3 + 1) * 2)^2 = 64
-
- const piped = pipe(addOne, double, square);
- console.log(piped(3)); // ((3 + 1) * 2)^2 = 64
-
- // Practical example
- const processUser = pipe(
- (user) => ({ ...user, name: user.name.trim() }),
- (user) => ({ ...user, email: user.email.toLowerCase() }),
- (user) => ({ ...user, age: parseInt(user.age) }),
- );
-
- const user = processUser({
- name: " John ",
- email: "JOHN@EXAMPLE.COM",
- age: "30",
- });
- ```
-
- ### 4. Pure Functions and Immutability
-
- ```javascript
- // Impure function (modifies input)
- function addItemImpure(cart, item) {
- cart.items.push(item);
- cart.total += item.price;
- return cart;
- }
-
- // Pure function (no side effects)
- function addItemPure(cart, item) {
- return {
- ...cart,
- items: [...cart.items, item],
- total: cart.total + item.price,
- };
- }
-
- // Immutable array operations
- const numbers = [1, 2, 3, 4, 5];
-
- // Add to array
- const withSix = [...numbers, 6];
-
- // Remove from array
- const withoutThree = numbers.filter((n) => n !== 3);
-
- // Update array element
- const doubled = numbers.map((n) => (n === 3 ? n * 2 : n));
-
- // Immutable object operations
- const user = { name: "John", age: 30 };
-
- // Update property
- const olderUser = { ...user, age: 31 };
-
- // Add property
- const withEmail = { ...user, email: "john@example.com" };
-
- // Remove property
- const { age, ...withoutAge } = user;
-
- // Deep cloning (simple approach)
- const deepClone = (obj) => JSON.parse(JSON.stringify(obj));
+ Functional programming in JavaScript centers on pure functions, immutability, and composable transformations.
- // Better deep cloning
- const structuredClone = (obj) => globalThis.structuredClone(obj);
- ```
+ Key topics covered in [references/advanced-patterns.md](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
- ```javascript
- // Class syntax
- class User {
- // Private fields
- #password;
-
- // Public fields
- id;
- name;
-
- // Static field
- static count = 0;
-
- constructor(id, name, password) {
- this.id = id;
- this.name = name;
- this.#password = password;
- User.count++;
- }
-
- // Public method
- greet() {
- return `Hello, ${this.name}`;
- }
-
- // Private method
- #hashPassword(password) {
- return `hashed_${password}`;
- }
-
- // Getter
- get displayName() {
- return this.name.toUpperCase();
- }
-
- // Setter
- set password(newPassword) {
- this.#password = this.#hashPassword(newPassword);
- }
-
- // Static method
- static create(id, name, password) {
- return new User(id, name, password);
- }
- }
-
- // Inheritance
- class Admin extends User {
- constructor(id, name, password, role) {
- super(id, name, password);
- this.role = role;
- }
-
- greet() {
- return `${super.greet()}, I'm an admin`;
- }
- }
- ```
+ ES2022 classes support private fields (`#field`), static fields, getters/setters, and private methods. See [references/advanced-patterns.md](references/advanced-patterns.md) for a full example with inheritance.
## Modules (ES6)
```javascript
- // Exporting
- // math.js
+ // Named exports
export const PI = 3.14159;
- export function add(a, b) {
- return a + b;
- }
- export class Calculator {
- // ...
- }
+ export function add(a, b) { return a + b; }
// Default export
- export default function multiply(a, b) {
- return a * b;
- }
-
- // Importing
- // app.js
- import multiply, { PI, add, Calculator } from "./math.js";
-
- // Rename imports
- import { add as sum } from "./math.js";
+ export default function multiply(a, b) { return a * b; }
- // Import all
- import * as Math from "./math.js";
+ // Import
+ import multiply, { PI, add } from "./math.js";
- // Dynamic imports
- const module = await import("./math.js");
+ // Dynamic import (code splitting)
const { add } = await import("./math.js");
-
- // Conditional loading
- if (condition) {
- const module = await import("./feature.js");
- module.init();
- }
```
- ## Iterators and Generators
-
- ```javascript
- // Custom iterator
- const range = {
- from: 1,
- to: 5,
-
- [Symbol.iterator]() {
- return {
- current: this.from,
- last: this.to,
-
- next() {
- if (this.current <= this.last) {
- return { done: false, value: this.current++ };
- } else {
- return { done: true };
- }
- },
- };
- },
- };
-
- for (const num of range) {
- console.log(num); // 1, 2, 3, 4, 5
- }
-
- // Generator function
- function* rangeGenerator(from, to) {
- for (let i = from; i <= to; i++) {
- yield i;
- }
- }
-
- for (const num of rangeGenerator(1, 5)) {
- console.log(num);
- }
-
- // Infinite generator
- function* fibonacci() {
- let [prev, curr] = [0, 1];
- while (true) {
- yield curr;
- [prev, curr] = [curr, prev + curr];
- }
- }
+ For re-exports, namespace imports, and conditional dynamic loading see [references/advanced-patterns.md](references/advanced-patterns.md).
- // Async generator
- async function* fetchPages(url) {
- let page = 1;
- while (true) {
- const response = await fetch(`${url}?page=${page}`);
- const data = await response.json();
- if (data.length === 0) break;
- yield data;
- page++;
- }
- }
+ ## Iterators and Generators
- for await (const page of fetchPages("/api/users")) {
- console.log(page);
- }
- ```
+ Generators (`function*`) and async generators (`async function*`) enable lazy sequences and async pagination. See [references/advanced-patterns.md](references/advanced-patterns.md) for custom iterator, range generator, fibonacci, and `for await...of` examples.
## Modern Operators
```javascript
- // Optional chaining
- const user = { name: "John", address: { city: "NYC" } };
+ // Optional chaining — safe property access
const city = user?.address?.city;
- const zipCode = user?.address?.zipCode; // undefined
-
- // Function call
const result = obj.method?.();
- // Array access
- const first = arr?.[0];
-
- // Nullish coalescing
+ // Nullish coalescing — default only for null/undefined (not 0 or "")
const value = null ?? "default"; // 'default'
- const value = undefined ?? "default"; // 'default'
- const value = 0 ?? "default"; // 0 (not 'default')
- const value = "" ?? "default"; // '' (not 'default')
+ const zero = 0 ?? "default"; // 0
// Logical assignment
- let a = null;
- a ??= "default"; // a = 'default'
-
- let b = 5;
- b ??= 10; // b = 5 (unchanged)
-
- let obj = { count: 0 };
- obj.count ||= 1; // obj.count = 1
- obj.count &&= 2; // obj.count = 2
+ a ??= "default"; // assign if null/undefined
+ obj.count ||= 1; // assign if falsy
+ obj.count &&= 2; // assign if truthy
```
## Performance Optimization
- ```javascript
- // Debounce
- function debounce(fn, delay) {
- let timeoutId;
- return (...args) => {
- clearTimeout(timeoutId);
- timeoutId = setTimeout(() => fn(...args), delay);
- };
- }
-
- const searchDebounced = debounce(search, 300);
-
- // Throttle
- function throttle(fn, limit) {
- let inThrottle;
- return (...args) => {
- if (!inThrottle) {
- fn(...args);
- inThrottle = true;
- setTimeout(() => (inThrottle = false), limit);
- }
- };
- }
-
- const scrollThrottled = throttle(handleScroll, 100);
-
- // Lazy evaluation
- function* lazyMap(iterable, transform) {
- for (const item of iterable) {
- yield transform(item);
- }
- }
-
- // Use only what you need
- const numbers = [1, 2, 3, 4, 5];
- const doubled = lazyMap(numbers, (x) => x * 2);
- const first = doubled.next().value; // Only computes first value
- ```
+ See [references/advanced-patterns.md](references/advanced-patterns.md) for debounce, throttle, and lazy evaluation with generators.
## Best Practices
1. **Use const by default**: Only use let when reassignment is needed
2. **Prefer arrow functions**: Especially for callbacks
3. **Use template literals**: Instead of string concatenation
4. **Destructure objects and arrays**: For cleaner code
5. **Use async/await**: Instead of Promise chains
6. **Avoid mutating data**: Use spread operator and array methods
7. **Use optional chaining**: Prevent "Cannot read property of undefined"
8. **Use nullish coalescing**: For default values
9. **Prefer array methods**: Over traditional loops
10. **Use modules**: For better code organization
11. **Write pure functions**: Easier to test and reason about
12. **Use meaningful variable names**: Self-documenting code
13. **Keep functions small**: Single responsibility principle
14. **Handle errors properly**: Use try/catch with async/await
15. **Use strict mode**: `'use strict'` for better error catching
- ## Common Pitfalls
-
- 1. **this binding confusion**: Use arrow functions or bind()
- 2. **Async/await without error handling**: Always use try/catch
- 3. **Promise creation unnecessary**: Don't wrap already async functions
- 4. **Mutation of objects**: Use spread operator or Object.assign()
- 5. **Forgetting await**: Async functions return promises
- 6. **Blocking event loop**: Avoid synchronous operations
- 7. **Memory leaks**: Clean up event listeners and timers
- 8. **Not handling promise rejections**: Use catch() or try/catch
+ For common pitfalls (this binding, promise anti-patterns, memory leaks), see [references/advanced-patterns.md](references/advanced-patterns.md).