Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

JavaScript

A versatile, high-level programming language that enables dynamic, interactive web content and full-stack application development.

JavaScript Cheatsheet

A versatile, high-level programming language that enables dynamic, interactive web content and full-stack application development.

Overview

JavaScript is the backbone of modern web development, running in browsers and on servers via Node.js. It supports multiple programming paradigms including object-oriented, functional, and event-driven programming.

Core ConceptsVariablesFunctionsObjects/ArraysAsync/PromisesDOM/EventsJavaScript Runtime EnvironmentEvent LoopEvent LoopJavaScript EngineCall StackMemory HeapCallback QueueMicrotask QueueWeb APIsCore ConceptsVariablesFunctionsObjects/ArraysAsync/PromisesDOM/EventsJavaScript Runtime EnvironmentEvent LoopEvent LoopJavaScript EngineCall StackMemory HeapCallback QueueMicrotask QueueWeb APIs

Variables and Data Types

Key Concepts

  • let: Block-scoped variable that can be reassigned
  • const: Block-scoped constant that cannot be reassigned (but objects/arrays can be mutated)
  • var: Function-scoped variable (legacy, avoid in modern code)
  • Hoisting: Variables declared with var are hoisted; let and const have temporal dead zone
  • Primitive types: string, number, boolean, null, undefined, symbol, bigint
  • Reference types: objects, arrays, functions

Common Patterns

// Variable declarations
let counter = 0;              // Can be reassigned
const MAX_SIZE = 100;         // Cannot be reassigned
var legacyVar = 'avoid';      // Function-scoped (legacy)

// Data types
const str = 'Hello';          // String
const num = 42;               // Number
const bool = true;            // Boolean
const nothing = null;         // Null
const notDefined = undefined; // Undefined
const sym = Symbol('id');     // Symbol
const big = 9007199254740991n; // BigInt

// Type checking
typeof str;        // 'string'
typeof num;        // 'number'
typeof bool;       // 'boolean'
typeof nothing;    // 'object' (known quirk)
typeof notDefined; // 'undefined'
Array.isArray([1, 2, 3]); // true

// Type coercion
String(123);       // '123'
Number('42');      // 42
Boolean(0);        // false
parseInt('42px');  // 42
parseFloat('3.14'); // 3.14

Examples

// Block scoping demonstration
function scopeDemo() {
    var funcScoped = 'I am function scoped';

    if (true) {
        let blockScoped = 'I am block scoped';
        const alsoBlock = 'Me too';
        var stillFunc = 'Still function scoped';

        console.log(blockScoped); // Works
    }

    console.log(stillFunc);  // Works
    // console.log(blockScoped); // ReferenceError
}

// Temporal dead zone
console.log(x); // undefined (hoisted)
// console.log(y); // ReferenceError (TDZ)
var x = 5;
let y = 10;

// Const with objects (mutation allowed)
const user = { name: 'Alice' };
user.name = 'Bob';  // Allowed
user.age = 30;      // Allowed
// user = {};       // TypeError - reassignment not allowed

Functions

Key Concepts

  • Function declaration: Hoisted, can be called before definition
  • Function expression: Not hoisted, assigned to a variable
  • Arrow functions: Concise syntax, lexically bound this
  • Callbacks: Functions passed as arguments to other functions
  • Higher-order functions: Functions that take or return other functions
  • Closures: Functions that remember their lexical scope

Common Patterns

// Function declaration
function greet(name) {
    return `Hello, ${name}!`;
}

// Function expression
const greetExpr = function(name) {
    return `Hello, ${name}!`;
};

// Arrow function
const greetArrow = (name) => `Hello, ${name}!`;

// Multiple parameters
const add = (a, b) => a + b;

// No parameters
const getRandom = () => Math.random();

// Multiple statements
const calculate = (a, b) => {
    const sum = a + b;
    const product = a * b;
    return { sum, product };
};

// Default parameters
const greetWithDefault = (name = 'World') => `Hello, ${name}!`;

// Rest parameters
const sum = (...numbers) => numbers.reduce((acc, n) => acc + n, 0);

// Callback example
const fetchData = (callback) => {
    setTimeout(() => {
        callback('Data loaded');
    }, 1000);
};

// IIFE (Immediately Invoked Function Expression)
(function() {
    console.log('Executed immediately');
})();

Examples

// Closure example
function createCounter() {
    let count = 0;
    return {
        increment: () => ++count,
        decrement: () => --count,
        getCount: () => count
    };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2

// Arrow functions and 'this'
const obj = {
    name: 'Object',
    regularFunc: function() {
        console.log(this.name); // 'Object'
    },
    arrowFunc: () => {
        console.log(this.name); // undefined (lexical this)
    },
    methodWithCallback: function() {
        // Arrow keeps 'this' context
        setTimeout(() => {
            console.log(this.name); // 'Object'
        }, 100);
    }
};

// Higher-order function
const withLogging = (fn) => {
    return (...args) => {
        console.log(`Calling with args: ${args}`);
        const result = fn(...args);
        console.log(`Result: ${result}`);
        return result;
    };
};

const loggedAdd = withLogging(add);
loggedAdd(2, 3); // Logs: "Calling with args: 2,3" then "Result: 5"

Objects and Arrays

Key Concepts

  • Object literals: Key-value pairs enclosed in braces
  • Destructuring: Extract values from objects/arrays into variables
  • Spread operator: Expand iterables into individual elements
  • Rest operator: Collect remaining elements into an array
  • Object methods: Object.keys(), Object.values(), Object.entries()
  • Array methods: Chainable methods for data transformation

Common Patterns

// Object creation
const user = {
    name: 'Alice',
    age: 30,
    email: 'alice@example.com'
};

// Shorthand property names
const name = 'Bob';
const age = 25;
const person = { name, age }; // { name: 'Bob', age: 25 }

// Computed property names
const key = 'dynamicKey';
const obj = { [key]: 'value' };

// Object destructuring
const { name: userName, age: userAge } = user;

// Nested destructuring
const data = { user: { details: { city: 'London' } } };
const { user: { details: { city } } } = data;

// Default values in destructuring
const { country = 'UK' } = user;

// Array destructuring
const colours = ['red', 'green', 'blue'];
const [first, second, third] = colours;
const [primary, ...rest] = colours; // rest = ['green', 'blue']

// Spread operator
const newUser = { ...user, role: 'admin' };
const allColours = [...colours, 'yellow'];

// Object methods
Object.keys(user);    // ['name', 'age', 'email']
Object.values(user);  // ['Alice', 30, 'alice@example.com']
Object.entries(user); // [['name', 'Alice'], ['age', 30], ...]
Object.assign({}, user, { role: 'admin' });

// Optional chaining
const city = user?.address?.city; // undefined (no error)

// Nullish coalescing
const value = null ?? 'default'; // 'default'
const zero = 0 ?? 'default';     // 0

Examples

// Deep cloning
const original = { a: 1, b: { c: 2 } };
const clone = structuredClone(original);
// Legacy method (loses Dates, Maps, undefined, functions):
// JSON.parse(JSON.stringify(original))

// Merging objects
const defaults = { theme: 'light', language: 'en' };
const userPrefs = { theme: 'dark' };
const settings = { ...defaults, ...userPrefs };
// { theme: 'dark', language: 'en' }

// Array of objects manipulation
const users = [
    { id: 1, name: 'Alice', active: true },
    { id: 2, name: 'Bob', active: false },
    { id: 3, name: 'Charlie', active: true }
];

// Find by property
const bob = users.find(u => u.name === 'Bob');

// Filter active users
const activeUsers = users.filter(u => u.active);

// Extract names
const names = users.map(u => u.name);

// Group by property (ES2024+)
const grouped = Object.groupBy(users, u => u.active ? 'active' : 'inactive');

// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a]; // a = 2, b = 1

// Function parameter destructuring
const printUser = ({ name, age = 0 }) => {
    console.log(`${name} is ${age} years old`);
};
printUser(user);

DOM Manipulation

Key Concepts

  • Document Object Model (DOM): Tree representation of HTML
  • Selectors: Methods to find elements in the DOM
  • Element creation: Programmatically building DOM nodes
  • Attributes and properties: Managing element data
  • Traversal: Navigating the DOM tree

Common Patterns

// Selecting elements
const element = document.getElementById('myId');
const single = document.querySelector('.class');
const multiple = document.querySelectorAll('div.item');

// Creating elements
const div = document.createElement('div');
div.textContent = 'Hello World';
div.innerHTML = '<span>With HTML</span>';

// Setting attributes
div.id = 'myDiv';
div.className = 'container active';
div.classList.add('highlight');
div.classList.remove('active');
div.classList.toggle('visible');
div.setAttribute('data-id', '123');

// Getting attributes
const dataId = div.getAttribute('data-id');
const hasClass = div.classList.contains('container');

// Styles
div.style.backgroundColor = 'blue';
div.style.cssText = 'color: white; padding: 10px;';

// Inserting elements
document.body.appendChild(div);
parent.insertBefore(newElement, referenceElement);
element.append(child1, child2, 'text');
element.prepend(child);
element.before(sibling);
element.after(sibling);

// Removing elements
element.remove();
parent.removeChild(child);

// Cloning
const clone = element.cloneNode(true); // true = deep clone

// DOM traversal
element.parentElement;
element.children;
element.firstElementChild;
element.lastElementChild;
element.nextElementSibling;
element.previousElementSibling;
element.closest('.ancestor');

Examples

// Building a list dynamically
const items = ['Apple', 'Banana', 'Cherry'];
const ul = document.createElement('ul');

items.forEach(item => {
    const li = document.createElement('li');
    li.textContent = item;
    ul.appendChild(li);
});

document.body.appendChild(ul);

// Using DocumentFragment for performance
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
    const div = document.createElement('div');
    div.textContent = `Item ${i}`;
    fragment.appendChild(div);
}
document.body.appendChild(fragment);

// Template literals for complex HTML
const createCard = (user) => {
    const card = document.createElement('div');
    card.className = 'card';
    card.innerHTML = `
        <h2>${user.name}</h2>
        <p>${user.email}</p>
        <button data-id="${user.id}">View Profile</button>
    `;
    return card;
};

// Replacing content
const container = document.querySelector('#container');
container.replaceChildren(); // Clear all children
container.replaceChildren(...newElements); // Replace with new

Event Handling and Propagation

Key Concepts

  • Event listeners: Functions that respond to user interactions
  • Event bubbling: Events propagate up from target to ancestors
  • Event capturing: Events propagate down from ancestors to target
  • Event delegation: Handle events on parent for dynamic children
  • Event object: Contains information about the event
Event Propagation Phases1. Capture1. Capture1. Capture2. Target3. Bubble3. Bubble3. BubbleWindowDocumentParent ElementTarget ElementEvent Propagation Phases1. Capture1. Capture1. Capture2. Target3. Bubble3. Bubble3. BubbleWindowDocumentParent ElementTarget Element

Common Patterns

// Adding event listeners
element.addEventListener('click', (event) => {
    console.log('Clicked!', event.target);
});

// Removing event listeners
const handler = (event) => console.log(event);
element.addEventListener('click', handler);
element.removeEventListener('click', handler);

// Event options
element.addEventListener('click', handler, {
    once: true,      // Remove after first trigger
    passive: true,   // Won't call preventDefault()
    capture: true    // Use capture phase
});

// Common events
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mouseenter', handler);
element.addEventListener('mouseleave', handler);
element.addEventListener('keydown', handler);
element.addEventListener('keyup', handler);
element.addEventListener('submit', handler);
element.addEventListener('change', handler);
element.addEventListener('input', handler);
element.addEventListener('focus', handler);
element.addEventListener('blur', handler);
element.addEventListener('scroll', handler);
element.addEventListener('load', handler);

// Prevent default behaviour
form.addEventListener('submit', (e) => {
    e.preventDefault();
    // Handle form manually
});

// Stop propagation
element.addEventListener('click', (e) => {
    e.stopPropagation(); // Stop bubbling
});

// Event object properties
element.addEventListener('click', (e) => {
    e.target;        // Element that triggered event
    e.currentTarget; // Element with listener attached
    e.type;          // Event type ('click')
    e.timeStamp;     // When event occurred
    e.clientX;       // Mouse X coordinate
    e.clientY;       // Mouse Y coordinate
    e.key;           // Key pressed (for keyboard events)
});

Examples

// Event delegation
const list = document.querySelector('#todoList');

list.addEventListener('click', (e) => {
    if (e.target.matches('.delete-btn')) {
        const item = e.target.closest('li');
        item.remove();
    }

    if (e.target.matches('.toggle-btn')) {
        e.target.closest('li').classList.toggle('completed');
    }
});

// Debouncing scroll/resize events
const debounce = (fn, delay) => {
    let timeoutId;
    return (...args) => {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn(...args), delay);
    };
};

window.addEventListener('scroll', debounce(() => {
    console.log('Scrolled!');
}, 250));

// Throttling for rate limiting
const throttle = (fn, limit) => {
    let inThrottle;
    return (...args) => {
        if (!inThrottle) {
            fn(...args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
};

// Custom events
const customEvent = new CustomEvent('userLoggedIn', {
    detail: { userId: 123, username: 'alice' }
});

document.addEventListener('userLoggedIn', (e) => {
    console.log('User logged in:', e.detail);
});

document.dispatchEvent(customEvent);

// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
    if (e.ctrlKey && e.key === 's') {
        e.preventDefault();
        saveDocument();
    }
});

Promises and Async Patterns

Key Concepts

  • Promise: Object representing eventual completion/failure of async operation
  • Promise states: pending, fulfilled, rejected
  • then/catch/finally: Methods for handling promise results
  • async/await: Syntactic sugar for working with promises
  • Promise combinators: all, allSettled, race, any
resolve(value)reject(error).then(onFulfilled).catch(onRejected)PendingFulfilledRejectedInitial stateOperation succeededOperation failedresolve(value)reject(error).then(onFulfilled).catch(onRejected)PendingFulfilledRejectedInitial stateOperation succeededOperation failed

Common Patterns

// Creating a promise
const promise = new Promise((resolve, reject) => {
    // Async operation
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('Operation successful');
        } else {
            reject(new Error('Operation failed'));
        }
    }, 1000);
});

// Consuming promises
promise
    .then(result => {
        console.log(result);
        return 'Next value';
    })
    .then(nextResult => {
        console.log(nextResult);
    })
    .catch(error => {
        console.error(error.message);
    })
    .finally(() => {
        console.log('Cleanup');
    });

// Async/await syntax
async function fetchData() {
    try {
        const result = await promise;
        console.log(result);
        return result;
    } catch (error) {
        console.error(error.message);
        throw error;
    } finally {
        console.log('Cleanup');
    }
}

// Promise.all - all must succeed
const results = await Promise.all([
    fetch('/api/users'),
    fetch('/api/posts'),
    fetch('/api/comments')
]);

// Promise.allSettled - get all results regardless
const outcomes = await Promise.allSettled([
    fetch('/api/users'),
    fetch('/api/posts')
]);
outcomes.forEach(outcome => {
    if (outcome.status === 'fulfilled') {
        console.log(outcome.value);
    } else {
        console.log(outcome.reason);
    }
});

// Promise.race - first to settle
const fastest = await Promise.race([
    fetch('/api/primary'),
    fetch('/api/backup')
]);

// Promise.any - first to succeed
const firstSuccess = await Promise.any([
    fetch('/api/server1'),
    fetch('/api/server2')
]);

Examples

// Sequential async operations
async function processItems(items) {
    const results = [];
    for (const item of items) {
        const result = await processItem(item);
        results.push(result);
    }
    return results;
}

// Parallel async operations
async function processItemsParallel(items) {
    const promises = items.map(item => processItem(item));
    return Promise.all(promises);
}

// Retry pattern
async function fetchWithRetry(url, retries = 3, delay = 1000) {
    for (let i = 0; i < retries; i++) {
        try {
            return await fetch(url);
        } catch (error) {
            if (i === retries - 1) throw error;
            await new Promise(r => setTimeout(r, delay * Math.pow(2, i)));
        }
    }
}

// Timeout pattern
function withTimeout(promise, ms) {
    const timeout = new Promise((_, reject) => {
        setTimeout(() => reject(new Error('Timeout')), ms);
    });
    return Promise.race([promise, timeout]);
}

const result = await withTimeout(fetch('/api/slow'), 5000);

// Promisify callback-based function
const readFileAsync = (path) => {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf8', (err, data) => {
            if (err) reject(err);
            else resolve(data);
        });
    });
};

// Async iteration
async function* asyncGenerator() {
    for (let i = 0; i < 5; i++) {
        await new Promise(r => setTimeout(r, 100));
        yield i;
    }
}

for await (const value of asyncGenerator()) {
    console.log(value);
}

Fetch API

Key Concepts

  • Fetch: Modern API for making HTTP requests
  • Request/Response: Objects representing HTTP request/response
  • Headers: HTTP headers management
  • AbortController: Cancel ongoing requests
  • CORS: Cross-Origin Resource Sharing considerations

Common Patterns

// Basic GET request
const response = await fetch('https://api.example.com/data');
const data = await response.json();

// POST request
const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token123'
    },
    body: JSON.stringify({
        name: 'Alice',
        email: 'alice@example.com'
    })
});

// Other HTTP methods
const response = await fetch(url, { method: 'PUT', body, headers });
const response = await fetch(url, { method: 'PATCH', body, headers });
const response = await fetch(url, { method: 'DELETE' });

// Check response status
const response = await fetch(url);
if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
}

// Response methods
const json = await response.json();     // Parse JSON
const text = await response.text();     // Get text
const blob = await response.blob();     // Get binary data
const buffer = await response.arrayBuffer();
const formData = await response.formData();

// Headers
const response = await fetch(url);
response.headers.get('Content-Type');
response.headers.has('Authorization');

// FormData upload
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('name', 'document');

const response = await fetch('/upload', {
    method: 'POST',
    body: formData  // Content-Type set automatically
});

// URL search params
const params = new URLSearchParams({
    page: 1,
    limit: 10,
    search: 'query'
});
const response = await fetch(`/api/items?${params}`);

// Abort request
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);

const response = await fetch(url, {
    signal: controller.signal
});

Examples

// Comprehensive fetch wrapper
async function apiRequest(endpoint, options = {}) {
    const baseUrl = 'https://api.example.com';
    const defaultHeaders = {
        'Content-Type': 'application/json'
    };

    const config = {
        ...options,
        headers: {
            ...defaultHeaders,
            ...options.headers
        }
    };

    if (config.body && typeof config.body === 'object') {
        config.body = JSON.stringify(config.body);
    }

    try {
        const response = await fetch(`${baseUrl}${endpoint}`, config);

        if (!response.ok) {
            const error = await response.json().catch(() => ({}));
            throw new Error(error.message || `HTTP ${response.status}`);
        }

        const contentType = response.headers.get('Content-Type');
        if (contentType?.includes('application/json')) {
            return response.json();
        }

        return response.text();
    } catch (error) {
        if (error.name === 'AbortError') {
            console.log('Request aborted');
        }
        throw error;
    }
}

// Usage
const users = await apiRequest('/users');
const newUser = await apiRequest('/users', {
    method: 'POST',
    body: { name: 'Alice' }
});

// Parallel requests
async function fetchDashboardData() {
    const [users, posts, stats] = await Promise.all([
        apiRequest('/users'),
        apiRequest('/posts'),
        apiRequest('/stats')
    ]);

    return { users, posts, stats };
}

// Download file
async function downloadFile(url, filename) {
    const response = await fetch(url);
    const blob = await response.blob();

    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = filename;
    link.click();

    URL.revokeObjectURL(link.href);
}

// Upload with progress (using XMLHttpRequest)
function uploadWithProgress(url, file, onProgress) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();

        xhr.upload.addEventListener('progress', (e) => {
            if (e.lengthComputable) {
                onProgress((e.loaded / e.total) * 100);
            }
        });

        xhr.addEventListener('load', () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(JSON.parse(xhr.responseText));
            } else {
                reject(new Error(`Upload failed: ${xhr.status}`));
            }
        });

        xhr.addEventListener('error', () => reject(new Error('Upload failed')));

        const formData = new FormData();
        formData.append('file', file);

        xhr.open('POST', url);
        xhr.send(formData);
    });
}

JSON Operations

Key Concepts

  • JSON.parse(): Convert JSON string to JavaScript object
  • JSON.stringify(): Convert JavaScript object to JSON string
  • Reviver/Replacer: Custom transformation during parse/stringify
  • Circular references: Must be handled specially
  • JSON limitations: No functions, undefined, symbols, or circular refs

Common Patterns

// Basic parsing
const jsonString = '{"name":"Alice","age":30}';
const obj = JSON.parse(jsonString);

// Basic stringify
const obj = { name: 'Alice', age: 30 };
const json = JSON.stringify(obj);

// Pretty print
const pretty = JSON.stringify(obj, null, 2);

// Replacer function (filter/transform)
const json = JSON.stringify(obj, (key, value) => {
    if (key === 'password') return undefined; // Exclude
    if (typeof value === 'string') return value.toUpperCase();
    return value;
});

// Replacer array (whitelist)
const json = JSON.stringify(obj, ['name', 'email']);

// Reviver function (transform on parse)
const obj = JSON.parse(jsonString, (key, value) => {
    if (key === 'date') return new Date(value);
    return value;
});

// Deep clone — prefer structuredClone(); the JSON round-trip
// works for simple objects only (drops Dates, Maps, undefined)
const clone = structuredClone(original);

// Handle errors
try {
    const obj = JSON.parse(invalidJson);
} catch (error) {
    if (error instanceof SyntaxError) {
        console.error('Invalid JSON:', error.message);
    }
}

// Check if valid JSON
function isValidJSON(str) {
    try {
        JSON.parse(str);
        return true;
    } catch {
        return false;
    }
}

Examples

// Handling dates
const data = {
    name: 'Event',
    date: new Date('2024-01-15')
};

// Dates become strings
const json = JSON.stringify(data);
// '{"name":"Event","date":"2024-01-15T00:00:00.000Z"}'

// Parse with date revival
const parsed = JSON.parse(json, (key, value) => {
    const datePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
    if (typeof value === 'string' && datePattern.test(value)) {
        return new Date(value);
    }
    return value;
});

// Custom toJSON method
class User {
    constructor(name, password) {
        this.name = name;
        this.password = password;
    }

    toJSON() {
        return {
            name: this.name
            // Password excluded
        };
    }
}

const user = new User('Alice', 'secret');
JSON.stringify(user); // '{"name":"Alice"}'

// Handle circular references
function safeStringify(obj) {
    const seen = new WeakSet();
    return JSON.stringify(obj, (key, value) => {
        if (typeof value === 'object' && value !== null) {
            if (seen.has(value)) {
                return '[Circular]';
            }
            seen.add(value);
        }
        return value;
    });
}

// Local storage helpers
const storage = {
    get(key) {
        const item = localStorage.getItem(key);
        return item ? JSON.parse(item) : null;
    },

    set(key, value) {
        localStorage.setItem(key, JSON.stringify(value));
    },

    remove(key) {
        localStorage.removeItem(key);
    }
};

storage.set('user', { name: 'Alice', preferences: { theme: 'dark' } });
const user = storage.get('user');

Common Array Methods

Key Concepts

  • Transformation methods: Create new arrays (map, filter, slice)
  • Aggregation methods: Reduce to single value (reduce, find, some)
  • Mutation methods: Modify original array (push, pop, sort, splice)
  • Iteration methods: Loop through elements (forEach, for...of)
  • Method chaining: Combine multiple methods for complex operations

Common Patterns

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

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

// filter - keep matching elements
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4]

// reduce - accumulate to single value
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15

// forEach - iterate without return
numbers.forEach((n, index) => {
    console.log(`Index ${index}: ${n}`);
});

// find - first matching element
const found = numbers.find(n => n > 3);
// 4

// findIndex - index of first match
const index = numbers.findIndex(n => n > 3);
// 3

// some - at least one matches
const hasEven = numbers.some(n => n % 2 === 0);
// true

// every - all match
const allPositive = numbers.every(n => n > 0);
// true

// includes - check existence
const hasThree = numbers.includes(3);
// true

// indexOf/lastIndexOf
const idx = numbers.indexOf(3);
// 2

// slice - copy portion (non-mutating)
const portion = numbers.slice(1, 4);
// [2, 3, 4]

// concat - merge arrays
const merged = numbers.concat([6, 7, 8]);

// flat - flatten nested arrays
const nested = [[1, 2], [3, 4]];
const flat = nested.flat();
// [1, 2, 3, 4]

// flatMap - map then flatten
const result = numbers.flatMap(n => [n, n * 2]);
// [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

// Array.from - create from iterable
const arr = Array.from('hello');
// ['h', 'e', 'l', 'l', 'o']

// Array.from with map
const squares = Array.from({ length: 5 }, (_, i) => i * i);
// [0, 1, 4, 9, 16]

// Mutating methods (use with caution)
numbers.push(6);     // Add to end
numbers.pop();       // Remove from end
numbers.unshift(0);  // Add to start
numbers.shift();     // Remove from start
numbers.splice(2, 1); // Remove at index
numbers.sort((a, b) => a - b); // Sort
numbers.reverse();   // Reverse

Examples

// Method chaining
const users = [
    { name: 'Alice', age: 30, active: true },
    { name: 'Bob', age: 25, active: false },
    { name: 'Charlie', age: 35, active: true },
    { name: 'Diana', age: 28, active: true }
];

const result = users
    .filter(user => user.active)
    .map(user => user.name)
    .sort()
    .join(', ');
// 'Alice, Charlie, Diana'

// Reduce for complex transformations
const grouped = users.reduce((acc, user) => {
    const key = user.active ? 'active' : 'inactive';
    if (!acc[key]) acc[key] = [];
    acc[key].push(user);
    return acc;
}, {});

// Count occurrences
const words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'];
const counts = words.reduce((acc, word) => {
    acc[word] = (acc[word] || 0) + 1;
    return acc;
}, {});
// { apple: 3, banana: 2, cherry: 1 }

// Unique values
const unique = [...new Set(words)];
// ['apple', 'banana', 'cherry']

// Find max/min
const ages = users.map(u => u.age);
const maxAge = Math.max(...ages);
const minAge = Math.min(...ages);

// Partition array
const partition = (arr, predicate) => {
    return arr.reduce(([pass, fail], item) => {
        return predicate(item)
            ? [[...pass, item], fail]
            : [pass, [...fail, item]];
    }, [[], []]);
};

const [active, inactive] = partition(users, u => u.active);

// Sort by multiple criteria
users.sort((a, b) => {
    if (a.active !== b.active) {
        return b.active - a.active; // Active first
    }
    return a.name.localeCompare(b.name); // Then by name
});

// toSorted, toReversed, toSpliced (non-mutating, ES2023)
const sorted = numbers.toSorted((a, b) => b - a);
const reversed = numbers.toReversed();
const spliced = numbers.toSpliced(2, 1, 99);

Error Handling

Key Concepts

  • try/catch/finally: Handle synchronous errors
  • Error types: Error, TypeError, ReferenceError, SyntaxError, etc.
  • Custom errors: Extend Error class for specific error types
  • Error propagation: Errors bubble up unless caught
  • Async error handling: Use try/catch with async/await

Common Patterns

// Basic try/catch
try {
    const result = riskyOperation();
    console.log(result);
} catch (error) {
    console.error('Error:', error.message);
} finally {
    cleanup();
}

// Throwing errors
function divide(a, b) {
    if (b === 0) {
        throw new Error('Division by zero');
    }
    return a / b;
}

// Error types
throw new Error('Generic error');
throw new TypeError('Expected a string');
throw new RangeError('Value out of range');
throw new ReferenceError('Variable not defined');

// Check error type
try {
    JSON.parse(invalidJson);
} catch (error) {
    if (error instanceof SyntaxError) {
        console.error('Invalid JSON');
    } else {
        throw error; // Re-throw unexpected errors
    }
}

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

throw new ValidationError('Email is required', 'email');

// Async error handling
async function fetchData() {
    try {
        const response = await fetch('/api/data');
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
        return await response.json();
    } catch (error) {
        console.error('Fetch failed:', error.message);
        throw error; // Re-throw for caller to handle
    }
}

// Promise error handling
fetchData()
    .then(data => console.log(data))
    .catch(error => console.error(error));

// Error boundaries pattern
async function withErrorHandling(fn, fallback) {
    try {
        return await fn();
    } catch (error) {
        console.error(error);
        return fallback;
    }
}

const data = await withErrorHandling(
    () => fetchData(),
    { error: true, data: [] }
);

Examples

// Comprehensive error handling
class AppError extends Error {
    constructor(message, statusCode = 500, isOperational = true) {
        super(message);
        this.name = this.constructor.name;
        this.statusCode = statusCode;
        this.isOperational = isOperational;
        Error.captureStackTrace(this, this.constructor);
    }
}

class NotFoundError extends AppError {
    constructor(resource) {
        super(`${resource} not found`, 404);
    }
}

class ValidationError extends AppError {
    constructor(errors) {
        super('Validation failed', 400);
        this.errors = errors;
    }
}

// Usage in API
async function getUser(id) {
    const user = await db.findUser(id);
    if (!user) {
        throw new NotFoundError('User');
    }
    return user;
}

// Global error handler
function handleError(error) {
    if (error instanceof ValidationError) {
        console.error('Validation:', error.errors);
        return { status: 400, errors: error.errors };
    }

    if (error instanceof NotFoundError) {
        console.error('Not found:', error.message);
        return { status: 404, message: error.message };
    }

    // Unexpected error
    console.error('Unexpected error:', error);
    return { status: 500, message: 'Internal server error' };
}

// Async operation with multiple error types
async function processOrder(orderId) {
    let order;
    try {
        order = await getOrder(orderId);
        validateOrder(order);
        await chargePayment(order);
        await shipOrder(order);
        return { success: true };
    } catch (error) {
        if (error.name === 'PaymentError') {
            await refundPayment(order);
        }
        throw error;
    }
}

// Error logging utility
function logError(error, context = {}) {
    const errorLog = {
        timestamp: new Date().toISOString(),
        name: error.name,
        message: error.message,
        stack: error.stack,
        ...context
    };

    console.error(JSON.stringify(errorLog, null, 2));
    // Send to error tracking service
}

// Assert utility
function assert(condition, message) {
    if (!condition) {
        throw new Error(message || 'Assertion failed');
    }
}

assert(user.age >= 18, 'User must be at least 18 years old');

Quick Reference

Category Method/Syntax Description
Variables let x = 1 Block-scoped, reassignable
const x = 1 Block-scoped, constant
var x = 1 Function-scoped (legacy)
Functions function fn() {} Function declaration
const fn = () => {} Arrow function
async function fn() {} Async function
Arrays arr.map(fn) Transform elements
arr.filter(fn) Filter elements
arr.reduce(fn, init) Reduce to value
arr.find(fn) Find first match
arr.forEach(fn) Iterate elements
[...arr] Spread/copy array
Objects {...obj} Spread/copy object
{a, b} = obj Destructure object
Object.keys(obj) Get keys array
Object.values(obj) Get values array
obj?.prop Optional chaining
a ?? b Nullish coalescing
DOM querySelector(sel) Select element
querySelectorAll(sel) Select all matching
createElement(tag) Create element
addEventListener(evt, fn) Add event listener
classList.add/remove/toggle Modify classes
Promises new Promise((res, rej) => {}) Create promise
await promise Wait for promise
Promise.all([...]) Wait for all
Promise.race([...]) First to settle
Fetch fetch(url) GET request
response.json() Parse JSON response
fetch(url, {method: 'POST'}) POST request
JSON JSON.parse(str) Parse JSON string
JSON.stringify(obj) Convert to JSON
Errors try {...} catch (e) {...} Error handling
throw new Error(msg) Throw error

Common Issues and Solutions

Issue: undefined is not a function

Cause: Calling something that isn't a function or method doesn't exist.

// Problem
const obj = {};
obj.method(); // TypeError

// Solution: Check existence
if (typeof obj.method === 'function') {
    obj.method();
}

// Or use optional chaining
obj.method?.();

Issue: Callback hell / Pyramid of doom

Cause: Nested callbacks make code hard to read and maintain.

// Problem
getData(function(a) {
    getMoreData(a, function(b) {
        getEvenMoreData(b, function(c) {
            // Deep nesting
        });
    });
});

// Solution: Use async/await
async function loadAll() {
    const a = await getData();
    const b = await getMoreData(a);
    const c = await getEvenMoreData(b);
    return c;
}

Issue: this is undefined in callback

Cause: Regular functions lose this context in callbacks.

// Problem
const obj = {
    value: 42,
    getValue: function() {
        setTimeout(function() {
            console.log(this.value); // undefined
        }, 100);
    }
};

// Solution 1: Arrow function
const obj = {
    value: 42,
    getValue: function() {
        setTimeout(() => {
            console.log(this.value); // 42
        }, 100);
    }
};

// Solution 2: Bind
setTimeout(function() {
    console.log(this.value);
}.bind(this), 100);

Issue: Array/Object mutation causing bugs

Cause: Mutating original data leads to unexpected side effects.

// Problem
const original = [1, 2, 3];
const modified = original;
modified.push(4);
console.log(original); // [1, 2, 3, 4] - mutated!

// Solution: Create copies
const modified = [...original, 4];
const modified = original.concat(4);

// For objects
const newObj = { ...original, newProp: value };

Issue: Race conditions with async code

Cause: Multiple async operations completing in unexpected order.

// Problem
let result;
fetchData1().then(data => result = data);
fetchData2().then(data => result = data);
// Which one wins?

// Solution: Use Promise.all or chain properly
const [data1, data2] = await Promise.all([
    fetchData1(),
    fetchData2()
]);

Issue: Memory leaks with event listeners

Cause: Not removing event listeners when no longer needed.

// Problem: Listener accumulates
function setup() {
    window.addEventListener('resize', handleResize);
}

// Solution: Remove when done
function cleanup() {
    window.removeEventListener('resize', handleResize);
}

// Or use AbortController
const controller = new AbortController();
window.addEventListener('resize', handleResize, {
    signal: controller.signal
});

// Later
controller.abort(); // Removes all listeners with this signal

Issue: Floating point precision errors

Cause: JavaScript uses IEEE 754 floating point.

// Problem
0.1 + 0.2 === 0.3; // false
0.1 + 0.2; // 0.30000000000000004

// Solution: Use tolerance or integers
Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON; // true

// Or work with integers (cents instead of pounds)
const price = 199; // £1.99 in pence
const display = (price / 100).toFixed(2);

Issue: Unhandled promise rejections

Cause: Promises without catch handlers.

// Problem
async function riskyOperation() {
    throw new Error('Failed');
}
riskyOperation(); // Unhandled rejection

// Solution: Always handle rejections
riskyOperation().catch(console.error);

// Or with try/catch
try {
    await riskyOperation();
} catch (error) {
    console.error(error);
}

// Global handler (last resort)
window.addEventListener('unhandledrejection', event => {
    console.error('Unhandled rejection:', event.reason);
});

Related Topics

To further enhance your JavaScript knowledge, consider exploring these complementary topics:

  • TypeScript: Adds static typing to JavaScript for improved tooling and error detection at compile time
  • Node.js: Server-side JavaScript runtime for building backend applications and APIs
  • React/Vue/Angular: Frontend frameworks for building complex, reactive user interfaces
  • Testing (Jest/Mocha): Unit and integration testing frameworks for ensuring code quality
  • Webpack/Vite: Module bundlers and build tools for optimising JavaScript applications
  • Web APIs: Browser APIs like WebSockets, Service Workers, IndexedDB, and Web Workers for advanced functionality