modern-javascript-patterns
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.
- License 许可
- MIT
- 最近更新
- 2026-11-01
- Stars
- ★ 3
- Forks
- 1
01 前言 Front Matter
- 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.
03 SKILL.md 正文
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:
// 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:
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:
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:
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:
// 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:
// 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
// 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
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:
// 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:
// 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:
// 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:
// 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:
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:
// 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:
// 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:
// 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
// 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
// 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));
// Better deep cloning
const structuredClone = obj => globalThis.structuredClone(obj);
Modern Class Features
// 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`;
}
}
Modules (ES6)
// Exporting
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export class Calculator {
// ...
}
// 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';
// Import all
import * as Math from './math.js';
// Dynamic imports
const module = await import('./math.js');
const { add } = await import('./math.js');
// Conditional loading
if (condition) {
const module = await import('./feature.js');
module.init();
}
Iterators and Generators
// 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];
}
}
// 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++;
}
}
for await (const page of fetchPages('/api/users')) {
console.log(page);
}
Modern Operators
// Optional chaining
const user = { name: 'John', address: { city: 'NYC' } };
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
const value = null ?? 'default'; // 'default'
const value = undefined ?? 'default'; // 'default'
const value = 0 ?? 'default'; // 0 (not 'default')
const value = '' ?? 'default'; // '' (not 'default')
// 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
Performance Optimization
// 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
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
Common Pitfalls
- this binding confusion: Use arrow functions or bind()
- Async/await without error handling: Always use try/catch
- Promise creation unnecessary: Don't wrap already async functions
- Mutation of objects: Use spread operator or Object.assign()
- Forgetting await: Async functions return promises
- Blocking event loop: Avoid synchronous operations
- Memory leaks: Clean up event listeners and timers
- Not handling promise rejections: Use catch() or try/catch
Resources
- MDN Web Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript
- JavaScript.info: https://javascript.info/
- You Don't Know JS: https://github.com/getify/You-Dont-Know-JS
- Eloquent JavaScript: https://eloquentjavascript.net/
- ES6 Features: http://es6-features.org/
04 同仓库更多 Skills
xiaolai-methodology
以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 "李笑来"、"笑来"、"笑来老师"、"扮演李笑来"、"找笑来聊聊"、"笑来怎么看"、"xiaolai"、"把时间当作朋友"、"通往财富自由之路"、"定投改变命运"、"韭菜的自我修养"、"自学是门手艺"、"让时间陪你慢慢变富"、"七年就是一辈子"、"元认知"、"活在未来"、"复利/长期主义"、"践行"、"痴迷改进"、"个人商业模式/出售时间"、"睡后收入"、"定投"、"财富自由"、"操作系统升级"、"最少必要知识"、"北极星提示词"、"用 AI 该怎么学/思考" 等任一触发词,或当用户带着关于创业/赚钱/投资/学习/职业/重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。
zread
Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `./.zread/wiki/`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module/package explanations, or "what does this repo do"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread / zread.ai directly. Also use it proactively before diving into a large unknown repo — if `./.zread/wiki/current` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word "zread".
youtube
Launch the YouTube Creator Agent to auto-generate videos. Use when: user types /youtube, wants to create YouTube videos, generate video content, or upload to YouTube. Keywords: youtube, video, 视频, 创作视频, 发布视频
youtube-script-sync
Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新/同步/优化 Youtube视频逐字稿知识库”, or says “更新/同步/优化该知识库” while this knowledge base is the active context.
writing-skills
How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.
writing-rules
How to write .claude/rules/ files that Claude actually follows. Use when creating, improving, or reviewing project rules.