Sign In Create Account
Modern Features

Modern JavaScript Features

ES6+ features and modern JavaScript patterns for cleaner, more maintainable code.

Destructuring

Prefer destructuring over manual variable assignment for cleaner, more readable code.

  • • Use object destructuring for extracting multiple properties
  • • Use array destructuring for position-based values
  • • Combine with default values and renaming
  • • Useful for function parameters and return values

Destructuring Examples

// ✅ 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

  • • Prefer template literals over string concatenation
  • • Use for multi-line strings
  • • Leverage expression interpolation
  • • Great for HTML templates and SQL queries

Template Literal Examples

// ✅ 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';

Array Methods

Use modern array methods for functional programming patterns instead of traditional loops.

Transformation Methods

  • • Use map() for transforming arrays
  • • Use filter() for filtering elements
  • • Use reduce() for accumulation
  • • Chain methods for complex transformations

Array Method Examples

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
  }
}

Advanced Array Methods

Advanced Array Examples

// ✅ 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]

Promises & Async/Await

Use modern asynchronous patterns for cleaner, more readable async code.

Async/Await Pattern

  • • Prefer async/await over .then() chains
  • • Always handle errors with try/catch blocks
  • • Use Promise.all() for concurrent operations
  • • Use Promise.allSettled() when some operations can fail

Async/Await Examples

// ✅ 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 Patterns

Promise Pattern Examples

// ✅ 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);
    }
  }
}

Modern Object Features

Object Shorthand & Spread

Object Feature Examples

// ✅ 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 & Nullish Coalescing

Safe Property Access Examples

// ✅ 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 ''