JavaScript

The Complete JavaScript Course 2025-2026: From Zero to Expert – Modern JavaScript

the complete javascript course 2025-2026 from zero to expert

Why JavaScript Remains Essential in 2025-2026

JavaScript continues to dominate the programming landscape in 2025-2026, powering over 98% of all websites worldwide. The Complete JavaScript Course 2025-2026: From Zero to Expert – Your Ultimate Guide to Mastering Modern JavaScript. Whether you’re aspiring to become a front-end developer, full-stack engineer, or mobile app creator, mastering JavaScript is your gateway to countless career opportunities. This comprehensive JavaScript course guide will take you from absolute beginner to expert level, covering everything you need to know about modern JavaScript development.

The JavaScript ecosystem has evolved dramatically, with new frameworks, tools, and best practices emerging constantly. This complete JavaScript course 2025-2026 curriculum reflects the latest industry standards, incorporating ECMAScript 2024 features, modern development workflows, and real-world project implementations that employers actually seek.

Table of Contents

  1. JavaScript Fundamentals – Building Your Foundation
  2. Advanced JavaScript Concepts – Deep Diving into Core Mechanics
  3. Asynchronous JavaScript – Mastering Promises, Async/Await, and APIs
  4. Modern JavaScript Development – Tools, Workflows, and Best Practices
  5. JavaScript Frameworks and Libraries – React, Vue, Angular, and Beyond
  6. Backend JavaScript with Node.js – Server-Side Development
  7. Testing and Debugging – Writing Reliable, Maintainable Code
  8. JavaScript Design Patterns – Professional Code Architecture
  9. Performance Optimization – Building Fast, Efficient Applications
  10. Real-World Projects – Portfolio-Ready Applications

The Complete JavaScript Course 2025-2026: From Zero to Expert – Modern JavaScript


Part 1: JavaScript Fundamentals – Building Your Foundation

Understanding JavaScript: What, Why, and How

JavaScript is a high-level, interpreted programming language that enables interactive web experiences. Originally created by Brendan Eich in just 10 days back in 1995, JavaScript has transformed into a sophisticated, multi-paradigm language supporting object-oriented, functional, and imperative programming styles.

Why Learn JavaScript in 2025-2026?

The demand for JavaScript developers has never been higher. JavaScript enables:

  • Front-end Development: Creating interactive user interfaces and single-page applications
  • Back-end Development: Building scalable servers with Node.js and Deno
  • Mobile Development: Crafting cross-platform apps with React Native and Ionic
  • Desktop Applications: Developing desktop software using Electron
  • Machine Learning: Implementing AI features with TensorFlow.js
  • Internet of Things: Programming IoT devices and robotics
  • Game Development: Building browser-based and mobile games

Setting Up Your Development Environment

Before writing your first line of code, you’ll need a proper development setup:

Essential Tools for 2025-2026:

  1. Code Editor: Visual Studio Code remains the industry standard, offering excellent JavaScript support, IntelliSense, debugging capabilities, and thousands of extensions. Alternative options include WebStorm, Sublime Text, and Atom.
  2. Web Browser: Chrome DevTools or Firefox Developer Edition provide powerful debugging, profiling, and network analysis tools essential for JavaScript development.
  3. Node.js and npm: Install the latest LTS (Long Term Support) version of Node.js, which includes npm (Node Package Manager) for managing project dependencies.
  4. Version Control: Git for tracking code changes and GitHub/GitLab for collaboration and portfolio building.
  5. Modern Terminal: Use iTerm2 (Mac), Windows Terminal (Windows), or any Linux terminal for executing commands and running development servers.

JavaScript Basics: Variables, Data Types, and Operators

Variables and Declaration Keywords:

JavaScript offers three ways to declare variables in modern code:

  • let: Block-scoped variable that can be reassigned
  • const: Block-scoped constant that cannot be reassigned (use by default)
  • var: Function-scoped variable (legacy, avoid in modern code)
// Modern variable declaration
let userName = "Sarah";
const birthYear = 1995;
let currentYear = 2025;
let age = currentYear - birthYear; // 30

Primitive Data Types:

JavaScript has seven primitive data types:

  1. String: Text enclosed in quotes ('hello', "world", `template`)
  2. Number: Both integers and floating-point numbers (64-bit IEEE 754)
  3. BigInt: Integers larger than 2^53 – 1
  4. Boolean: True or false values
  5. Undefined: Variable declared but not assigned
  6. Null: Intentional absence of value
  7. Symbol: Unique identifier (primarily for object properties)

Operators:

JavaScript provides various operators for different operations:

  • Arithmetic: +, -, *, /, % (modulo), ** (exponentiation)
  • Assignment: =, +=, -=, *=, /=
  • Comparison: ===, !==, >, <, >=, <=
  • Logical: && (AND), || (OR), ! (NOT)
  • Nullish Coalescing: ?? (returns right operand when left is null/undefined)
  • Optional Chaining: ?. (safe property access)

Control Flow: Conditionals and Loops

Conditional Statements:

Control program flow based on conditions:

// If-else statement
if (age >= 18) {
    console.log("Adult");
} else if (age >= 13) {
    console.log("Teenager");
} else {
    console.log("Child");
}

// Ternary operator (concise conditional)
const status = age >= 18 ? "Adult" : "Minor";

// Switch statement (multiple conditions)
switch (day) {
    case "Monday":
        console.log("Start of work week");
        break;
    case "Friday":
        console.log("Almost weekend!");
        break;
    default:
        console.log("Regular day");
}

Loops:

Iterate over data efficiently:

// For loop (traditional)
for (let i = 0; i < 5; i++) {
    console.log(i);
}

// While loop
let count = 0;
while (count < 5) {
    console.log(count);
    count++;
}

// For...of loop (iterating arrays)
const colors = ["red", "green", "blue"];
for (const color of colors) {
    console.log(color);
}

// For...in loop (object properties)
const person = { name: "John", age: 30 };
for (const key in person) {
    console.log(`${key}: ${person[key]}`);
}

Functions: The Heart of JavaScript

Functions are reusable blocks of code that perform specific tasks.

Function Declaration:

function calculateArea(width, height) {
    return width * height;
}

const area = calculateArea(10, 5); // 50

Function Expression:

const calculateArea = function(width, height) {
    return width * height;
};

Arrow Functions (ES6+):

// Concise syntax
const calculateArea = (width, height) => width * height;

// With block body
const greet = (name) => {
    const message = `Hello, ${name}!`;
    return message;
};

Default Parameters:

function greet(name = "Guest", time = "day") {
    return `Good ${time}, ${name}!`;
}

greet(); // "Good day, Guest!"
greet("Sarah", "morning"); // "Good morning, Sarah!"

Rest Parameters:

function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

sum(1, 2, 3, 4, 5); // 15

Arrays: Working with Collections

Arrays are ordered collections of values.

Array Creation and Basic Methods:

const fruits = ["apple", "banana", "orange"];

// Adding elements
fruits.push("grape");      // Add to end
fruits.unshift("mango");   // Add to beginning

// Removing elements
fruits.pop();              // Remove from end
fruits.shift();            // Remove from beginning

// Finding elements
const index = fruits.indexOf("banana");
const includes = fruits.includes("apple");

// Accessing elements
const firstFruit = fruits[0];
const lastFruit = fruits[fruits.length - 1];

Modern Array Methods (Essential for 2025-2026):

const numbers = [1, 2, 3, 4, 5];

// Map (transform each element)
const doubled = numbers.map(num => num * 2);
// [2, 4, 6, 8, 10]

// Filter (select elements matching condition)
const evenNumbers = numbers.filter(num => num % 2 === 0);
// [2, 4]

// Reduce (accumulate to single value)
const sum = numbers.reduce((total, num) => total + num, 0);
// 15

// Find (first element matching condition)
const firstEven = numbers.find(num => num % 2 === 0);
// 2

// Some (at least one element matches)
const hasEven = numbers.some(num => num % 2 === 0);
// true

// Every (all elements match)
const allPositive = numbers.every(num => num > 0);
// true

// ForEach (execute function for each element)
numbers.forEach((num, index) => {
    console.log(`Index ${index}: ${num}`);
});

Advanced Array Techniques:

// Destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]

// Spread operator
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// [1, 2, 3, 4, 5, 6]

// Array.from() (create array from iterable)
const range = Array.from({ length: 5 }, (_, i) => i + 1);
// [1, 2, 3, 4, 5]

// Flat and FlatMap (2025 essentials)
const nested = [1, [2, 3], [4, [5, 6]]];
const flat = nested.flat(2);
// [1, 2, 3, 4, 5, 6]

Objects: Modeling Real-World Entities

Objects store collections of key-value pairs.

Object Creation and Access:

const person = {
    firstName: "John",
    lastName: "Doe",
    age: 30,
    hobbies: ["reading", "coding", "hiking"],
    address: {
        street: "123 Main St",
        city: "New York"
    },
    fullName: function() {
        return `${this.firstName} ${this.lastName}`;
    }
};

// Accessing properties
console.log(person.firstName);        // Dot notation
console.log(person["lastName"]);      // Bracket notation
console.log(person.address.city);     // Nested access
console.log(person.fullName());       // Method call

Modern Object Features:

// Object destructuring
const { firstName, age, address: { city } } = person;

// Property shorthand
const name = "Sarah";
const age = 25;
const user = { name, age }; // { name: "Sarah", age: 25 }

// Computed property names
const propertyName = "email";
const user = {
    [propertyName]: "sarah@example.com"
};

// Object spread
const basicInfo = { name: "John", age: 30 };
const fullInfo = { ...basicInfo, city: "NYC", job: "Developer" };

// Optional chaining (critical for 2025-2026)
const cityName = person?.address?.city ?? "Unknown";

Essential Object Methods:

// Object.keys() - get all keys
const keys = Object.keys(person);
// ["firstName", "lastName", "age", ...]

// Object.values() - get all values
const values = Object.values(person);

// Object.entries() - get key-value pairs
const entries = Object.entries(person);
// [["firstName", "John"], ["lastName", "Doe"], ...]

// Object.assign() - merge objects
const merged = Object.assign({}, obj1, obj2);

// Object.freeze() - make immutable
Object.freeze(person);

// Object.seal() - prevent adding/removing properties
Object.seal(person);

Part 2: Advanced JavaScript Concepts – Deep Diving into Core Mechanics

The JavaScript Engine and Runtime

Understanding how JavaScript executes code is crucial for writing efficient applications.

JavaScript Engine Components:

  1. Call Stack: Keeps track of function execution contexts
  2. Heap: Memory allocation for objects and variables
  3. Event Loop: Manages asynchronous operations
  4. Callback Queue: Stores callback functions waiting to execute
  5. Web APIs: Browser-provided functions (setTimeout, fetch, DOM manipulation)

Execution Context:

Every function creates an execution context containing:

  • Variable environment (variables and function declarations)
  • Scope chain (access to outer variables)
  • this keyword binding

Scope and Scope Chain

Scope determines variable accessibility.

Types of Scope:

// Global scope
const globalVar = "I'm global";

function outer() {
    // Function scope
    const outerVar = "I'm in outer";
    
    function inner() {
        // Nested function scope
        const innerVar = "I'm in inner";
        console.log(globalVar); // Accessible
        console.log(outerVar);  // Accessible
        console.log(innerVar);  // Accessible
    }
    
    if (true) {
        // Block scope (let and const only)
        const blockVar = "I'm in block";
        var functionVar = "I'm function-scoped";
    }
    
    // console.log(blockVar); // Error!
    console.log(functionVar);  // Works (var is function-scoped)
}

Lexical Scoping:

JavaScript uses lexical (static) scoping, meaning inner functions have access to variables from their outer functions based on where they’re written in the code, not where they’re called.

Hoisting: Understanding Variable and Function Behavior

Hoisting is JavaScript’s behavior of moving declarations to the top of their scope.

// Function declarations are hoisted
sayHello(); // Works!
function sayHello() {
    console.log("Hello!");
}

// Variable declarations (var) are hoisted but not initialized
console.log(myVar); // undefined (not ReferenceError)
var myVar = 5;

// let and const are hoisted but in "temporal dead zone"
// console.log(myLet); // ReferenceError
let myLet = 10;

// Function expressions are NOT hoisted
// sayGoodbye(); // TypeError
const sayGoodbye = function() {
    console.log("Goodbye!");
};

The this Keyword: Context Matters

The this keyword refers to different objects depending on how a function is called.

// 1. Global context
console.log(this); // Window object (browser) or global (Node.js)

// 2. Object method
const person = {
    name: "Sarah",
    greet() {
        console.log(this.name); // "Sarah"
    }
};

// 3. Regular function call
function showThis() {
    console.log(this); // undefined (strict mode) or Window
}

// 4. Arrow functions (lexical this)
const obj = {
    name: "John",
    regularFunc: function() {
        setTimeout(function() {
            console.log(this.name); // undefined (this refers to Window)
        }, 1000);
    },
    arrowFunc: function() {
        setTimeout(() => {
            console.log(this.name); // "John" (this from outer context)
        }, 1000);
    }
};

// 5. Explicit binding
const person1 = { name: "Alice" };
const person2 = { name: "Bob" };

function introduce() {
    console.log(`My name is ${this.name}`);
}

introduce.call(person1);  // "My name is Alice"
introduce.apply(person2); // "My name is Bob"
const boundFunc = introduce.bind(person1);
boundFunc(); // "My name is Alice"

Closures: Functions with Memory

A closure is a function that has access to variables from its outer (enclosing) function, even after the outer function has returned.

function createCounter() {
    let count = 0;
    
    return {
        increment() {
            count++;
            return count;
        },
        decrement() {
            count--;
            return count;
        },
        getCount() {
            return count;
        }
    };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount());  // 1
// count variable is private, accessible only through methods

Practical Closure Applications:

// Data privacy
function bankAccount(initialBalance) {
    let balance = initialBalance;
    
    return {
        deposit(amount) {
            balance += amount;
            return balance;
        },
        withdraw(amount) {
            if (amount <= balance) {
                balance -= amount;
                return balance;
            }
            return "Insufficient funds";
        },
        getBalance() {
            return balance;
        }
    };
}

// Function factories
function createMultiplier(multiplier) {
    return function(number) {
        return number * multiplier;
    };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15

Prototypes and Prototypal Inheritance

JavaScript uses prototype-based inheritance rather than classical inheritance.

Understanding Prototypes:

// Every object has a prototype
const obj = {};
console.log(obj.__proto__); // Object.prototype

// Arrays inherit from Array.prototype
const arr = [1, 2, 3];
console.log(arr.__proto__ === Array.prototype); // true

// Constructor functions
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.greet = function() {
    return `Hello, I'm ${this.name}`;
};

const john = new Person("John", 30);
console.log(john.greet()); // "Hello, I'm John"
console.log(john.__proto__ === Person.prototype); // true

ES6 Classes (Syntactic Sugar):

class Animal {
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
    
    makeSound() {
        return "Some generic sound";
    }
    
    describe() {
        return `${this.name} is a ${this.species}`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name, "dog");
        this.breed = breed;
    }
    
    makeSound() {
        return "Woof!";
    }
    
    fetch() {
        return `${this.name} is fetching!`;
    }
}

const buddy = new Dog("Buddy", "Golden Retriever");
console.log(buddy.describe());  // "Buddy is a dog"
console.log(buddy.makeSound()); // "Woof!"
console.log(buddy.fetch());     // "Buddy is fetching!"

Class Features in 2025-2026:

class User {
    // Private fields (2025 standard)
    #password;
    
    // Public fields
    isActive = true;
    
    // Static properties
    static userCount = 0;
    
    constructor(username, password) {
        this.username = username;
        this.#password = password;
        User.userCount++;
    }
    
    // Private methods
    #hashPassword() {
        return `hashed_${this.#password}`;
    }
    
    // Getter
    get accountStatus() {
        return this.isActive ? "Active" : "Inactive";
    }
    
    // Setter
    set accountStatus(status) {
        this.isActive = status === "active";
    }
    
    // Static methods
    static getCount() {
        return User.userCount;
    }
}

Object-Oriented Programming Principles

Four Pillars of OOP:

  1. Encapsulation: Bundling data and methods, hiding internal details
  2. Abstraction: Hiding complex implementation, showing only necessary features
  3. Inheritance: Creating new classes based on existing ones
  4. Polymorphism: Objects of different types responding to the same method call
// Encapsulation example
class BankAccount {
    #balance = 0;
    
    constructor(owner) {
        this.owner = owner;
    }
    
    deposit(amount) {
        if (amount > 0) {
            this.#balance += amount;
            return this.#balance;
        }
    }
    
    getBalance() {
        return this.#balance;
    }
}

// Inheritance and Polymorphism
class Shape {
    constructor(color) {
        this.color = color;
    }
    
    calculateArea() {
        throw new Error("Method must be implemented");
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }
    
    calculateArea() {
        return Math.PI * this.radius ** 2;
    }
}

class Rectangle extends Shape {
    constructor(color, width, height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    calculateArea() {
        return this.width * this.height;
    }
}

// Polymorphism in action
const shapes = [
    new Circle("red", 5),
    new Rectangle("blue", 4, 6)
];

shapes.forEach(shape => {
    console.log(`${shape.color} shape area: ${shape.calculateArea()}`);
});

Part 3: Asynchronous JavaScript – Mastering Promises, Async/Await, and APIs

Understanding Asynchronous Programming

JavaScript is single-threaded but can handle asynchronous operations through the event loop.

Synchronous vs Asynchronous:

// Synchronous (blocking)
console.log("First");
console.log("Second");
console.log("Third");
// Output: First, Second, Third (in order)

// Asynchronous (non-blocking)
console.log("First");
setTimeout(() => console.log("Second"), 1000);
console.log("Third");
// Output: First, Third, Second

Callbacks: The Original Async Pattern

function fetchUserData(userId, callback) {
    setTimeout(() => {
        const user = { id: userId, name: "John" };
        callback(user);
    }, 1000);
}

fetchUserData(1, (user) => {
    console.log(user);
});

// Callback Hell (avoid this!)
getData((a) => {
    getMoreData(a, (b) => {
        getMoreData(b, (c) => {
            getMoreData(c, (d) => {
                console.log(d);
            });
        });
    });
});

Promises: Modern Asynchronous Pattern

Promises represent eventual completion or failure of an asynchronous operation.

Promise States:

  • Pending: Initial state
  • Fulfilled: Operation completed successfully
  • Rejected: Operation failed
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
    const success = true;
    
    setTimeout(() => {
        if (success) {
            resolve("Operation successful!");
        } else {
            reject("Operation failed!");
        }
    }, 1000);
});

// Consuming Promises
myPromise
    .then(result => {
        console.log(result);
        return "Next step";
    })
    .then(nextResult => {
        console.log(nextResult);
    })
    .catch(error => {
        console.error(error);
    })
    .finally(() => {
        console.log("Always executes");
    });

Promise Chaining:

function fetchUser(userId) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({ id: userId, name: "Sarah" });
        }, 1000);
    });
}

function fetchPosts(userId) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve([
                { id: 1, title: "Post 1" },
                { id: 2, title: "Post 2" }
            ]);
        }, 1000);
    });
}

// Clean chaining
fetchUser(1)
    .then(user => {
        console.log(user);
        return fetchPosts(user.id);
    })
    .then(posts => {
        console.log(posts);
    })
    .catch(error => {
        console.error(error);
    });

Promise Combinators (Essential for 2025-2026):

// Promise.all() - wait for all promises
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = Promise.resolve(1);

Promise.all([promise1, promise2, promise3])
    .then(results => {
        console.log(results); // [3, 2, 1]
    });

// Promise.race() - first promise to settle
Promise.race([promise1, promise2, promise3])
    .then(result => {
        console.log(result); // 3 (first to resolve)
    });

// Promise.allSettled() - wait for all, regardless of outcome
const promises = [
    Promise.resolve(1),
    Promise.reject("Error"),
    Promise.resolve(3)
];

Promise.allSettled(promises)
    .then(results => {
        console.log(results);
        // [
        //   { status: 'fulfilled', value: 1 },
        //   { status: 'rejected', reason: 'Error' },
        //   { status: 'fulfilled', value: 3 }
        // ]
    });

// Promise.any() - first fulfilled promise
Promise.any(promises)
    .then(result => {
        console.log(result); // 1 (first fulfilled)
    });

Async/Await: Syntactic Sugar for Promises

Async/await makes asynchronous code look synchronous and is the preferred approach in 2025-2026.

// Basic async function
async function fetchData() {
    return "Data fetched";
}

// Always returns a Promise
fetchData().then(data => console.log(data));

// Using await
async function getUserData() {
    try {
        const user = await fetchUser(1);
        console.log(user);
        
        const posts = await fetchPosts(user.id);
        console.log(posts);
        
        return { user, posts };
    } catch (error) {
        console.error("Error:", error);
        throw error;
    }
}

// Parallel execution with async/await
async function fetchMultipleUsers() {
    try {
        // Sequential (slow)
        const user1 = await fetchUser(1);
        const user2 = await fetchUser(2);
        
        // Parallel (fast)
        const [user1, user2] = await Promise.all([
            fetchUser(1),
            fetchUser(2)
        ]);
        
        return [user1, user2];
    } catch (error) {
        console.error(error);
    }
}

Error Handling Patterns:

// Try-catch blocks
async function handleErrors() {
    try {
        const data = await riskyOperation();
        return data;
    } catch (error) {
        if (error.code === "NETWORK_ERROR") {
            // Handle network errors
        } else {
            // Handle other errors
        }
        throw error; // Re-throw if needed
    } finally {
        // Cleanup code
    }
}

// Promise catch
async function alternativeErrorHandling() {
    return await riskyOperation()
        .catch(error => {
            console.error(error);
            return defaultValue;
        });
}

Working with APIs: Fetch and AJAX

The Fetch API:

// Basic GET request
async function fetchTodos() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const data = await response.json();
        console.log(data);
        return data;
    } catch (error) {
        console.error("Fetch error:", error);
    }
}

// POST request
async function createPost(postData) {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(postData)
        });
        
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("Error creating post:", error);
    }
}

// Using Fetch with multiple endpoints
async function fetchUserProfile(userId) {
    try {
        const [user, posts, todos] = await Promise.all([
            fetch(`/api/users/${userId}`).then(r => r.json()),
            fetch(`/api/users/${userId}/posts`).then(r => r.json()),
            fetch(`/api/users/${userId}/todos`).then(r => r.json())
        ]);
        
        return { user, posts, todos };
    } catch (error) {
        console.error("Error fetching user profile:", error);
    }
}

Advanced Fetch Patterns:

// Request with timeout
async function fetchWithTimeout(url, timeout = 5000) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);
    
    try {
        const response = await fetch(url, {
            signal: controller.signal
        });
        clearTimeout(timeoutId);
        return await response.json();
    } catch (error) {
        if (error.name === 'AbortError') {
            throw new Error('Request timeout');
        }
        throw error;
    }
}

// Retry logic
async function fetchWithRetry(url, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            const response = await fetch(url);
            if (response.ok) {
                return await response.json();
            }
        } catch (error) {
            if (i === maxRetries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        }
    }
}

// Caching responses
const cache = new Map();

async function fetchWithCache(url) {
    if (cache.has(url)) {
        return cache.get(url);
    }
    
    const response = await fetch(url);
    const data = await response.json();
    cache.set(url, data);
    
    return data;
}

Part 4: Modern JavaScript Development – Tools, Workflows, and Best Practices

ES6+ Features: Modern JavaScript Syntax

Template Literals:

const name = "Sarah";
const age = 28;
const greeting = `Hello, my name is ${name} and I'm ${age} years old.`;

// Multi-line strings
const html = `
    <div class="card">
        <h2>${name}</h2>
        <p>Age: ${age}</p>
    </div>
`;

// Expression evaluation
const price = 100;
const tax = 0.15;
console.log(`Total: ${(price * (1 + tax)).toFixed(2)}`);

Destructuring Assignment:

// Array destructuring
const colors = ["red", "green", "blue", "yellow"];
const [primary, secondary, ...others] = colors;
// primary = "red", secondary = "green", others = ["blue", "yellow"]

// Skipping elements
const [first, , third] = colors;

// Object destructuring
const user = {
    username: "john_doe",
    email: "john@example.com",
    age: 30,
    location: {
        city: "New York",
        country: "USA"
    }
};

const { username, email, age: userAge } = user;
// Rename with : syntax

const { location: { city } } = user;
// Nested destructuring

// Default values
const { role = "user", premium = false } = user;

// Function parameter destructuring
function displayUser({ username, email, age = "Unknown" }) {
    console.log(`${username} (${email}) - Age: ${age}`);
}

displayUser(user);

Spread and Rest Operators:

// Spread operator (...)
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// Copying arrays (shallow copy)
const originalArray = [1, 2, 3];
const copiedArray = [...originalArray];

// Spreading in function calls
const numbers = [5, 3, 9, 1];
console.log(Math.max(...numbers)); // 9

// Object spread
const person = { name: "John", age: 30 };
const employee = { ...person, role: "Developer", salary: 75000 };

// Overriding properties
const updated = { ...person, age: 31 };

// Rest operator in functions
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

sum(1, 2, 3, 4, 5); // 15

// Rest in destructuring
const [head, ...tail] = [1, 2, 3, 4, 5];
// head = 1, tail = [2, 3, 4, 5]

Enhanced Object Literals:

const name = "Laptop";
const price = 999;
const brand = "TechCorp";

// Property shorthand
const product = {
    name,
    price,
    brand,
    
    // Method shorthand
    display() {
        return `${this.name} - ${this.price}`;
    },
    
    // Computed property names
    [`is${brand}Product`]: true
};

console.log(product.isTechCorpProduct); // true

Modules (ES6 Modules):

// export.js - Named exports
export const API_URL = "https://api.example.com";
export const API_KEY = "your-api-key";

export function fetchData(endpoint) {
    return fetch(`${API_URL}/${endpoint}`);
}

export class User {
    constructor(name) {
        this.name = name;
    }
}

// Default export
export default function processData(data) {
    return data.map(item => item.value);
}

// import.js - Importing
import processData from './export.js'; // Default import
import { API_URL, fetchData, User } from './export.js'; // Named imports
import * as api from './export.js'; // Namespace import
import { fetchData as getData } from './export.js'; // Alias import

// Dynamic imports (2025 standard)
async function loadModule() {
    const module = await import('./heavyModule.js');
    module.initialize();
}

Optional Chaining and Nullish Coalescing:

// Optional chaining (?.)
const user = {
    name: "John",
    address: {
        street: "123 Main St"
        // city is missing
    }
};

// Safe property access
const city = user?.address?.city; // undefined (no error)
const zip = user?.address?.zip?.code; // undefined

// Optional method calling
const result = obj.method?.(); // Only calls if method exists

// Array optional access
const firstItem = arr?.[0];

// Nullish coalescing (??)
const value1 = null ?? "default"; // "default"
const value2 = undefined ?? "default"; // "default"
const value3 = 0 ?? "default"; // 0 (not replaced!)
const value4 = "" ?? "default"; // "" (not replaced!)
const value5 = false ?? "default"; // false (not replaced!)

// Combining both
const displayCity = user?.address?.city ?? "City not specified";

// Nullish assignment (??=)
let config = { timeout: null };
config.timeout ??= 3000; // Sets to 3000 only if null/undefined

Advanced Array Methods (2025-2026):

// At() method - negative indexing
const arr = [10, 20, 30, 40, 50];
console.log(arr.at(-1)); // 50 (last element)
console.log(arr.at(-2)); // 40 (second to last)

// FindLast() and findLastIndex()
const numbers = [5, 12, 8, 130, 44];
const lastLarge = numbers.findLast(num => num > 10); // 44
const lastIndex = numbers.findLastIndex(num => num > 10); // 4

// ToSorted() - non-mutating sort
const original = [3, 1, 4, 1, 5];
const sorted = original.toSorted(); // [1, 1, 3, 4, 5]
console.log(original); // [3, 1, 4, 1, 5] (unchanged)

// ToReversed() - non-mutating reverse
const reversed = original.toReversed();

// ToSpliced() - non-mutating splice
const spliced = original.toSpliced(1, 2, 99, 88);

// With() - non-mutating element replacement
const replaced = original.with(0, 999); // [999, 1, 4, 1, 5]

// Group() - grouping elements
const inventory = [
    { type: 'fruit', name: 'apple' },
    { type: 'vegetable', name: 'carrot' },
    { type: 'fruit', name: 'banana' }
];

const grouped = Object.groupBy(inventory, item => item.type);
// {
//   fruit: [{type: 'fruit', name: 'apple'}, {type: 'fruit', name: 'banana'}],
//   vegetable: [{type: 'vegetable', name: 'carrot'}]
// }

JavaScript Build Tools and Module Bundlers

Package Managers:

  1. npm (Node Package Manager):
# Initialize new project
npm init -y

# Install dependencies
npm install lodash
npm install --save-dev webpack webpack-cli

# Install globally
npm install -g typescript

# Update packages
npm update

# Audit security
npm audit
npm audit fix
  1. Yarn (Alternative to npm):
# Initialize
yarn init

# Install dependencies
yarn add lodash
yarn add --dev webpack

# Install all dependencies
yarn install

# Upgrade packages
yarn upgrade
  1. pnpm (Fast, disk-efficient):
# Install pnpm
npm install -g pnpm

# Use pnpm
pnpm install
pnpm add package-name

Module Bundlers:

Vite (Recommended for 2025-2026):

Vite is the modern, lightning-fast build tool that has become the industry standard.

# Create new Vite project
npm create vite@latest my-project
cd my-project
npm install
npm run dev

Vite Configuration (vite.config.js):

import { defineConfig } from 'vite';

export default defineConfig({
    root: './src',
    build: {
        outDir: '../dist',
        minify: 'terser',
        sourcemap: true
    },
    server: {
        port: 3000,
        open: true
    },
    optimizeDeps: {
        include: ['lodash']
    }
});

Webpack (Legacy but still widely used):

// webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    devServer: {
        static: './dist',
        port: 3000
    },
    mode: 'development'
};

Transpilers and Compilers

Babel – JavaScript Compiler:

Babel converts modern JavaScript into backwards-compatible versions.

// .babelrc configuration
{
    "presets": [
        ["@babel/preset-env", {
            "targets": {
                "browsers": [">0.25%", "not dead"]
            },
            "useBuiltIns": "usage",
            "corejs": 3
        }]
    ],
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-optional-chaining"
    ]
}

TypeScript (Highly Recommended for 2025-2026):

TypeScript adds static typing to JavaScript, catching errors during development.

// TypeScript basics
let username: string = "John";
let age: number = 30;
let isActive: boolean = true;
let scores: number[] = [90, 85, 88];

// Interfaces
interface User {
    id: number;
    name: string;
    email: string;
    role?: string; // Optional property
}

function displayUser(user: User): void {
    console.log(`${user.name} (${user.email})`);
}

// Type aliases
type ID = string | number;
type Response<T> = {
    data: T;
    status: number;
    message: string;
};

// Generics
function getFirstElement<T>(arr: T[]): T | undefined {
    return arr[0];
}

const firstNumber = getFirstElement([1, 2, 3]); // number | undefined
const firstName = getFirstElement(["a", "b"]); // string | undefined

// Classes with TypeScript
class Animal {
    private name: string;
    protected species: string;
    public age: number;
    
    constructor(name: string, species: string, age: number) {
        this.name = name;
        this.species = species;
        this.age = age;
    }
    
    public describe(): string {
        return `${this.name} is a ${this.species}`;
    }
}

Code Quality Tools

ESLint – JavaScript Linter:

// .eslintrc.json
{
    "env": {
        "browser": true,
        "es2021": true,
        "node": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:react/recommended"
    ],
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "rules": {
        "indent": ["error", 4],
        "quotes": ["error", "single"],
        "semi": ["error", "always"],
        "no-unused-vars": "warn",
        "no-console": "off",
        "prefer-const": "error",
        "arrow-spacing": "error"
    }
}

Prettier – Code Formatter:

// .prettierrc
{
    "semi": true,
    "singleQuote": true,
    "tabWidth": 4,
    "trailingComma": "es5",
    "printWidth": 80,
    "arrowParens": "always"
}

Husky – Git Hooks:

// package.json
{
    "husky": {
        "hooks": {
            "pre-commit": "lint-staged"
        }
    },
    "lint-staged": {
        "*.js": [
            "eslint --fix",
            "prettier --write"
        ]
    }
}

Modern JavaScript Best Practices (2025-2026)

1. Use Modern Syntax:

// ✅ Good - Modern ES6+
const getUserData = async (userId) => {
    const response = await fetch(`/api/users/${userId}`);
    return await response.json();
};

// ❌ Avoid - Old-style
function getUserData(userId) {
    return fetch('/api/users/' + userId)
        .then(function(response) {
            return response.json();
        });
}

2. Prefer Const Over Let:

// ✅ Good - Use const by default
const API_URL = 'https://api.example.com';
const config = { timeout: 3000 };

// Only use let when reassignment is needed
let count = 0;
count++;

3. Use Descriptive Variable Names:

// ✅ Good
const userAuthenticationToken = generateToken();
const isUserLoggedIn = checkAuthStatus();
const maximumRetryAttempts = 3;

// ❌ Avoid
const t = generateToken();
const flag = checkAuthStatus();
const max = 3;

4. Leverage Array Methods:

// ✅ Good - Functional approach
const activeUsers = users
    .filter(user => user.isActive)
    .map(user => ({
        id: user.id,
        fullName: `${user.firstName} ${user.lastName}`
    }));

// ❌ Avoid - Imperative approach
const activeUsers = [];
for (let i = 0; i < users.length; i++) {
    if (users[i].isActive) {
        activeUsers.push({
            id: users[i].id,
            fullName: users[i].firstName + ' ' + users[i].lastName
        });
    }
}

5. Handle Errors Properly:

// ✅ Good - Proper error handling
async function fetchUserData(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        return await response.json();
    } catch (error) {
        console.error('Failed to fetch user:', error);
        // Handle error appropriately (show message, retry, etc.)
        throw error; // Re-throw if needed
    }
}

// ❌ Avoid - Silent failures
async function fetchUserData(userId) {
    const response = await fetch(`/api/users/${userId}`);
    return await response.json(); // No error handling!
}

6. Use Pure Functions:

// ✅ Good - Pure function (no side effects)
function calculateTotal(items, taxRate) {
    return items.reduce((total, item) => {
        return total + (item.price * (1 + taxRate));
    }, 0);
}

// ❌ Avoid - Impure function (modifies external state)
let total = 0;
function addToTotal(item, taxRate) {
    total += item.price * (1 + taxRate); // Side effect!
}

7. Avoid Deep Nesting:

// ✅ Good - Early returns, flat structure
function processOrder(order) {
    if (!order) {
        return null;
    }
    
    if (!order.items?.length) {
        return { error: 'No items in order' };
    }
    
    if (order.total < 0) {
        return { error: 'Invalid total' };
    }
    
    return calculateOrderTotal(order);
}

// ❌ Avoid - Pyramid of doom
function processOrder(order) {
    if (order) {
        if (order.items && order.items.length > 0) {
            if (order.total >= 0) {
                return calculateOrderTotal(order);
            } else {
                return { error: 'Invalid total' };
            }
        } else {
            return { error: 'No items in order' };
        }
    } else {
        return null;
    }
}

8. Use Object and Array Destructuring:

// ✅ Good
function displayUser({ name, email, age = 'Unknown' }) {
    console.log(`${name} (${email}) - Age: ${age}`);
}

const [first, second, ...rest] = items;

// ❌ Avoid
function displayUser(user) {
    console.log(user.name + ' (' + user.email + ') - Age: ' + 
               (user.age || 'Unknown'));
}

const first = items[0];
const second = items[1];

9. Modularize Code:

// ✅ Good - Separate concerns
// userService.js
export async function fetchUser(id) {
    const response = await fetch(`/api/users/${id}`);
    return await response.json();
}

// userValidator.js
export function isValidUser(user) {
    return user?.name && user?.email;
}

// userFormatter.js
export function formatUserName(user) {
    return `${user.firstName} ${user.lastName}`.trim();
}

10. Document Complex Code:

/**
 * Calculates the compound interest for an investment
 * @param {number} principal - Initial investment amount
 * @param {number} rate - Annual interest rate (as decimal, e.g., 0.05 for 5%)
 * @param {number} time - Investment period in years
 * @param {number} n - Number of times interest is compounded per year
 * @returns {number} Final amount after compound interest
 */
function calculateCompoundInterest(principal, rate, time, n = 12) {
    return principal * Math.pow(1 + rate / n, n * time);
}

Part 5: JavaScript Frameworks and Libraries – React, Vue, Angular, and Beyond

Introduction to JavaScript Frameworks

Modern web development relies heavily on frameworks that provide structure, reusability, and efficiency. In 2025-2026, the “Big Three” frameworks are React, Vue, and Angular, with React maintaining the dominant market position.

React – The Most Popular Framework

React is a JavaScript library for building user interfaces, created and maintained by Meta (Facebook).

Core React Concepts:

1. Components:

// Functional Component (modern standard)
import React from 'react';

function Welcome({ name }) {
    return <h1>Hello, {name}!</h1>;
}

// Component with hooks
import { useState, useEffect } from 'react';

function Counter() {
    const [count, setCount] = useState(0);
    
    useEffect(() => {
        document.title = `Count: ${count}`;
    }, [count]);
    
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}

2. JSX (JavaScript XML):

function UserCard({ user }) {
    const isOnline = user.status === 'online';
    
    return (
        <div className="user-card">
            <img src={user.avatar} alt={user.name} />
            <h2>{user.name}</h2>
            <p className={isOnline ? 'online' : 'offline'}>
                {isOnline ? '🟢 Online' : '⚫ Offline'}
            </p>
            {user.bio && <p>{user.bio}</p>}
        </div>
    );
}

3. State Management with useState:

import { useState } from 'react';

function TodoList() {
    const [todos, setTodos] = useState([]);
    const [input, setInput] = useState('');
    
    const addTodo = () => {
        if (input.trim()) {
            setTodos([...todos, {
                id: Date.now(),
                text: input,
                completed: false
            }]);
            setInput('');
        }
    };
    
    const toggleTodo = (id) => {
        setTodos(todos.map(todo =>
            todo.id === id
                ? { ...todo, completed: !todo.completed }
                : todo
        ));
    };
    
    const deleteTodo = (id) => {
        setTodos(todos.filter(todo => todo.id !== id));
    };
    
    return (
        <div>
            <input
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyPress={(e) => e.key === 'Enter' && addTodo()}
            />
            <button onClick={addTodo}>Add</button>
            
            <ul>
                {todos.map(todo => (
                    <li key={todo.id}>
                        <input
                            type="checkbox"
                            checked={todo.completed}
                            onChange={() => toggleTodo(todo.id)}
                        />
                        <span style={{
                            textDecoration: todo.completed ? 'line-through' : 'none'
                        }}>
                            {todo.text}
                        </span>
                        <button onClick={() => deleteTodo(todo.id)}>Delete</button>
                    </li>
                ))}
            </ul>
        </div>
    );
}

4. useEffect for Side Effects:

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    
    useEffect(() => {
        let cancelled = false;
        
        async function fetchUser() {
            try {
                setLoading(true);
                const response = await fetch(`/api/users/${userId}`);
                const data = await response.json();
                
                if (!cancelled) {
                    setUser(data);
                    setError(null);
                }
            } catch (err) {
                if (!cancelled) {
                    setError(err.message);
                }
            } finally {
                if (!cancelled) {
                    setLoading(false);
                }
            }
        }
        
        fetchUser();
        
        // Cleanup function
        return () => {
            cancelled = true;
        };
    }, [userId]); // Re-run when userId changes
    
    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error}</div>;
    if (!user) return <div>No user found</div>;
    
    return (
        <div>
            <h2>{user.name}</h2>
            <p>{user.email}</p>
        </div>
    );
}

5. Custom Hooks:

// useLocalStorage hook
function useLocalStorage(key, initialValue) {
    const [storedValue, setStoredValue] = useState(() => {
        try {
            const item = window.localStorage.getItem(key);
            return item ? JSON.parse(item) : initialValue;
        } catch (error) {
            console.error(error);
            return initialValue;
        }
    });
    
    const setValue = (value) => {
        try {
            setStoredValue(value);
            window.localStorage.setItem(key, JSON.stringify(value));
        } catch (error) {
            console.error(error);
        }
    };
    
    return [storedValue, setValue];
}

// Usage
function App() {
    const [name, setName] = useLocalStorage('name', 'Guest');
    
    return (
        <input
            value={name}
            onChange={(e) => setName(e.target.value)}
        />
    );
}

// useFetch hook
function useFetch(url) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    
    useEffect(() => {
        async function fetchData() {
            try {
                const response = await fetch(url);
                const json = await response.json();
                setData(json);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        }
        
        fetchData();
    }, [url]);
    
    return { data, loading, error };
}

6. Context API for State Management:

import { createContext, useContext, useState } from 'react';

// Create context
const ThemeContext = createContext();

// Provider component
function ThemeProvider({ children }) {
    const [theme, setTheme] = useState('light');
    
    const toggleTheme = () => {
        setTheme(prev => prev === 'light' ? 'dark' : 'light');
    };
    
    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    );
}

// Custom hook for using context
function useTheme() {
    const context = useContext(ThemeContext);
    if (!context) {
        throw new Error('useTheme must be used within ThemeProvider');
    }
    return context;
}

// Using the context
function App() {
    return (
        <ThemeProvider>
            <Header />
            <Content />
        </ThemeProvider>
    );
}

function Header() {
    const { theme, toggleTheme } = useTheme();
    
    return (
        <header className={theme}>
            <button onClick={toggleTheme}>
                Toggle Theme
            </button>
        </header>
    );
}

7. React Router for Navigation:

import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';

function App() {
    return (
        <BrowserRouter>
            <nav>
                <Link to="/">Home</Link>
                <Link to="/about">About</Link>
                <Link to="/users">Users</Link>
            </nav>
            
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/about" element={<About />} />
                <Route path="/users" element={<Users />} />
                <Route path="/users/:id" element={<UserDetail />} />
                <Route path="*" element={<NotFound />} />
            </Routes>
        </BrowserRouter>
    );
}

function UserDetail() {
    const { id } = useParams();
    return <div>User ID: {id}</div>;
}

Vue.js – The Progressive Framework

Vue is known for its gentle learning curve and excellent documentation.

Vue 3 Composition API:

<template>
    <div>
        <h1>{{ title }}</h1>
        <p>Count: {{ count }}</p>
        <button @click="increment">Increment</button>
        <button @click="decrement">Decrement</button>
    </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue';

// Reactive state
const count = ref(0);
const title = ref('Counter App');

// Computed properties
const doubleCount = computed(() => count.value * 2);

// Methods
function increment() {
    count.value++;
}

function decrement() {
    count.value--;
}

// Watchers
watch(count, (newValue, oldValue) => {
    console.log(`Count changed from ${oldValue} to ${newValue}`);
});

// Lifecycle hooks
onMounted(() => {
    console.log('Component mounted!');
});
</script>

<style scoped>
button {
    margin: 0 5px;
    padding: 10px 20px;
}
</style>

Vue Component Communication:

// Parent Component
<template>
    <div>
        <ChildComponent
            :message="parentMessage"
            @update="handleUpdate"
        />
    </div>
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const parentMessage = ref('Hello from parent');

function handleUpdate(data) {
    console.log('Received from child:', data);
}
</script>

// Child Component
<template>
    <div>
        <p>{{ message }}</p>
        <button @click="sendUpdate">Send to Parent</button>
    </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
    message: String
});

const emit = defineEmits(['update']);

function sendUpdate() {
    emit('update', 'Data from child');
}
</script>

Angular – The Complete Framework

Angular is a full-featured framework by Google, ideal for large enterprise applications.

Angular Component:

// user.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
    selector: 'app-user',
    templateUrl: './user.component.html',
    styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
    users: User[] = [];
    loading = true;
    
    constructor(private userService: UserService) {}
    
    ngOnInit(): void {
        this.loadUsers();
    }
    
    async loadUsers(): Promise<void> {
        try {
            this.loading = true;
            this.users = await this.userService.getUsers();
        } catch (error) {
            console.error('Error loading users:', error);
        } finally {
            this.loading = false;
        }
    }
    
    deleteUser(id: number): void {
        this.userService.deleteUser(id).subscribe({
            next: () => {
                this.users = this.users.filter(u => u.id !== id);
            },
            error: (error) => console.error(error)
        });
    }
}
<!-- user.component.html -->
<div class="user-container">
    <h2>Users</h2>
    
    <div *ngIf="loading" class="loading">
        Loading users...
    </div>
    
    <div *ngIf="!loading && users.length === 0">
        No users found.
    </div>
    
    <ul *ngIf="!loading && users.length > 0">
        <li *ngFor="let user of users">
            <span>{{ user.name }}</span>
            <button (click)="deleteUser(user.id)">Delete</button>
        </li>
    </ul>
</div>

State Management Libraries

Redux (React):

// store.js
import { createStore } from 'redux';

// Action types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const ADD_TODO = 'ADD_TODO';

// Action creators
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
export const addTodo = (text) => ({
    type: ADD_TODO,
    payload: { id: Date.now(), text, completed: false }
});

// Reducer
const initialState = {
    count: 0,
    todos: []
};

function rootReducer(state = initialState, action) {
    switch (action.type) {
        case INCREMENT:
            return { ...state, count: state.count + 1 };
        case DECREMENT:
            return { ...state, count: state.count - 1 };
        case ADD_TODO:
            return { ...state, todos: [...state.todos, action.payload] };
        default:
            return state;
    }
}

export const store = createStore(rootReducer);

// Component usage
import { useSelector, useDispatch } from 'react-redux';
import { increment, addTodo } from './store';

function Counter() {
    const count = useSelector(state => state.count);
    const dispatch = useDispatch();
    
    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => dispatch(increment())}>
                Increment
            </button>
        </div>
    );
}

Redux Toolkit (Modern Redux – Recommended 2025-2026):

// counterSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// Async thunk
export const fetchUsers = createAsyncThunk(
    'users/fetchUsers',
    async () => {
        const response = await fetch('/api/users');
        return await response.json();
    }
);

const counterSlice = createSlice({
    name: 'counter',
    initialState: {
        value: 0,
        users: [],
        loading: false
    },
    reducers: {
        increment: (state) => {
            state.value += 1;
        },
        decrement: (state) => {
            state.value -= 1;
        },
        incrementByAmount: (state, action) => {
            state.value += action.payload;
        }
    },
    extraReducers: (builder) => {
        builder
            .addCase(fetchUsers.pending, (state) => {
                state.loading = true;
            })
            .addCase(fetchUsers.fulfilled, (state, action) => {
                state.loading = false;
                state.users = action.payload;
            })
            .addCase(fetchUsers.rejected, (state) => {
                state.loading = false;
            });
    }
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
    reducer: {
        counter: counterReducer
    }
});

Zustand (Lightweight Alternative):

import create from 'zustand';

const useStore = create((set) => ({
    count: 0,
    todos: [],
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
    addTodo: (text) => set((state) => ({
        todos: [...state.todos, {
            id: Date.now(),
            text,
            completed: false
        }]
    })),
    toggleTodo: (id) => set((state) => ({
        todos: state.todos.map(todo =>
            todo.id === id ? { ...todo, completed: !todo.completed } : todo
        )
    }))
}));

// Usage
function Counter() {
    const { count, increment, decrement } = useStore();
    
    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>+</button>
            <button onClick={decrement}>-</button>
        </div>
    );
}

Popular JavaScript Libraries (2025-2026)

Utility Libraries:

  1. Lodash – Utility Functions:
import _ from 'lodash';

// Debouncing
const handleSearch = _.debounce((query) => {
    console.log('Searching:', query);
}, 300);

// Deep cloning
const original = { a: 1, b: { c: 2 } };
const clone = _.cloneDeep(original);

// Array operations
const numbers = [1, 2, 3, 4, 5];
const chunked = _.chunk(numbers, 2); // [[1, 2], [3, 4], [5]]
const unique = _.uniq([1, 2, 2, 3, 3, 3]); // [1, 2, 3]

// Object operations
const obj = { a: 1, b: 2, c: 3 };
const picked = _.pick(obj, ['a', 'c']); // { a: 1, c: 3 }
const omitted = _.omit(obj, ['b']); // { a: 1, c: 3 }
  1. Axios – HTTP Client:
import axios from 'axios';

// Basic request
const response = await axios.get('/api/users');
console.log(response.data);

// POST request
const newUser = await axios.post('/api/users', {
    name: 'John Doe',
    email: 'john@example.com'
});

// Interceptors
axios.interceptors.request.use(
    (config) => {
        config.headers.Authorization = `Bearer ${getToken()}`;
        return config;
    },
    (error) => Promise.reject(error)
);

axios.interceptors.response.use(
    (response) => response,
    (error) => {
        if (error.response?.status === 401) {
            // Handle unauthorized
        }
        return Promise.reject(error);
    }
);

// Create instance
const api = axios.create({
    baseURL: 'https://api.example.com',
    timeout: 5000,
    headers: { 'Content-Type': 'application/json' }
});
  1. Day.js – Date Manipulation:
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';

dayjs.extend(relativeTime);

// Formatting
const now = dayjs();
console.log(now.format('YYYY-MM-DD')); // "2025-10-04"
console.log(now.format('MMM DD, YYYY')); // "Oct 04, 2025"

// Manipulation
const tomorrow = dayjs().add(1, 'day');
const lastWeek = dayjs().subtract(1, 'week');

// Relative time
console.log(dayjs('2025-10-01').fromNow()); // "3 days ago"

// Comparison
const date1 = dayjs('2025-10-01');
const date2 = dayjs('2025-10-04');
console.log(date1.isBefore(date2)); // true

UI Component Libraries:

  1. Material-UI (MUI) for React:
import { Button, TextField, Card, CardContent } from '@mui/material';
import { ThemeProvider, createTheme } from '@mui/material/styles';

const theme = createTheme({
    palette: {
        primary: {
            main: '#1976d2'
        }
    }
});

function App() {
    return (
        <ThemeProvider theme={theme}>
            <Card>
                <CardContent>
                    <TextField
                        label="Username"
                        variant="outlined"
                        fullWidth
                    />
                    <Button variant="contained" color="primary">
                        Submit
                    </Button>
                </CardContent>
            </Card>
        </ThemeProvider>
    );
}
  1. Tailwind CSS (Utility-First CSS):
function Card({ title, description }) {
    return (
        <div className="max-w-sm rounded overflow-hidden shadow-lg bg-white">
            <div className="px-6 py-4">
                <h2 className="font-bold text-xl mb-2">{title}</h2>
                <p className="text-gray-700 text-base">{description}</p>
            </div>
            <div className="px-6 py-4">
                <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                    Learn More
                </button>
            </div>
        </div>
    );
}

Part 6: Backend JavaScript with Node.js – Server-Side Development

Introduction to Node.js

Node.js is a JavaScript runtime built on Chrome’s V8 engine, enabling JavaScript to run on servers.

Creating a Basic HTTP Server:

// server.js
const http = require('http');

const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html');
    res.end('<h1>Hello, World!</h1>');
});

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}/`);
});

Express.js – Web Application Framework

Express is the most popular Node.js framework for building web applications and APIs.

Basic Express Server:

const express = require('express');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.get('/', (req, res) => {
    res.send('Welcome to Express!');
});

app.get('/api/users', (req, res) => {
    res.json([
        { id: 1, name: 'John' },
        { id: 2, name: 'Sarah' }
    ]);
});

app.post('/api/users', (req, res) => {
    const newUser = req.body;
    // Process user data
    res.status(201).json(newUser);
});

// Error handling
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ error: 'Something went wrong!' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

RESTful API Example:

const express = require('express');
const router = express.Router();

// In-memory database (for demonstration)
let users = [
    { id: 1, name: 'John Doe', email: 'john@example.com' },
    { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];

// GET all users
router.get('/users', (req, res) => {
    res.json(users);
});

// GET single user
router.get('/users/:id', (req, res) => {
    const user = users.find(u => u.id === parseInt(req.params.id));
    if (!user) {
        return res.status(404).json({ error: 'User not found' });
    }
    res.json(user);
});

// CREATE user
router.post('/users', (req, res) => {
    const { name, email } = req.body;
    
    // Validation
    if (!name || !email) {
        return res.status(400).json({ error: 'Name and email are required' });
    }
    
    const newUser = {
        id: users.length + 1,
        name,
        email
    };
    
    users.push(newUser);
    res.status(201).json(newUser);
});

// UPDATE user
router.put('/users/:id', (req, res) => {
    const user = users.find(u => u.id === parseInt(req.params.id));
    
    if (!user) {
        return res.status(404).json({ error: 'User not found' });
    }
    
    const { name, email } = req.body;
    if (name) user.name = name;
    if (email) user.email = email;
    
    res.json(user);
});

// DELETE user
router.delete('/users/:id', (req, res) => {
    const index = users.findIndex(u => u.id === parseInt(req.params.id));
    
    if (index === -1) {
        return res.status(404).json({ error: 'User not found' });
    }
    
    users.splice(index, 1);
    res.status(204).send();
});

module.exports = router;

Middleware Examples:

// Logging middleware
const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
    next();
};

// Authentication middleware
const authenticate = (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];
    
    if (!token) {
        return res.status(401).json({ error: 'No token provided' });
    }
    
    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded;
        next();
    } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
    }
};

// Rate limiting
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100 // Limit each IP to 100 requests per windowMs
});

// CORS middleware
const cors = require('cors');

app.use(cors({
    origin: 'https://example.com',
    credentials: true
}));

// Using middleware
app.use(logger);
app.use('/api', limiter);
app.get('/protected', authenticate, (req, res) => {
    res.json({ message: 'Protected route', user: req.user });
});

Working with Databases

MongoDB with Mongoose:

const mongoose = require('mongoose');

// Connect to MongoDB
mongoose.connect('mongodb://localhost/myapp', {
    useNewUrlParser: true,
    useUnifiedTopology: true
});

// Define Schema
const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true,
        trim: true
    },
    email: {
        type: String,
        required: true,
        unique: true,
        lowercase: true
    },
    age: {
        type: Number,
        min: 0
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

// Create Model
const User = mongoose.model('User', userSchema);

// CRUD Operations
async function createUser(userData) {
    try {
        const user = new User(userData);
        await user.save();
        return user;
    } catch (error) {
        console.error('Error creating user:', error);
        throw error;
    }
}

async function findUsers(query = {}) {
    try {
        const users = await User.find(query);
        return users;
    } catch (error) {
        console.error('Error finding users:', error);
        throw error;
    }
}

async function updateUser(id, updates) {
    try {
        const user = await User.findByIdAndUpdate(
            id,
            updates,
            { new: true, runValidators: true }
        );
        return user;
    } catch (error) {
        console.error('Error updating user:', error);
        throw error;
    }
}

async function deleteUser(id) {
    try {
        await User.findByIdAndDelete(id);
    } catch (error) {
        console.error('Error deleting user:', error);
        throw error;
    }
}

PostgreSQL with Prisma (Modern ORM):

// schema.prisma
datasource db {
    provider = "postgresql"
    url      = env("DATABASE_URL")
}

generator client {
    provider = "prisma-client-js"
}

model User {
    id        Int      @id @default(autoincrement())
    name      String
    email     String   @unique
    posts     Post[]
    createdAt DateTime @default(now())
}

model Post {
    id        Int      @id @default(autoincrement())
    title     String
    content   String?
    published Boolean  @default(false)
    author    User     @relation(fields: [authorId], references: [id])
    authorId  Int
    createdAt DateTime @default(now())
}

// Usage
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

// Create user
async function createUser(name, email) {
    const user = await prisma.user.create({
        data: {
            name,
            email
        }
    });
    return user;
}

// Get users with posts
async function getUsersWithPosts() {
    const users = await prisma.user.findMany({
        include: {
            posts: true
        }
    });
    return users;
}

// Update user
async function updateUserEmail(id, newEmail) {
    const user = await prisma.user.update({
        where: { id },
        data: { email: newEmail }
    });
    return user;
}

// Delete user
async function deleteUser(id) {
    await prisma.user.delete({
        where: { id }
    });
}

Authentication and Security

JWT Authentication:

const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');

// Register user
async function register(req, res) {
    try {
        const { username, email, password } = req.body;
        
        // Hash password
        const hashedPassword = await bcrypt.hash(password, 10);
        
        // Create user
        const user = await User.create({
            username,
            email,
            password: hashedPassword
        });
        
        res.status(201).json({
            message: 'User registered successfully',
            userId: user.id
        });
    } catch (error) {
        res.status(500).json({ error: 'Registration failed' });
    }
}

// Login user
async function login(req, res) {
    try {
        const { email, password } = req.body;
        
        // Find user
        const user = await User.findOne({ email });
        if (!user) {
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        // Verify password
        const isValidPassword = await bcrypt.compare(password, user.password);
        if (!isValidPassword) {
            return res.status(401).json({ error: 'Invalid credentials' });
        }
        
        // Generate JWT
        const token = jwt.sign(
            { userId: user.id, email: user.email },
            process.env.JWT_SECRET,
            { expiresIn: '24h' }
        );
        
        res.json({
            token,
            user: {
                id: user.id,
                username: user.username,
                email: user.email
            }
        });
    } catch (error) {
        res.status(500).json({ error: 'Login failed' });
    }
}

// Verify token middleware
function verifyToken(req, res, next) {
    const token = req.headers.authorization?.split(' ')[1];
    
    if (!token) {
        return res.status(401).json({ error: 'No token provided' });
    }
    
    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded;
        next();
    } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
    }
}

Security Best Practices:

const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

const app = express();

// Helmet - Security headers
app.use(helmet());

// Rate limiting
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100
});
app.use('/api', limiter);

// Data sanitization against NoSQL injection
app.use(mongoSanitize());

// Data sanitization against XSS
app.use(xss());

// Prevent parameter pollution
const hpp = require('hpp');
app.use(hpp());

// Environment variables
require('dotenv').config();

// Validate input
const { body, validationResult } = require('express-validator');

app.post('/api/users',
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 8 }),
    (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        
        // Process request
    }
);

Part 7: Testing and Debugging – Writing Reliable, Maintainable Code

Unit Testing with Jest

Jest is the most popular JavaScript testing framework.

Basic Test Structure:

// math.js
function add(a, b) {
    return a + b;
}

function subtract(a, b) {
    return a - b;
}

module.exports = { add, subtract };

// math.test.js
const { add, subtract } = require('./math');

describe('Math operations', () => {
    test('adds 1 + 2 to equal 3', () => {
        expect(add(1, 2)).toBe(3);
    });
    
    test('subtracts 5 - 2 to equal 3', () => {
        expect(subtract(5, 2)).toBe(3);
    });
    
    test('handles negative numbers', () => {
        expect(add(-1, -2)).toBe(-3);
    });
});

Testing Asynchronous Code:

// api.js
async function fetchUser(id) {
    const response = await fetch(`/api/users/${id}`);
    return await response.json();
}

// api.test.js
describe('fetchUser', () => {
    test('fetches user data', async () => {
        const user = await fetchUser(1);
        expect(user).toHaveProperty('id');
        expect(user).toHaveProperty('name');
    });
    
    test('handles errors', async () => {
        await expect(fetchUser(999)).rejects.toThrow();
    });
});

Mocking:

// userService.test.js
const userService = require('./userService');
const database = require('./database');

// Mock the database module
jest.mock('./database');

describe('userService', () => {
    beforeEach(() => {
        jest.clearAllMocks();
    });
    
    test('gets user by id', async () => {
        const mockUser = { id: 1, name: 'John' };
        database.query.mockResolvedValue(mockUser);
        
        const user = await userService.getUserById(1);
        
        expect(database.query).toHaveBeenCalledWith('SELECT * FROM users WHERE id = ?', [1]);
        expect(user).toEqual(mockUser);
    });
});

Testing React Components:

import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

describe('Counter component', () => {
    test('renders initial count', () => {
        render(<Counter />);
        expect(screen.getByText(/count: 0/i)).toBeInTheDocument();
    });
    
    test('increments count on button click', () => {
        render(<Counter />);
        const button = screen.getByText(/increment/i);
        
        fireEvent.click(button);
        expect(screen.getByText(/count: 1/i)).toBeInTheDocument();
        
        fireEvent.click(button);
        expect(screen.getByText(/count: 2/i)).toBeInTheDocument();
    });
});

Integration and End-to-End Testing

Supertest for API Testing:

const request = require('supertest');
const app = require('../app');

describe('User API', () => {
    test('GET /api/users returns users', async () => {
        const response = await request(app)
            .get('/api/users')
            .expect('Content-Type', /json/)
            .expect(200);
        
        expect(Array.isArray(response.body)).toBe(true);
    });
    
    test('POST /api/users creates user', async () => {
        const newUser = {
            name: 'Test User',
            email: 'test@example.com'
        };
        
        const response = await request(app)
            .post('/api/users')
            .send(newUser)
            .expect(201);
        
        expect(response.body).toHaveProperty('id');
        expect(response.body.name).toBe(newUser.name);
    });
});

Cypress for E2E Testing:

// cypress/integration/login.spec.js
describe('Login Flow', () => {
    beforeEach(() => {
        cy.visit('/login');
    });
    
    it('should login successfully with valid credentials', () => {
        cy.get('input[name="email"]').type('user@example.com');
        cy.get('input[name="password"]').type('password123');
        cy.get('button[type="submit"]').click();
        
        cy.url().should('include', '/dashboard');
        cy.contains('Welcome back!').should('be.visible');
    });
    
    it('should show error with invalid credentials', () => {
        cy.get('input[name="email"]').type('wrong@example.com');
        cy.get('input[name="password"]').type('wrongpass');
        cy.get('button[type="submit"]').click();
        
        cy.contains('Invalid credentials').should('be.visible');
    });
});

Debugging Techniques

Console Methods:

// Basic logging
console.log('Simple message');
console.info('Information');
console.warn('Warning message');
console.error('Error message');

// Grouped logs
console.group('User Details');
console.log('Name: John');
console.log('Age: 30');
console.groupEnd();

// Table format
const users = [
    { id: 1, name: 'John', age: 30 },
    { id: 2, name: 'Sarah', age: 25 }
];
console.table(users);

// Timing
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData');

// Assertions
console.assert(user.age >= 18, 'User must be 18 or older');

// Stack trace
console.trace('Trace point');

Chrome DevTools:

// Debugger statement
function processData(data) {
    debugger; // Execution pauses here when DevTools open
    const result = data.map(item => item * 2);
    return result;
}

// Conditional breakpoints (set in DevTools)
// Break when specific condition is true
// Example: break when i === 5 in a loop

Error Handling Best Practices:

// Custom error classes
class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = 'ValidationError';
        this.statusCode = 400;
    }
}

class NotFoundError extends Error {
    constructor(message) {
        super(message);
        this.name = 'NotFoundError';
        this.statusCode = 404;
    }
}

// Using custom errors
function validateUser(user) {
    if (!user.email) {
        throw new ValidationError('Email is required');
    }
    if (!user.email.includes('@')) {
        throw new ValidationError('Invalid email format');
    }
}

// Global error handler (Express)
app.use((err, req, res, next) => {
    console.error(err.stack);
    
    const statusCode = err.statusCode || 500;
    const message = err.message || 'Internal Server Error';
    
    res.status(statusCode).json({
        error: {
            message,
            ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
        }
    });
});

Part 8: JavaScript Design Patterns – Professional Code Architecture

Creational Patterns

1. Singleton Pattern:

class Database {
    constructor() {
        if (Database.instance) {
            return Database.instance;
        }
        
        this.connection = null;
        Database.instance = this;
    }
    
    connect() {
        if (!this.connection) {
            this.connection = 'Connected to database';
            console.log(this.connection);
        }
        return this.connection;
    }
}

// Usage
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // true (same instance)

2. Factory Pattern:

class Car {
    constructor(options) {
        this.doors = options.doors || 4;
        this.state = options.state || 'brand new';
        this.color = options.color || 'silver';
    }
}

class Truck {
    constructor(options) {
        this.doors = options.doors || 4;
        this.state = options.state || 'used';
        this.wheelSize = options.wheelSize || 'large';
    }
}

class VehicleFactory {
    createVehicle(type, options) {
        switch (type) {
            case 'car':
                return new Car(options);
            case 'truck':
                return new Truck(options);
            default:
                throw new Error('Unknown vehicle type');
        }
    }
}

// Usage
const factory = new VehicleFactory();
const myCar = factory.createVehicle('car', { color: 'red', doors: 2 });
const myTruck = factory.createVehicle('truck', { wheelSize: 'huge' });

3. Builder Pattern:

class UserBuilder {
    constructor(firstName, lastName) {
        this.user = {
            firstName,
            lastName
        };
    }
    
    setEmail(email) {
        this.user.email = email;
        return this;
    }
    
    setAge(age) {
        this.user.age = age;
        return this;
    }
    
    setAddress(address) {
        this.user.address = address;
        return this;
    }
    
    setPhone(phone) {
        this.user.phone = phone;
        return this;
    }
    
    build() {
        return this.user;
    }
}

// Usage - Method chaining
const user = new UserBuilder('John', 'Doe')
    .setEmail('john@example.com')
    .setAge(30)
    .setAddress('123 Main St')
    .setPhone('555-1234')
    .build();

Structural Patterns

1. Module Pattern:

const ShoppingCart = (function() {
    // Private variables
    let items = [];
    let total = 0;
    
    // Private methods
    function calculateTotal() {
        total = items.reduce((sum, item) => sum + item.price, 0);
    }
    
    // Public API
    return {
        addItem(item) {
            items.push(item);
            calculateTotal();
        },
        
        removeItem(itemId) {
            items = items.filter(item => item.id !== itemId);
            calculateTotal();
        },
        
        getItems() {
            return [...items]; // Return copy
        },
        
        getTotal() {
            return total;
        },
        
        clear() {
            items = [];
            total = 0;
        }
    };
})();

// Usage
ShoppingCart.addItem({ id: 1, name: 'Laptop', price: 999 });
ShoppingCart.addItem({ id: 2, name: 'Mouse', price: 29 });
console.log(ShoppingCart.getTotal()); // 1028

2. Decorator Pattern:

class Coffee {
    cost() {
        return 5;
    }
    
    description() {
        return 'Simple coffee';
    }
}

class MilkDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }
    
    cost() {
        return this.coffee.cost() + 2;
    }
    
    description() {
        return this.coffee.description() + ', milk';
    }
}

class SugarDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }
    
    cost() {
        return this.coffee.cost() + 1;
    }
    
    description() {
        return this.coffee.description() + ', sugar';
    }
}

// Usage
let myCoffee = new Coffee();
myCoffee = new MilkDecorator(myCoffee);
myCoffee = new SugarDecorator(myCoffee);

console.log(myCoffee.description()); // "Simple coffee, milk, sugar"
console.log(myCoffee.cost()); // 8

3. Adapter Pattern:

// Old interface
class OldCalculator {
    constructor() {
        this.operations = function(value1, value2, operation) {
            switch (operation) {
                case 'add':
                    return value1 + value2;
                case 'subtract':
                    return value1 - value2;
                default:
                    return NaN;
            }
        };
    }
}

// New interface
class NewCalculator {
    constructor() {
        this.add = function(value1, value2) {
            return value1 + value2;
        };
        this.subtract = function(value1, value2) {
            return value1 - value2;
        };
    }
}

// Adapter
class CalculatorAdapter {
    constructor() {
        const newCalculator = new NewCalculator();
        
        this.operations = function(value1, value2, operation) {
            switch (operation) {
                case 'add':
                    return newCalculator.add(value1, value2);
                case 'subtract':
                    return newCalculator.subtract(value1, value2);
                default:
                    return NaN;
            }
        };
    }
}

// Usage - same interface, new implementation
const adapter = new CalculatorAdapter();
console.log(adapter.operations(10, 5, 'add')); // 15

Behavioral Patterns

1. Observer Pattern:

class EventEmitter {
    constructor() {
        this.events = {};
    }
    
    on(event, listener) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
    }
    
    emit(event, ...args) {
        if (this.events[event]) {
            this.events[event].forEach(listener => {
                listener(...args);
            });
        }
    }
    
    off(event, listenerToRemove) {
        if (this.events[event]) {
            this.events[event] = this.events[event].filter(
                listener => listener !== listenerToRemove
            );
        }
    }
}

// Usage
const emitter = new EventEmitter();

const logUser = (user) => console.log('User logged in:', user);
const sendEmail = (user) => console.log('Sending email to:', user);

emitter.on('userLoggedIn', logUser);
emitter.on('userLoggedIn', sendEmail);

emitter.emit('userLoggedIn', 'john@example.com');
// Output:
// User logged in: john@example.com
// Sending email to: john@example.com

2. Strategy Pattern:

// Different strategies
class CreditCardStrategy {
    pay(amount) {
        console.log(`Paid ${amount} using Credit Card`);
    }
}

class PayPalStrategy {
    pay(amount) {
        console.log(`Paid ${amount} using PayPal`);
    }
}

class CryptoStrategy {
    pay(amount) {
        console.log(`Paid ${amount} using Cryptocurrency`);
    }
}

// Context
class ShoppingCart {
    constructor(paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    setPaymentStrategy(paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    checkout(amount) {
        this.paymentStrategy.pay(amount);
    }
}

// Usage
const cart = new ShoppingCart(new CreditCardStrategy());
cart.checkout(100); // Paid 100 using Credit Card

cart.setPaymentStrategy(new PayPalStrategy());
cart.checkout(200); // Paid 200 using PayPal

3. Command Pattern:

class Command {
    constructor(execute, undo) {
        this.execute = execute;
        this.undo = undo;
    }
}

class Calculator {
    constructor() {
        this.value = 0;
        this.history = [];
    }
    
    executeCommand(command) {
        this.value = command.execute(this.value);
        this.history.push(command);
    }
    
    undo() {
        const command = this.history.pop();
        if (command) {
            this.value = command.undo(this.value);
        }
    }
    
    getValue() {
        return this.value;
    }
}

// Commands
function add(value) {
    return new Command(
        currentValue => currentValue + value,
        currentValue => currentValue - value
    );
}

function subtract(value) {
    return new Command(
        currentValue => currentValue - value,
        currentValue => currentValue + value
    );
}

// Usage
const calculator = new Calculator();
calculator.executeCommand(add(10));
console.log(calculator.getValue()); // 10

calculator.executeCommand(subtract(3));
console.log(calculator.getValue()); // 7

calculator.undo();
console.log(calculator.getValue()); // 10

Part 9: Performance Optimization – Building Fast, Efficient Applications

JavaScript Performance Fundamentals

1. Minimize DOM Manipulation:

// ❌ Bad - Multiple DOM updates
function renderList(items) {
    const list = document.getElementById('list');
    items.forEach(item => {
        const li = document.createElement('li');
        li.textContent = item;
        list.appendChild(li); // Reflow on each iteration
    });
}

// ✅ Good - Single DOM update
function renderList(items) {
    const list = document.getElementById('list');
    const fragment = document.createDocumentFragment();
    
    items.forEach(item => {
        const li = document.createElement('li');
        li.textContent = item;
        fragment.appendChild(li);
    });
    
    list.appendChild(fragment); // Single reflow
}

// ✅ Better - Template strings
function renderList(items) {
    const list = document.getElementById('list');
    list.innerHTML = items.map(item => `<li>${item}</li>`).join('');
}

2. Debouncing and Throttling:

// Debounce - Execute after delay with no new calls
function debounce(func, delay) {
    let timeoutId;
    
    return function(...args) {
        clearTimeout(timeoutId);
        
        timeoutId = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
}

// Usage - Search input
const searchInput = document.getElementById('search');
const handleSearch = debounce((query) => {
    console.log('Searching for:', query);
    // Make API call
}, 300);

searchInput.addEventListener('input', (e) => {
    handleSearch(e.target.value);
});

// Throttle - Execute at most once per interval
function throttle(func, limit) {
    let inThrottle;
    
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

// Usage - Scroll handler
const handleScroll = throttle(() => {
    console.log('Scroll position:', window.scrollY);
}, 100);

window.addEventListener('scroll', handleScroll);

3. Lazy Loading:

// Image lazy loading
const images = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            img.removeAttribute('data-src');
            observer.unobserve(img);
        }
    });
});

images.forEach(img => imageObserver.observe(img));

// Module lazy loading (dynamic imports)
async function loadModule() {
    const button = document.getElementById('loadBtn');
    
    button.addEventListener('click', async () => {
        const module = await import('./heavyModule.js');
        module.initialize();
    });
}

// React lazy loading
import React, { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
    return (
        <Suspense fallback={<div>Loading...</div>}>
            <HeavyComponent />
        </Suspense>
    );
}

4. Memoization:

// Simple memoization
function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        
        if (cache.has(key)) {
            console.log('Returning cached result');
            return cache.get(key);
        }
        
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

// Usage - Expensive calculation
const fibonacci = memoize(function(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
});

console.log(fibonacci(40)); // Much faster with memoization

// React useMemo and useCallback
import { useMemo, useCallback } from 'react';

function ExpensiveComponent({ data, onUpdate }) {
    // Memoize expensive calculation
    const processedData = useMemo(() => {
        console.log('Processing data...');
        return data.map(item => item * 2).filter(item => item > 10);
    }, [data]); // Only recalculate when data changes
    
    // Memoize callback function
    const handleClick = useCallback(() => {
        onUpdate(processedData);
    }, [processedData, onUpdate]);
    
    return (
        <div>
            <button onClick={handleClick}>Update</button>
            <ul>
                {processedData.map(item => <li key={item}>{item}</li>)}
            </ul>
        </div>
    );
}

5. Web Workers for Heavy Computation:

// worker.js
self.addEventListener('message', (e) => {
    const { data } = e;
    
    // Perform heavy computation
    let result = 0;
    for (let i = 0; i < data.iterations; i++) {
        result += Math.sqrt(i);
    }
    
    self.postMessage({ result });
});

// main.js
const worker = new Worker('worker.js');

worker.addEventListener('message', (e) => {
    console.log('Result from worker:', e.data.result);
});

worker.postMessage({ iterations: 1000000 });

// Keep UI responsive while worker computes
document.getElementById('btn').addEventListener('click', () => {
    console.log('UI still responsive!');
});

6. Code Splitting:

// Webpack code splitting
// Dynamic imports create separate bundles

// Route-based code splitting
const routes = [
    {
        path: '/home',
        component: () => import('./pages/Home')
    },
    {
        path: '/about',
        component: () => import('./pages/About')
    },
    {
        path: '/products',
        component: () => import('./pages/Products')
    }
];

// Vendor code splitting (webpack.config.js)
module.exports = {
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    priority: 10
                },
                common: {
                    minChunks: 2,
                    priority: 5,
                    reuseExistingChunk: true
                }
            }
        }
    }
};

7. Efficient Data Structures:

// Use Map for frequent lookups
// ❌ Bad - Array with find
const users = [/* thousands of users */];
const user = users.find(u => u.id === targetId); // O(n)

// ✅ Good - Map
const usersMap = new Map();
users.forEach(user => usersMap.set(user.id, user));
const user = usersMap.get(targetId); // O(1)

// Use Set for unique values
// ❌ Bad - Array includes
const uniqueValues = [];
if (!uniqueValues.includes(newValue)) {
    uniqueValues.push(newValue);
}

// ✅ Good - Set
const uniqueValues = new Set();
uniqueValues.add(newValue); // Automatically handles duplicates

Memory Management

1. Avoid Memory Leaks:

// ❌ Bad - Event listener not removed
function attachListener() {
    const button = document.getElementById('btn');
    button.addEventListener('click', handleClick);
}

// ✅ Good - Clean up event listeners
function attachListener() {
    const button = document.getElementById('btn');
    const handler = () => console.log('Clicked');
    
    button.addEventListener('click', handler);
    
    // Return cleanup function
    return () => {
        button.removeEventListener('click', handler);
    };
}

const cleanup = attachListener();
// Later...
cleanup();

// React cleanup
useEffect(() => {
    const subscription = api.subscribe(data => {
        setData(data);
    });
    
    // Cleanup function
    return () => {
        subscription.unsubscribe();
    };
}, []);

2. WeakMap and WeakSet:

// WeakMap - Keys are weakly referenced (garbage collected)
const privateData = new WeakMap();

class User {
    constructor(name) {
        this.name = name;
        privateData.set(this, { password: 'secret123' });
    }
    
    getPassword() {
        return privateData.get(this).password;
    }
}

let user = new User('John');
console.log(user.getPassword()); // 'secret123'
user = null; // Private data automatically garbage collected

Performance Monitoring

1. Performance API:

// Measure execution time
performance.mark('start');

// Some operations
for (let i = 0; i < 1000000; i++) {
    Math.sqrt(i);
}

performance.mark('end');
performance.measure('Operation Time', 'start', 'end');

const measure = performance.getEntriesByName('Operation Time')[0];
console.log(`Duration: ${measure.duration}ms`);

// Navigation timing
const perfData = performance.getEntriesByType('navigation')[0];
console.log('DOM Content Loaded:', perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart);
console.log('Page Load:', perfData.loadEventEnd - perfData.loadEventStart);

2. Lighthouse and DevTools:

// Use Chrome DevTools Performance tab
// 1. Open DevTools (F12)
// 2. Go to Performance tab
// 3. Record interaction
// 4. Analyze flame chart, bottlenecks, and long tasks

// Programmatic Lighthouse
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

async function runLighthouse(url) {
    const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
    const options = {
        port: chrome.port,
        output: 'json'
    };
    
    const result = await lighthouse(url, options);
    const scores = result.lhr.categories;
    
    console.log('Performance:', scores.performance.score * 100);
    console.log('Accessibility:', scores.accessibility.score * 100);
    console.log('Best Practices:', scores['best-practices'].score * 100);
    console.log('SEO:', scores.seo.score * 100);
    
    await chrome.kill();
}

Part 10: Real-World Projects – Portfolio-Ready Applications

Project 1: Task Management Application (Full-Stack)

Frontend (React):

// TaskApp.jsx
import { useState, useEffect } from 'react';
import axios from 'axios';

function TaskApp() {
    const [tasks, setTasks] = useState([]);
    const [newTask, setNewTask] = useState('');
    const [filter, setFilter] = useState('all');
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        fetchTasks();
    }, []);

    const fetchTasks = async () => {
        try {
            setLoading(true);
            const response = await axios.get('/api/tasks');
            setTasks(response.data);
        } catch (error) {
            console.error('Error fetching tasks:', error);
        } finally {
            setLoading(false);
        }
    };

    const addTask = async () => {
        if (!newTask.trim()) return;
        
        try {
            const response = await axios.post('/api/tasks', {
                title: newTask,
                completed: false
            });
            setTasks([...tasks, response.data]);
            setNewTask('');
        } catch (error) {
            console.error('Error adding task:', error);
        }
    };

    const toggleTask = async (id) => {
        const task = tasks.find(t => t.id === id);
        try {
            const response = await axios.put(`/api/tasks/${id}`, {
                ...task,
                completed: !task.completed
            });
            setTasks(tasks.map(t => t.id === id ? response.data : t));
        } catch (error) {
            console.error('Error updating task:', error);
        }
    };

    const deleteTask = async (id) => {
        try {
            await axios.delete(`/api/tasks/${id}`);
            setTasks(tasks.filter(t => t.id !== id));
        } catch (error) {
            console.error('Error deleting task:', error);
        }
    };

    const filteredTasks = tasks.filter(task => {
        if (filter === 'active') return !task.completed;
        if (filter === 'completed') return task.completed;
        return true;
    });

    return (
        <div className="task-app">
            <h1>Task Manager</h1>
            
            <div className="add-task">
                <input
                    type="text"
                    value={newTask}
                    onChange={(e) => setNewTask(e.target.value)}
                    onKeyPress={(e) => e.key === 'Enter' && addTask()}
                    placeholder="Add new task..."
                />
                <button onClick={addTask}>Add Task</button>
            </div>

            <div className="filters">
                <button onClick={() => setFilter('all')} 
                        className={filter === 'all' ? 'active' : ''}>
                    All
                </button>
                <button onClick={() => setFilter('active')}
                        className={filter === 'active' ? 'active' : ''}>
                    Active
                </button>
                <button onClick={() => setFilter('completed')}
                        className={filter === 'completed' ? 'active' : ''}>
                    Completed
                </button>
            </div>

            {loading ? (
                <div>Loading...</div>
            ) : (
                <ul className="task-list">
                    {filteredTasks.map(task => (
                        <li key={task.id} className={task.completed ? 'completed' : ''}>
                            <input
                                type="checkbox"
                                checked={task.completed}
                                onChange={() => toggleTask(task.id)}
                            />
                            <span>{task.title}</span>
                            <button onClick={() => deleteTask(task.id)}>Delete</button>
                        </li>
                    ))}
                </ul>
            )}
            
            <div className="stats">
                {tasks.filter(t => !t.completed).length} tasks remaining
            </div>
        </div>
    );
}

export default TaskApp;

Backend (Express + MongoDB):

// server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();

const app = express();

// Middleware
app.use(cors());
app.use(express.json());

// MongoDB Connection
mongoose.connect(process.env.MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true
});

// Task Schema
const taskSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    completed: {
        type: Boolean,
        default: false
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

const Task = mongoose.model('Task', taskSchema);

// Routes
// Get all tasks
app.get('/api/tasks', async (req, res) => {
    try {
        const tasks = await Task.find().sort({ createdAt: -1 });
        res.json(tasks);
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

// Create task
app.post('/api/tasks', async (req, res) => {
    try {
        const task = new Task({
            title: req.body.title,
            completed: req.body.completed || false
        });
        await task.save();
        res.status(201).json(task);
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

// Update task
app.put('/api/tasks/:id', async (req, res) => {
    try {
        const task = await Task.findByIdAndUpdate(
            req.params.id,
            req.body,
            { new: true, runValidators: true }
        );
        if (!task) {
            return res.status(404).json({ error: 'Task not found' });
        }
        res.json(task);
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

// Delete task
app.delete('/api/tasks/:id', async (req, res) => {
    try {
        const task = await Task.findByIdAndDelete(req.params.id);
        if (!task) {
            return res.status(404).json({ error: 'Task not found' });
        }
        res.status(204).send();
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Project 2: Weather Dashboard (API Integration)

// WeatherDashboard.jsx
import { useState, useEffect } from 'react';
import axios from 'axios';

function WeatherDashboard() {
    const [city, setCity] = useState('London');
    const [weather, setWeather] = useState(null);
    const [forecast, setForecast] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    const API_KEY = process.env.REACT_APP_WEATHER_API_KEY;

    useEffect(() => {
        fetchWeather();
    }, []);

    const fetchWeather = async (searchCity = city) => {
        try {
            setLoading(true);
            setError(null);

            const [currentWeather, forecastData] = await Promise.all([
                axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${searchCity}&appid=${API_KEY}&units=metric`),
                axios.get(`https://api.openweathermap.org/data/2.5/forecast?q=${searchCity}&appid=${API_KEY}&units=metric`)
            ]);

            setWeather(currentWeather.data);
            
            // Get forecast for next 5 days (one per day)
            const dailyForecast = forecastData.data.list.filter((item, index) => index % 8 === 0).slice(0, 5);
            setForecast(dailyForecast);
            
        } catch (err) {
            setError('City not found or API error');
            console.error(err);
        } finally {
            setLoading(false);
        }
    };

    const handleSearch = (e) => {
        e.preventDefault();
        fetchWeather();
    };

    return (
        <div className="weather-dashboard">
            <h1>Weather Dashboard</h1>

            <form onSubmit={handleSearch}>
                <input
                    type="text"
                    value={city}
                    onChange={(e) => setCity(e.target.value)}
                    placeholder="Enter city name"
                />
                <button type="submit">Search</button>
            </form>

            {loading && <div className="loading">Loading...</div>}
            {error && <div className="error">{error}</div>}

            {weather && !loading && (
                <div className="current-weather">
                    <h2>{weather.name}, {weather.sys.country}</h2>
                    <div className="temperature">{Math.round(weather.main.temp)}°C</div>
                    <div className="description">{weather.weather[0].description}</div>
                    <div className="details">
                        <div>Feels like: {Math.round(weather.main.feels_like)}°C</div>
                        <div>Humidity: {weather.main.humidity}%</div>
                        <div>Wind: {weather.wind.speed} m/s</div>
                    </div>
                </div>
            )}

            {forecast.length > 0 && (
                <div className="forecast">
                    <h3>5-Day Forecast</h3>
                    <div className="forecast-grid">
                        {forecast.map((day, index) => (
                            <div key={index} className="forecast-item">
                                <div>{new Date(day.dt * 1000).toLocaleDateString('en-US', { weekday: 'short' })}</div>
                                <div>{Math.round(day.main.temp)}°C</div>
                                <div>{day.weather[0].description}</div>
                            </div>
                        ))}
                    </div>
                </div>
            )}
        </div>
    );
}

export default WeatherDashboard;