# JavaScript Intermediate Guide ## Table of Contents - [Introduction](#introduction) - [ES6+ Modern Features](#es6-modern-features) - [Let and Const](#let-and-const) - [Arrow Functions](#arrow-functions) - [Template Literals](#template-literals) - [Destructuring](#destructuring) - [Spread and Rest Operators](#spread-and-rest-operators) - [Modules (Import/Export)](#modules-importexport) - [Asynchronous JavaScript](#asynchronous-javascript) - [Callbacks](#callbacks) - [Promises](#promises) - [Async/Await](#asyncawait) - [Error Handling in Async Code](#error-handling-in-async-code) - [DOM Manipulation](#dom-manipulation) - [Selecting Elements](#selecting-elements) - [Modifying Elements](#modifying-elements) - [Event Handling](#event-handling) - [Creating and Removing Elements](#creating-and-removing-elements) - [Error Handling](#error-handling) - [Try/Catch](#trycatch) - [Throwing Errors](#throwing-errors) - [Custom Errors](#custom-errors) - [Functional Programming](#functional-programming) - [Pure Functions](#pure-functions) - [Higher-Order Functions](#higher-order-functions) - [Immutability](#immutability) - [Array Methods in Depth](#array-methods-in-depth) - [Working with APIs](#working-with-apis) - [Fetch API](#fetch-api) - [JSON](#json) - [REST API Concepts](#rest-api-concepts) - [Practice Projects](#practice-projects) --- ## Introduction This guide builds on the concepts from the Beginner guides. We'll cover modern JavaScript (ES6+), asynchronous programming, DOM manipulation, error handling, and functional programming concepts. By the end, you'll be ready to build real-world applications. --- ## ES6+ Modern Features ECMAScript 2015 (ES6) and subsequent versions introduced many features that make JavaScript more powerful and expressive. ### Let and Const We've covered these in the beginner guide, but let's dive deeper: ```javascript // let - block scoped, can be reassigned let count = 0; count = 1; // OK // const - block scoped, cannot be reassigned const PI = 3.14159; // PI = 3; // Error! // const with objects - the reference is constant, not the contents const user = { name: "Alice" }; user.name = "Bob"; // OK - modifying property // user = {}; // Error - reassigning // const with arrays const numbers = [1, 2, 3]; numbers.push(4); // OK // numbers = []; // Error // Temporal Dead Zone (TDZ) { // console.log(x); // ReferenceError! let x = 5; console.log(x); // 5 } ``` ### Arrow Functions Arrow functions are more than just shorter syntax - they have different behavior with `this`: ```javascript // Basic arrow function const add = (a, b) => a + b; // Single parameter - no parentheses needed const square = x => x * x; // No parameters const getRandom = () => Math.random(); // Multiple statements need braces and return const calculate = (a, b) => { const sum = a + b; const product = a * b; return { sum, product }; }; // Arrow functions and 'this' function Timer() { this.seconds = 0; // Regular function - 'this' refers to the Timer instance setInterval(function() { this.seconds++; console.log(this.seconds); }, 1000); // Arrow function - 'this' is lexically bound setInterval(() => { this.seconds++; console.log(this.seconds); }, 1000); } // Arrow functions as methods (be careful!) const person = { name: "Alice", // Arrow function here loses 'this' context greet: () => { console.log("Hello, " + this.name); // 'this' is NOT person! }, // Regular method introduce() { console.log("I am " + this.name); // Works correctly } }; ``` ### Template Literals Template literals provide powerful string formatting: ```javascript // Basic interpolation const name = "Alice"; const greeting = `Hello, ${name}!`; console.log(greeting); // "Hello, Alice!" // Expressions work too const a = 5, b = 3; console.log(`${a} + ${b} = ${a + b}`); // "5 + 3 = 8" // Multi-line strings const poem = `Roses are red, Violets are blue, Sugar is sweet, And so are you.`; console.log(poem); // Tagged templates function highlight(strings, ...values) { return strings.reduce((result, string, i) => { return result + string + (values[i] ? `${values[i]}` : ''); }, ''); } const name = "Alice"; const result = highlight`Hello, ${name}! Welcome back.`; console.log(result); // "Hello, Alice! Welcome back." ``` ### Destructuring Destructuring allows you to unpack values from arrays or properties from objects: ```javascript // Array destructuring const colors = ["red", "green", "blue"]; const [first, second, third] = colors; console.log(first); // "red" // Skip elements const [primary, , tertiary] = colors; console.log(primary, tertiary); // "red", "blue" // Rest pattern const [head, ...tail] = colors; console.log(head); // "red" console.log(tail); // ["green", "blue"] // Default values const [a, b, c, d = "default"] = [1, 2, 3]; console.log(d); // "default" // Swap variables let x = 1, y = 2; [x, y] = [y, x]; console.log(x, y); // 2, 1 // Object destructuring const person = { name: "Alice", age: 25, city: "NYC" }; const { name, age } = person; console.log(name, age); // "Alice", 25 // Rename variables const { name: personName, age: personAge } = person; console.log(personName); // "Alice" // Default values const { name, country = "USA" } = person; console.log(country); // "USA" // Nested destructuring const data = { user: { profile: { email: "alice@example.com" } } }; const { user: { profile: { email } } } = data; console.log(email); // "alice@example.com" // In function parameters function greet({ name, age }) { return `Hello, ${name}! You are ${age}.`; } console.log(greet({ name: "Alice", age: 25 })); // "Hello, Alice! You are 25." ``` ### Spread and Rest Operators The spread operator (`...`) expands iterables, while rest collects multiple elements: ```javascript // Spread with arrays const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combined = [...arr1, ...arr2]; console.log(combined); // [1, 2, 3, 4, 5, 6] // Copy array const copy = [...arr1]; console.log(copy); // [1, 2, 3] // Spread with objects const obj1 = { a: 1, b: 2 }; const obj2 = { b: 3, c: 4 }; const merged = { ...obj1, ...obj2 }; console.log(merged); // { a: 1, b: 3, c: 4 } (b is overwritten) // Copy object const clone = { ...obj1 }; // In function calls const nums = [1, 2, 3, 4, 5]; console.log(Math.max(...nums)); // 5 // Rest parameters function sum(...numbers) { return numbers.reduce((a, b) => a + b, 0); } console.log(sum(1, 2, 3, 4, 5)); // 15 // Destructuring with rest const [first, ...rest] = [1, 2, 3, 4, 5]; console.log(first); // 1 console.log(rest); // [2, 3, 4, 5] // Object destructuring with rest const { name, ...others } = { name: "Alice", age: 25, city: "NYC" }; console.log(name); // "Alice" console.log(others); // { age: 25, city: "NYC" } ``` ### Modules (Import/Export) Modules allow you to organize code into separate files: ```javascript // Named exports (math.js) export const add = (a, b) => a + b; export const subtract = (a, b) => a - b; export const multiply = (a, b) => a * b; // Default export (utils.js) export default function formatCurrency(value) { return `$${value.toFixed(2)}`; } // Importing named exports import { add, subtract } from './math.js'; console.log(add(2, 3)); // 5 // Import with alias import { add as sum } from './math.js'; console.log(sum(2, 3)); // 5 // Import all as namespace import * as MathUtils from './math.js'; console.log(MathUtils.add(2, 3)); // 5 // Import default export import formatCurrency from './utils.js'; console.log(formatCurrency(99.9)); // "$99.90" // Combined import import formatCurrency, { add } from './both.js'; ``` --- ## Asynchronous JavaScript JavaScript is single-threaded but can handle asynchronous operations through the event loop. ### Callbacks Callbacks are functions passed as arguments to other functions: ```javascript // Synchronous callback function processArray(arr, callback) { const results = []; for (const item of arr) { results.push(callback(item)); } return results; } const numbers = [1, 2, 3]; const doubled = processArray(numbers, x => x * 2); console.log(doubled); // [2, 4, 6] // Asynchronous callback (old pattern) function fetchData(callback) { setTimeout(() => { callback(null, { name: "Alice", age: 25 }); }, 1000); } fetchData((error, data) => { if (error) { console.error("Error:", error); } else { console.log("Data:", data); } }); // Callback hell (avoid!) getData(function(a) { getMoreData(a, function(b) { getEvenMoreData(b, function(c) { console.log(c); }); }); }); ``` ### Promises Promises provide a cleaner way to handle asynchronous operations: ```javascript // Creating a promise function fetchUser(id) { return new Promise((resolve, reject) => { setTimeout(() => { if (id > 0) { resolve({ id, name: "Alice" }); } else { reject(new Error("Invalid user ID")); } }, 1000); }); } // Using promises fetchUser(1) .then(user => { console.log("User:", user); return fetchUser(2); }) .then(user2 => { console.log("User 2:", user2); }) .catch(error => { console.error("Error:", error.message); }) .finally(() => { console.log("Operation complete"); }); // Promise.all - wait for all promises const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = Promise.resolve(3); Promise.all([promise1, promise2, promise3]) .then(values => console.log(values)); // [1, 2, 3] // Promise.race - first one to resolve Promise.race([ new Promise(resolve => setTimeout(resolve, 100, "fast")), new Promise(resolve => setTimeout(resolve, 200, "slow")) ]) .then(value => console.log(value)); // "fast" // Promise.allSettled - wait for all to settle Promise.allSettled([ Promise.resolve(1), Promise.reject("error"), Promise.resolve(3) ]) .then(results => console.log(results)); // [{status: "fulfilled", value: 1}, {status: "rejected", reason: "error"}, ...] ``` ### Async/Await Async/await provides synchronous-looking code for asynchronous operations: ```javascript // Async function async function getUser(id) { return { id, name: "Alice" }; } // Using async/await async function main() { try { const user = await getUser(1); console.log("User:", user); } catch (error) { console.error("Error:", error); } } main(); // Await with multiple promises async function getData() { const [users, posts] = await Promise.all([ fetch('/api/users').then(r => r.json()), fetch('/api/posts').then(r => r.json()) ]); return { users, posts }; } // Sequential vs parallel async function sequential() { const result1 = await fetch('/api/item1'); const result2 = await fetch('/api/item2'); // Takes 2x as long } async function parallel() { const [result1, result2] = await Promise.all([ fetch('/api/item1'), fetch('/api/item2') ]); // Takes same time as slowest request } ``` ### Error Handling in Async Code ```javascript // Try/Catch with async/await async function fetchData(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error("Fetch failed:", error); throw error; // Re-throw for caller to handle } } // Handling multiple errors async function processAll(items) { const results = []; for (const item of items) { try { const result = await processItem(item); results.push({ success: true, data: result }); } catch (error) { results.push({ success: false, error: error.message }); } } return results; } // Error boundaries pattern async function safeAsync(fn, defaultValue) { try { return await fn(); } catch (error) { console.error(error); return defaultValue; } } const data = await safeAsync(() => fetchData('/api/data'), []); ``` --- ## DOM Manipulation The Document Object Model (DOM) represents your HTML as objects that JavaScript can manipulate. ### Selecting Elements ```javascript // Single elements const element = document.getElementById('myId'); const firstDiv = document.querySelector('div'); const elementByClass = document.querySelector('.myClass'); // Multiple elements (returns NodeList - array-like) const allDivs = document.querySelectorAll('div'); const allItems = document.getElementsByClassName('item'); // NodeList methods allDivs.forEach(div => console.log(div)); // Convert to real array const arrayFromNodes = Array.from(allDivs); // Check relationships const parent = element.parentElement; const children = element.children; const nextSibling = element.nextElementSibling; const prevSibling = element.previousElementSibling; ``` ### Modifying Elements ```javascript // Text content element.textContent = "New text"; element.innerText = "Visible text"; // HTML content element.innerHTML = "Bold text"; // Attributes element.setAttribute('data-id', '123'); const id = element.getAttribute('data-id'); element.removeAttribute('data-id'); // CSS styles element.style.color = 'blue'; element.style.backgroundColor = '#f0f0f0'; element.style.display = 'none'; // Classes element.classList.add('active'); element.classList.remove('hidden'); element.classList.toggle('selected'); const hasClass = element.classList.contains('active'); // Dataset (data-* attributes) element.dataset.userId = '123'; console.log(element.dataset.userId); ``` ### Event Handling ```javascript // Add event listener element.addEventListener('click', function(event) { console.log('Clicked!', event.target); }); // Arrow function element.addEventListener('click', (event) => { console.log(event.clientX, event.clientY); }); // Remove event listener (needs named function) function handleClick(event) { console.log('Clicked!'); } element.addEventListener('click', handleClick); element.removeEventListener('click', handleClick); // Event delegation document.querySelector('ul').addEventListener('click', (event) => { if (event.target.tagName === 'LI') { console.log('List item clicked:', event.target.textContent); } }); // Common events element.addEventListener('click'); // Mouse click element.addEventListener('dblclick'); // Double click element.addEventListener('mouseenter'); // Mouse over element.addEventListener('mouseleave'); // Mouse out element.addEventListener('mouseover'); // Mouse enters (bubbles) element.addEventListener('mouseout'); // Mouse leaves (bubbles) input.addEventListener('input'); // Value changes input.addEventListener('change'); // Value changes (on blur) input.addEventListener('focus'); // Gets focus input.addEventListener('blur'); // Loses focus document.addEventListener('keydown'); // Key pressed document.addEventListener('keyup'); // Key released form.addEventListener('submit'); // Form submitted ``` ### Creating and Removing Elements ```javascript // Create new element const newDiv = document.createElement('div'); newDiv.textContent = "Hello!"; newDiv.classList.add('greeting'); // Add to DOM parent.appendChild(newDiv); // At the end parent.insertBefore(newDiv, parent.firstChild); // At specific position // Modern insertion methods parent.append(newDiv); // Multiple items parent.prepend(newDiv); // At beginning const reference = parent.querySelector('.some-element'); reference.before(newDiv); // Before reference reference.after(newDiv); // After reference // Remove element newDiv.remove(); // Modern method parent.removeChild(newDiv); // Old method // Clone element const clone = element.cloneNode(true); // Deep clone (with children) const shallowClone = element.cloneNode(false); // Just the element ``` --- ## Error Handling Proper error handling makes your code more robust and easier to debug. ### Try/Catch ```javascript // Basic try/catch try { const result = riskyOperation(); console.log(result); } catch (error) { console.error("Something went wrong:", error.message); } // Try/Catch/Finally try { const data = JSON.parse(userInput); processData(data); } catch (error) { console.error("Failed to parse:", error.message); } finally { console.log("Cleanup here"); // Always runs } // Catching specific error types try { // Code that might throw different errors } catch (error) { if (error instanceof TypeError) { console.log("Type error:", error.message); } else if (error instanceof RangeError) { console.log("Range error:", error.message); } else { throw error; // Re-throw unknown errors } } ``` ### Throwing Errors ```javascript // Throw built-in errors throw new Error("Something went wrong"); throw new TypeError("Expected a string"); throw new RangeError("Value out of range"); throw new SyntaxError("Invalid syntax"); throw new ReferenceError("Variable not defined"); // Custom error class class ValidationError extends Error { constructor(message, field) { super(message); this.name = "ValidationError"; this.field = field; } } function validateAge(age) { if (typeof age !== "number") { throw new TypeError("Age must be a number"); } if (age < 0 || age > 150) { throw new ValidationError("Age must be between 0 and 150", "age"); } return true; } ``` ### Custom Errors ```javascript class AppError extends Error { constructor(message, code, details = {}) { super(message); this.name = this.constructor.name; this.code = code; this.details = details; Error.captureStackTrace(this, this.constructor); } } class NotFoundError extends AppError { constructor(resource) { super(`${resource} not found`, "NOT_FOUND", { resource }); } } class UnauthorizedError extends AppError { constructor() { super("You must be logged in", "UNAUTHORIZED"); } } // Usage function getUser(id) { const user = database.find(id); if (!user) { throw new NotFoundError("User"); } return user; } ``` --- ## Functional Programming Functional programming is a paradigm that treats computation as the evaluation of mathematical functions. ### Pure Functions ```javascript // Pure function - same input always gives same output, no side effects function add(a, b) { return a + b; } // Impure function - side effects let total = 0; function addToTotal(value) { total += value; // Modifies external state return total; } // Impure function - different output for same input function getRandom() { return Math.random(); } // Pure version of impure functions function addToTotalPure(total, value) { return total + value; } const newTotal = addToTotalPure(0, 5); // Always returns 5 function getRandomPure(seed) { // Deterministic pseudo-random const x = Math.sin(seed++) * 10000; return x - Math.floor(x); } ``` ### Higher-Order Functions ```javascript // Function that returns a function function multiplier(factor) { return function(number) { return number * factor; }; } const double = multiplier(2); const triple = multiplier(3); console.log(double(5)); // 10 console.log(triple(5)); // 15 // Function that takes a function function applyOperation(arr, operation) { return arr.map(operation); } console.log(applyOperation([1, 2, 3], x => x * 2)); // [2, 4, 6] ``` ### Immutability ```javascript // Mutable (avoid) const arr = [1, 2, 3]; arr.push(4); // Mutates original // Immutable (preferred) const arr = [1, 2, 3]; const newArr = [...arr, 4]; // Creates new array // Updating object const obj = { a: 1, b: 2 }; const newObj = { ...obj, b: 3 }; // Override b const newObj2 = { ...obj, c: 3 }; // Add c // Deep clone for nested objects const deepClone = JSON.parse(JSON.stringify(original)); // Or use a library like Lodash // import _ from 'lodash'; // const clone = _.cloneDeep(original); // Immer library for complex immutable updates // import { produce } from 'immer'; // const nextState = produce(state, draft => { // draft.users[0].name = 'Alice'; // }); ``` ### Array Methods in Depth ```javascript const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // map - transform each element const doubled = numbers.map(n => n * 2); // filter - keep elements that pass test const evens = numbers.filter(n => n % 2 === 0); // reduce - combine into single value const sum = numbers.reduce((acc, n) => acc + n, 0); // find - get first matching element const firstEven = numbers.find(n => n % 2 === 0); // findIndex - get index of first match const firstEvenIndex = numbers.findIndex(n => n % 2 === 0); // some - check if any element passes const hasEven = numbers.some(n => n % 2 === 0); // every - check if all elements pass const allPositive = numbers.every(n => n > 0); // flatMap - map then flatten const words = ["hello", "world"]; const chars = words.flatMap(word => word.split("")); // ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"] // Chaining const result = numbers .filter(n => n > 3) .map(n => n * 2) .reduce((a, b) => a + b, 0); ``` --- ## Working with APIs ### Fetch API ```javascript // Basic GET request fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); // Async/await version async function getData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (error) { console.error('Error:', error); } } // POST request async function createUser(userData) { const response = await fetch('https://api.example.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }); if (!response.ok) { throw new Error('Failed to create user'); } return response.json(); } // Using async/await const user = await createUser({ name: 'Alice', email: 'alice@example.com' }); ``` ### JSON ```javascript // Parse JSON string to JavaScript object const jsonString = '{"name": "Alice", "age": 25}'; const obj = JSON.parse(jsonString); // Convert JavaScript object to JSON string const json = JSON.stringify(obj, null, 2); // Pretty print // JSON with reviver function const data = JSON.parse(jsonString, (key, value) => { if (key === 'age') { return value * 2; // Transform value } return value; }); // JSON with replacer const json = JSON.stringify(obj, ['name', 'age'], 2); // Only includes name and age properties ``` ### REST API Concepts ```javascript // RESTful API methods const api = { // GET - retrieve data async get(url) { const response = await fetch(url); return response.json(); }, // POST - create new resource async post(url, data) { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); }, // PUT - update entire resource async put(url, data) { const response = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); }, // PATCH - partial update async patch(url, data) { const response = await fetch(url, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); }, // DELETE - remove resource async delete(url) { await fetch(url, { method: 'DELETE' }); } }; // Using the API const users = await api.get('/api/users'); const newUser = await api.post('/api/users', { name: 'Alice' }); const updatedUser = await api.patch('/api/users/1', { name: 'Bob' }); await api.delete('/api/users/1'); ``` --- ## Practice Projects ### Project 1: Todo List Application ```javascript // Simple todo list with localStorage class TodoList { constructor() { this.todos = JSON.parse(localStorage.getItem('todos')) || []; } add(todo) { this.todos.push({ id: Date.now(), text: todo, completed: false, createdAt: new Date().toISOString() }); this.save(); } toggle(id) { const todo = this.todos.find(t => t.id === id); if (todo) { todo.completed = !todo.completed; this.save(); } } delete(id) { this.todos = this.todos.filter(t => t.id !== id); this.save(); } save() { localStorage.setItem('todos', JSON.stringify(this.todos)); } } // Usage const todoList = new TodoList(); todoList.add("Learn JavaScript"); todoList.add("Build a project"); ``` ### Project 2: Weather App Skeleton ```javascript async function getWeather(city) { const apiKey = 'YOUR_API_KEY'; const url = `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}`; try { const response = await fetch(url); if (!response.ok) { throw new Error('Weather data not available'); } const data = await response.json(); return { location: data.location.name, temperature: data.current.temp_c, condition: data.current.condition.text, humidity: data.current.humidity }; } catch (error) { console.error('Error fetching weather:', error); return null; } } ``` --- ## Summary | Topic | Key Concepts | |-------|--------------| | **ES6+ Features** | Arrow functions, destructuring, spread, modules | | **Async JavaScript** | Promises, async/await, fetch API | | **DOM Manipulation** | Select, modify, create elements, events | | **Error Handling** | Try/catch, throwing errors, custom errors | | **Functional Programming** | Pure functions, immutability, array methods | | **APIs** | REST concepts, fetch, JSON | --- ## Next Steps - Move to the [Advanced Guide](JavaScript-Advanced-Guide.md) for deeper topics - Practice building projects with the concepts learned - Explore [DOM Manipulation](JavaScript-DOM-Manipulation.md) in detail - Learn about [Testing](JavaScript-Testing.md) your code --- ## Quick Reference ```javascript // Arrow functions const add = (a, b) => a + b; // Destructuring const { name, age } = person; const [first, ...rest] = arr; // Spread const newArr = [...arr1, ...arr2]; const newObj = { ...obj1, ...obj2 }; // Async/await async function fetchData() { try { const data = await fetch(url); return await data.json(); } catch (error) { console.error(error); } } // DOM document.querySelector('.class'); element.addEventListener('click', handler); element.textContent = 'new text'; ```