Advanced JavaScript Concepts

Optional Deep Dive Material for Session 2 (Optional)

Université de Toulon

LIS UMR CNRS 7020

2025-10-11

Optional Advanced Material

This document contains advanced JavaScript concepts that were moved from the main Session 2 lecture to keep the core session focused and manageable.

Prerequisites: Complete Session 2 main content first.

When to read: When you’re comfortable with JavaScript basics and want to deepen your understanding before or after Session 3.

Advanced Array Methods

Beyond map() and filter()

While Session 2 covers basic map() and filter(), here are additional powerful array methods:

reduce() - The Swiss Army Knife

// Calculate total price
const prices = [19.99, 29.99, 39.99];
const total = prices.reduce((sum, price) => sum + price, 0);
// Result: 89.97

// Group reviews by rating
const reviews = [
  { rating: 5, comment: "Great!" },
  { rating: 4, comment: "Good" },
  { rating: 5, comment: "Excellent!" }
];

const groupedByRating = reviews.reduce((groups, review) => {
  const rating = review.rating;
  if (!groups[rating]) groups[rating] = [];
  groups[rating].push(review);
  return groups;
}, {});
// Result: { 4: [...], 5: [...] }

find() and findIndex()

// Find specific review
const reviews = [
  { id: 1, rating: 5 },
  { id: 2, rating: 3 },
  { id: 3, rating: 4 }
];

const highRating = reviews.find(review => review.rating >= 4);
// Result: { id: 1, rating: 5 }

const index = reviews.findIndex(review => review.id === 2);
// Result: 1

some() and every()

// Check if any review is 5 stars
const hasExcellentReview = reviews.some(review => review.rating === 5);

// Check if all reviews are positive (>= 3)
const allPositive = reviews.every(review => review.rating >= 3);

Advanced Error Handling

Try-Catch Patterns

Async Error Handling

// Basic pattern (covered in Session 2)
async function fetchReviews() {
  try {
    const response = await fetch('/api/reviews');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
    return [];
  }
}

// Advanced pattern with specific error types
async function fetchReviewsAdvanced(productId) {
  try {
    const response = await fetch(`/api/products/${productId}/reviews`);
    
    if (!response.ok) {
      if (response.status === 404) {
        throw new Error('Product not found');
      } else if (response.status === 500) {
        throw new Error('Server error - please try again later');
      } else {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
    }
    
    const data = await response.json();
    return data;
  } catch (error) {
    if (error instanceof TypeError) {
      // Network error
      throw new Error('Network error - check your connection');
    }
    throw error; // Re-throw other errors
  }
}

Error Boundaries in Vanilla JS

// Global error handler
window.addEventListener('error', (event) => {
  console.error('Global error:', event.error);
  // Send to logging service
  // Show user-friendly message
});

// Unhandled promise rejection handler
window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);
  event.preventDefault(); // Prevent console error
});

Browser Storage APIs

localStorage and sessionStorage

Basic Usage

// Save user preferences
const userPrefs = {
  theme: 'dark',
  itemsPerPage: 10,
  sortBy: 'rating'
};

localStorage.setItem('userPrefs', JSON.stringify(userPrefs));

// Retrieve preferences
const savedPrefs = JSON.parse(localStorage.getItem('userPrefs') || '{}');

Shopping Cart Implementation

class ShoppingCart {
  constructor() {
    this.items = this.loadFromStorage();
  }
  
  addItem(product) {
    const existingItem = this.items.find(item => item.id === product.id);
    
    if (existingItem) {
      existingItem.quantity += 1;
    } else {
      this.items.push({ ...product, quantity: 1 });
    }
    
    this.saveToStorage();
    this.updateUI();
  }
  
  removeItem(productId) {
    this.items = this.items.filter(item => item.id !== productId);
    this.saveToStorage();
    this.updateUI();
  }
  
  loadFromStorage() {
    try {
      return JSON.parse(localStorage.getItem('cart') || '[]');
    } catch (error) {
      console.error('Error loading cart:', error);
      return [];
    }
  }
  
  saveToStorage() {
    try {
      localStorage.setItem('cart', JSON.stringify(this.items));
    } catch (error) {
      console.error('Error saving cart:', error);
    }
  }
  
  updateUI() {
    // Update cart display
    const cartElement = document.querySelector('#cart-items');
    if (cartElement) {
      cartElement.innerHTML = this.items.map(item => `
        <div class="cart-item">
          <span>${item.name}</span>
          <span>Qty: ${item.quantity}</span>
          <span>$${(item.price * item.quantity).toFixed(2)}</span>
        </div>
      `).join('');
    }
  }
}

// Usage
const cart = new ShoppingCart();

Advanced DOM Manipulation

Event Delegation

Instead of adding event listeners to every button:

// ❌ Bad: Multiple event listeners
document.querySelectorAll('.review-like-btn').forEach(btn => {
  btn.addEventListener('click', handleLike);
});

// ✅ Good: Single delegated event listener
document.addEventListener('click', (event) => {
  if (event.target.matches('.review-like-btn')) {
    handleLike(event);
  }
});

Creating Complex Elements

function createReviewCard(review) {
  const card = document.createElement('div');
  card.className = 'review-card bg-white p-4 rounded-lg shadow';
  
  // Create star rating
  const stars = Array.from({ length: 5 }, (_, i) => {
    const star = document.createElement('span');
    star.className = i < review.rating ? 'star-filled' : 'star-empty';
    star.textContent = '★';
    return star;
  });
  
  const starsContainer = document.createElement('div');
  starsContainer.className = 'flex gap-1';
  starsContainer.append(...stars);
  
  // Create content
  const title = document.createElement('h3');
  title.textContent = review.title;
  title.className = 'font-bold text-lg';
  
  const content = document.createElement('p');
  content.textContent = review.content;
  content.className = 'text-gray-700 mt-2';
  
  const author = document.createElement('div');
  author.textContent = `By ${review.author} on ${review.date}`;
  author.className = 'text-sm text-gray-500 mt-2';
  
  // Assemble card
  card.append(starsContainer, title, content, author);
  
  return card;
}

Performance Optimization

Debouncing and Throttling

Search Input Debouncing

function debounce(func, delay) {
  let timeoutId;
  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

// Usage for search
const searchInput = document.querySelector('#search');
const debouncedSearch = debounce(performSearch, 300);

searchInput.addEventListener('input', (event) => {
  debouncedSearch(event.target.value);
});

function performSearch(query) {
  console.log('Searching for:', query);
  // Perform actual search
}

Scroll Event Throttling

function throttle(func, delay) {
  let timeoutId;
  let lastExecTime = 0;
  
  return function (...args) {
    const currentTime = Date.now();
    
    if (currentTime - lastExecTime > delay) {
      func.apply(this, args);
      lastExecTime = currentTime;
    } else {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        func.apply(this, args);
        lastExecTime = Date.now();
      }, delay - (currentTime - lastExecTime));
    }
  };
}

// Usage for scroll events
const throttledScrollHandler = throttle(handleScroll, 100);
window.addEventListener('scroll', throttledScrollHandler);

Modern JavaScript Features

Optional Chaining

// Safe property access
const userCity = user?.profile?.address?.city;

// Safe method calls
const result = api.getData?.();

// Safe array access
const firstReview = reviews?.[0];

Nullish Coalescing

// Use default only for null/undefined
const username = user.name ?? 'Anonymous';
const port = process.env.PORT ?? 3000;

// Different from || operator
const count = 0;
console.log(count || 10);  // 10 (falsy)
console.log(count ?? 10);  // 0 (not nullish)

Destructuring Patterns

// Array destructuring with rest
const [first, second, ...rest] = reviews;

// Object destructuring with renaming
const { title: reviewTitle, rating: stars } = review;

// Nested destructuring
const { user: { name, profile: { age } } } = reviewData;

// Function parameter destructuring
function displayReview({ title, rating, author = 'Anonymous' }) {
  console.log(`${title} - ${rating} stars by ${author}`);
}

When to Use These Advanced Features

In React Development: - Array methods: Essential for rendering lists and data manipulation - Error handling: Critical for user experience - Storage APIs: Less needed (React has state management) - DOM manipulation: Rarely needed (React handles DOM) - Performance: React has built-in optimizations

Recommendation: Focus on array methods and error handling first. The other topics become more relevant in specialized scenarios or when working outside React.