911 lines
19 KiB
Markdown
911 lines
19 KiB
Markdown
# Modern JavaScript Patterns Implementation Playbook
|
|
|
|
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
|
|
|
# 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));
|
|
|
|
// Better deep cloning
|
|
const structuredClone = obj => globalThis.structuredClone(obj);
|
|
```
|
|
|
|
## 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`;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Modules (ES6)
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```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];
|
|
}
|
|
}
|
|
|
|
// 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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```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
|
|
```
|
|
|
|
## 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
|
|
|
|
## 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/
|