ES6+ features and modern JavaScript patterns for cleaner, more maintainable code.
Prefer destructuring over manual variable assignment for cleaner, more readable code.
// ✅ Object destructuring
const user = {
name: 'Alice',
email: '[email protected]',
age: 28,
preferences: {
theme: 'dark',
language: 'en'
}
};
// Extract multiple properties at once
const { name, email, age } = user;
// With default values and renaming
const { name: userName, role = 'user', isActive = true } = user;
// Nested destructuring
const { preferences: { theme, language } } = user;
// ✅ Array destructuring
const coordinates = [40.7128, -74.0060];
const [latitude, longitude] = coordinates;
// Skip elements with commas
const colors = ['red', 'green', 'blue', 'yellow'];
const [primary, , tertiary] = colors; // primary = 'red', tertiary = 'blue'
// ✅ Function parameter destructuring
function createUser({ name, email, age = 18, role = 'user' }) {
return {
name,
email,
age,
role,
createdAt: new Date()
};
}
// ✅ Function return value destructuring
function getCoordinates() {
return { lat: 40.7128, lng: -74.0060, city: 'New York' };
}
const { lat, lng, city } = getCoordinates();
// ✅ Swapping variables
let a = 1, b = 2;
[a, b] = [b, a]; // Now a = 2, b = 1
// ❌ Manual assignment instead of destructuring
const userName = user.name;
const userEmail = user.email;
const userAge = user.age; // Repetitive and verbose
// ❌ Not using destructuring for function parameters
function createUser(userInfo) {
const name = userInfo.name;
const email = userInfo.email;
const age = userInfo.age || 18;
// More verbose than destructuring
} // ✅ Template literals for string interpolation
const userName = 'Alice';
const age = 28;
const greeting = `Hello, ${userName}! You are ${age} years old.`;
// ✅ Multi-line strings
const emailTemplate = `
Dear ${userName},
Thank you for joining our platform.
Your account has been created successfully.
Best regards,
The Team
`;
// ✅ Expression evaluation
const order = { total: 99.99, tax: 8.99 };
const receipt = `
Subtotal: $${(order.total - order.tax).toFixed(2)}
Tax: $${order.tax.toFixed(2)}
Total: $${order.total.toFixed(2)}
`;
// ✅ HTML template creation (safe for client-side rendering)
function createUserCard(user) {
return `
<div class="user-card">
<h3>${user.name}</h3>
<p>${user.email}</p>
<span class="badge ${user.isActive ? 'active' : 'inactive'}">
${user.isActive ? 'Active' : 'Inactive'}
</span>
</div>
`;
}
// ⚠️ Never use template literals for SQL queries — use parameterized queries instead
// ❌ String concatenation
const greeting = 'Hello, ' + userName + '! You are ' + age + ' years old.';
// ❌ Manual line breaks with concatenation
const message = 'Line 1\n' +
'Line 2\n' +
'Line 3'; Use modern array methods for functional programming patterns instead of traditional loops.
map() for transforming
arrays
filter() for filtering
elements
reduce() for accumulation
const users = [
{ id: 1, name: 'Alice', age: 28, isActive: true },
{ id: 2, name: 'Bob', age: 34, isActive: false },
{ id: 3, name: 'Charlie', age: 22, isActive: true }
];
// ✅ Transform data with map()
const userNames = users.map(user => user.name);
const userSummaries = users.map(user => ({
id: user.id,
displayName: `${user.name} (Age: ${user.age})`,
status: user.isActive ? 'Active' : 'Inactive'
}));
// ✅ Filter data
const activeUsers = users.filter(user => user.isActive);
const youngUsers = users.filter(user => user.age < 30);
// ✅ Combine filtering and mapping
const activeUserNames = users
.filter(user => user.isActive)
.map(user => user.name);
// ✅ Reduce for calculations
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
const usersByStatus = users.reduce((acc, user) => {
const status = user.isActive ? 'active' : 'inactive';
acc[status] = acc[status] || [];
acc[status].push(user);
return acc;
}, {});
// ✅ Find specific items
const youngActiveUser = users.find(user => user.age < 30 && user.isActive);
const hasInactiveUsers = users.some(user => !user.isActive);
const allUsersActive = users.every(user => user.isActive);
// ❌ Using traditional for loops when array methods are clearer
const userNames = [];
for (let i = 0; i < users.length; i++) {
userNames.push(users[i].name); // map() is clearer
}
const activeUsers = [];
for (let i = 0; i < users.length; i++) {
if (users[i].isActive) {
activeUsers.push(users[i]); // filter() is clearer
}
} // ✅ Flattening arrays
const nestedArrays = [[1, 2], [3, 4], [5, 6]];
const flattened = nestedArrays.flat(); // [1, 2, 3, 4, 5, 6]
// Deep flattening
const deepNested = [1, [2, [3, [4]]]];
const deepFlattened = deepNested.flat(Infinity); // [1, 2, 3, 4]
// ✅ FlatMap for mapping and flattening
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap(sentence => sentence.split(' '));
// Result: ['Hello', 'world', 'How', 'are', 'you']
// ✅ Array.from() for creating arrays
const range = Array.from({ length: 5 }, (_, i) => i + 1); // [1, 2, 3, 4, 5]
const squares = Array.from({ length: 5 }, (_, i) => (i + 1) ** 2); // [1, 4, 9, 16, 25]
// ✅ Set operations with arrays
const array1 = [1, 2, 3, 4];
const array2 = [3, 4, 5, 6];
// Unique values
const unique = [...new Set([...array1, ...array2])]; // [1, 2, 3, 4, 5, 6]
// Intersection
const intersection = array1.filter(x => array2.includes(x)); // [3, 4]
// Difference
const difference = array1.filter(x => !array2.includes(x)); // [1, 2] Use modern asynchronous patterns for cleaner, more readable async code.
async/await over .then() chains
Promise.all()
for concurrent operations
Promise.allSettled() when some operations can fail
// ✅ Async/await for sequential operations
async function getUserProfile(userId) {
try {
const user = await fetchUser(userId);
const preferences = await fetchUserPreferences(userId);
const subscriptions = await fetchUserSubscriptions(userId);
return {
...user,
preferences,
subscriptions
};
} catch (error) {
console.error('Failed to fetch user profile:', error);
throw new Error(`Unable to load profile for user ${userId}`);
}
}
// ✅ Concurrent operations with Promise.all()
async function getUserDashboardData(userId) {
try {
const [user, orders, notifications, settings] = await Promise.all([
fetchUser(userId),
fetchUserOrders(userId),
fetchUserNotifications(userId),
fetchUserSettings(userId)
]);
return { user, orders, notifications, settings };
} catch (error) {
throw new Error('Failed to load dashboard data');
}
}
// ✅ Handle partial failures with Promise.allSettled()
async function syncUserData(userId) {
const operations = [
syncUserProfile(userId),
syncUserPreferences(userId),
syncUserHistory(userId)
];
const results = await Promise.allSettled(operations);
const failures = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
if (failures.length > 0) {
console.warn('Some sync operations failed:', failures);
}
return results.filter(result => result.status === 'fulfilled')
.map(result => result.value);
}
// ✅ Error handling with specific error types
async function processPayment(paymentData) {
try {
const validation = await validatePayment(paymentData);
const charge = await createCharge(validation);
const receipt = await generateReceipt(charge);
return { charge, receipt };
} catch (error) {
if (error.code === 'INSUFFICIENT_FUNDS') {
throw new Error('Payment declined: insufficient funds');
} else if (error.code === 'INVALID_CARD') {
throw new Error('Payment declined: invalid card details');
} else {
throw new Error('Payment processing failed');
}
}
}
// ❌ Promise chains instead of async/await
function getUserProfile(userId) {
return fetchUser(userId)
.then(user => {
return fetchUserPreferences(userId)
.then(preferences => {
return fetchUserSubscriptions(userId)
.then(subscriptions => {
return { ...user, preferences, subscriptions };
});
});
})
.catch(error => {
console.error('Error:', error);
throw new Error(`Unable to load profile for user ${userId}`);
});
} // ✅ Promise.race() for timeouts
async function fetchWithTimeout(url, timeoutMs = 5000) {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), timeoutMs);
});
try {
const response = await Promise.race([fetchPromise, timeoutPromise]);
return await response.json();
} catch (error) {
throw new Error(`Failed to fetch ${url}: ${error.message}`);
}
}
// ✅ Retry pattern with exponential backoff
async function retryWithBackoff(operation, maxRetries = 3, baseDelay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
const delay = baseDelay * Math.pow(2, attempt - 1);
console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// ✅ Promise-based queue for rate limiting
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = [];
this.queue = [];
}
async add(asyncFunction) {
return new Promise((resolve, reject) => {
this.queue.push({
asyncFunction,
resolve,
reject
});
this.tryNext();
});
}
async tryNext() {
if (this.running.length < this.concurrency && this.queue.length > 0) {
const { asyncFunction, resolve, reject } = this.queue.shift();
const promise = asyncFunction()
.then(resolve)
.catch(reject)
.finally(() => {
this.running = this.running.filter(p => p !== promise);
this.tryNext();
});
this.running.push(promise);
}
}
} // ✅ Object property shorthand
const name = 'Alice';
const age = 28;
const email = '[email protected]';
const user = { name, age, email }; // Same as { name: name, age: age, email: email }
// ✅ Object method shorthand
const userService = {
users: [],
addUser(user) { // Instead of addUser: function(user)
this.users.push(user);
},
async fetchUser(id) { // Works with async too
return await apiClient.get(`/users/${id}`);
}
};
// ✅ Spread operator for object composition
const defaultSettings = {
theme: 'light',
language: 'en',
notifications: true
};
const userSettings = {
...defaultSettings,
theme: 'dark', // Override specific properties
fontSize: 'large'
};
// ✅ Spread for object updates (immutable)
function updateUserProfile(user, updates) {
return {
...user,
...updates,
updatedAt: new Date()
};
}
// ✅ Computed property names
const dynamicKey = 'userRole';
const user = {
name: 'Alice',
[dynamicKey]: 'admin', // Dynamic property name
[`is${dynamicKey.charAt(0).toUpperCase()}${dynamicKey.slice(1)}`]: true
};
// ✅ Object.entries(), Object.keys(), Object.values()
const settings = { theme: 'dark', language: 'en', fontSize: 'large' };
// Convert to array of key-value pairs
const settingsEntries = Object.entries(settings);
// [['theme', 'dark'], ['language', 'en'], ['fontSize', 'large']]
// Get just the keys or values
const settingKeys = Object.keys(settings); // ['theme', 'language', 'fontSize']
const settingValues = Object.values(settings); // ['dark', 'en', 'large']
// Transform object
const uppercaseSettings = Object.fromEntries(
Object.entries(settings).map(([key, value]) => [key.toUpperCase(), value])
); // ✅ Optional chaining for safe property access
const user = {
name: 'Alice',
profile: {
settings: {
theme: 'dark'
}
}
};
// Safe property access
const theme = user?.profile?.settings?.theme; // 'dark'
const missing = user?.profile?.preferences?.language; // undefined
// Safe method calls
const result = api.someMethod?.(); // Only calls if method exists
// Safe array access
const firstItem = items?.[0]; // Safe even if items is null/undefined
// ✅ Nullish coalescing for default values
const displayName = user.displayName ?? user.name ?? 'Anonymous';
const port = process.env.PORT ?? 3000;
const config = userConfig ?? defaultConfig;
// ✅ Combining both patterns
function getUserPreference(user, key, defaultValue) {
return user?.preferences?.[key] ?? defaultValue;
}
const fontSize = getUserPreference(user, 'fontSize', 'medium');
// ❌ Manual null checking
if (user && user.profile && user.profile.settings && user.profile.settings.theme) {
const theme = user.profile.settings.theme;
}
// ❌ Using || for defaults (can be problematic)
const port = process.env.PORT || 3000; // Issues if PORT is '0' or false
const displayName = user.displayName || user.name || 'Anonymous'; // Issues if displayName is ''