Console Methods
The console object provides various methods beyond console.log().
// Basic logging
console.log("Simple message");
console.log("Value:", variable, "Another:", other);
// Log levels
console.info("Informational message");
console.warn("Warning message"); // Yellow highlight
console.error("Error message"); // Red highlight, with stack trace
// Format output
console.log("%c Styled text", "color: blue; font-size: 20px");
console.log("%o", object); // Object format
console.log("%d", 123); // Integer
console.log("%f", 3.14); // Float
console.log("%s", "text"); // String
// Display objects
const user = { name: "John", age: 30 };
console.log(user); // Collapsed view
console.dir(user); // Full object tree
console.table([ // Table format
{ name: "John", age: 30 },
{ name: "Jane", age: 25 }
]);
// Grouping
console.group("User Details");
console.log("Name:", user.name);
console.log("Age:", user.age);
console.groupEnd();
console.groupCollapsed("Hidden by default");
console.log("Details inside");
console.groupEnd();
// Assertions
console.assert(1 === 1, "This won't show");
console.assert(1 === 2, "Assertion failed!"); // Shows error
// Counting
console.count("loop"); // loop: 1
console.count("loop"); // loop: 2
console.countReset("loop");
// Timing
console.time("operation");
// ... code to measure ...
console.timeLog("operation"); // Intermediate time
// ... more code ...
console.timeEnd("operation"); // Final time
// Stack trace
function a() { b(); }
function b() { c(); }
function c() { console.trace("Call stack:"); }
a(); // Shows: c -> b -> a
// Clear console
console.clear();
Breakpoints & DevTools
Use browser DevTools for step-by-step debugging.
// Programmatic breakpoint
function calculateTotal(items) {
debugger; // Execution pauses here when DevTools is open
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
// Conditional breakpoint (set in DevTools)
// Right-click line number -> "Add conditional breakpoint"
// Example condition: item.price > 100
// ===== DevTools Features =====
// Sources Panel:
// - Set breakpoints by clicking line numbers
// - Step Over (F10) - next line
// - Step Into (F11) - enter function
// - Step Out (Shift+F11) - exit function
// - Continue (F8) - run until next breakpoint
// Watch expressions:
// Add variables to watch panel to monitor their values
// Call Stack:
// Shows the path of function calls to current point
// Scope:
// View local, closure, and global variables
// ===== Event Listener Breakpoints =====
// In Sources panel, expand "Event Listener Breakpoints"
// Check events like "Mouse > click" to break on any click handler
// ===== XHR/Fetch Breakpoints =====
// Break when URL contains specific string
// Useful for debugging API calls
// ===== DOM Breakpoints =====
// Right-click element in Elements panel
// - Break on subtree modifications
// - Break on attribute modifications
// - Break on node removal
- F12 or Ctrl+Shift+I - Open DevTools
- Ctrl+Shift+J - Open Console directly
- Ctrl+P - Quick file open in Sources
- Ctrl+Shift+F - Search all files
Debugging Errors
Techniques for tracking down and fixing common errors.
// ===== TypeError =====
// "Cannot read property 'x' of undefined"
const obj = undefined;
// obj.property; // Error!
// Fix: Check before accessing
if (obj && obj.property) { }
// Or use optional chaining
obj?.property;
// ===== ReferenceError =====
// "x is not defined"
// console.log(undefinedVar); // Error!
// Fix: Ensure variable is declared
let definedVar = "value";
// ===== SyntaxError =====
// Usually caught before runtime
// Check for:
// - Missing brackets/parentheses
// - Invalid JSON
// - Reserved words as identifiers
// ===== Debugging async code =====
async function fetchData() {
try {
const response = await fetch("/api/data");
console.log("Response status:", response.status);
if (!response.ok) {
console.error("Request failed:", response.statusText);
return;
}
const data = await response.json();
console.log("Data received:", data);
return data;
} catch (error) {
console.error("Fetch error:", error);
console.error("Stack:", error.stack);
}
}
// ===== Debugging event listeners =====
function handleClick(event) {
console.log("Event type:", event.type);
console.log("Target:", event.target);
console.log("Current target:", event.currentTarget);
console.log("Event phase:", event.eventPhase);
// Do something
}
// ===== Debugging this context =====
const obj2 = {
value: 42,
getValue() {
console.log("this is:", this);
console.log("this.value:", this.value);
return this.value;
}
};
obj2.getValue(); // this = obj2
const fn = obj2.getValue;
fn(); // this = undefined (strict) or window
// Fix: bind, arrow function, or call with context
fn.call(obj2); // this = obj2
Debugging Strategies
Systematic approaches to finding and fixing bugs.
// ===== 1. Reproduce the Bug =====
// Create minimal test case that triggers the bug
function buggyFunction(data) {
// Simplify until you isolate the issue
}
// ===== 2. Binary Search Debugging =====
// Add console.log in the middle of code
// If bug is before that point, move log earlier
// If after, move log later
function processData(items) {
console.log("Step 1: items received", items);
const filtered = items.filter(x => x.active);
console.log("Step 2: after filter", filtered);
const mapped = filtered.map(x => x.value);
console.log("Step 3: after map", mapped);
return mapped.reduce((a, b) => a + b, 0);
}
// ===== 3. Rubber Duck Debugging =====
// Explain your code line-by-line out loud
// Often reveals faulty assumptions
// ===== 4. Check Inputs and Outputs =====
function calculate(a, b) {
console.log("Input:", { a, b, typeA: typeof a, typeB: typeof b });
const result = a + b;
console.log("Output:", result, typeof result);
return result;
}
calculate(5, "3"); // Might reveal string concatenation!
// ===== 5. Use Defensive Programming =====
function divide(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("Arguments must be numbers");
}
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
// ===== 6. Isolate Components =====
// Test each function independently
function validateEmail(email) {
const pattern = /^[\w.-]+@[\w.-]+\.\w{2,}$/;
return pattern.test(email);
}
// Test in console
console.log(validateEmail("test@example.com")); // true
console.log(validateEmail("invalid")); // false
// ===== 7. Check for Common Issues =====
// - Off-by-one errors in loops
// - Async timing issues
// - Scope and closure problems
// - Mutating objects unexpectedly
// - Type coercion surprises
Network Debugging
Debug API calls and network issues.
// Wrap fetch for logging
async function debugFetch(url, options = {}) {
console.group(`Fetch: ${options.method || "GET"} ${url}`);
console.log("Options:", options);
console.time("Request duration");
try {
const response = await fetch(url, options);
console.log("Status:", response.status, response.statusText);
console.log("Headers:", Object.fromEntries(response.headers));
// Clone to read body without consuming it
const clone = response.clone();
const body = await clone.text();
console.log("Body:", body.substring(0, 500));
console.timeEnd("Request duration");
console.groupEnd();
return response;
} catch (error) {
console.error("Request failed:", error);
console.timeEnd("Request duration");
console.groupEnd();
throw error;
}
}
// DevTools Network Panel:
// - View all requests and responses
// - Filter by type (XHR, Fetch, JS, CSS)
// - See request/response headers
// - Preview response body
// - View timing breakdown
// - Throttle connection speed
// - Block specific requests
// - Replay requests
Performance Profiling
Chrome DevTools provides powerful tools to identify performance bottlenecks, analyze runtime behavior, and optimize your JavaScript code.
Performance Panel
// Mark timeline for Performance panel
performance.mark('startTask');
// Do expensive work
processLargeDataset(data);
performance.mark('endTask');
performance.measure('processData', 'startTask', 'endTask');
// Get measurements
const measures = performance.getEntriesByName('processData');
console.log('Duration:', measures[0].duration, 'ms');
// Clean up
performance.clearMarks();
performance.clearMeasures();
// User Timing API for custom metrics
class PerformanceTracker {
static startMeasure(name) {
performance.mark(`${name}-start`);
}
static endMeasure(name) {
performance.mark(`${name}-end`);
performance.measure(name, `${name}-start`, `${name}-end`);
const entry = performance.getEntriesByName(name)[0];
console.log(`[Performance] ${name}: ${entry.duration.toFixed(2)}ms`);
// Send to analytics
if (entry.duration > 100) {
analytics.track('slow_operation', { name, duration: entry.duration });
}
return entry.duration;
}
}
// Usage
PerformanceTracker.startMeasure('render');
renderComponent();
PerformanceTracker.endMeasure('render');
// Frame timing for animations
function measureFPS() {
let frames = 0;
let lastTime = performance.now();
function count() {
frames++;
const now = performance.now();
if (now - lastTime >= 1000) {
console.log('FPS:', frames);
frames = 0;
lastTime = now;
}
requestAnimationFrame(count);
}
requestAnimationFrame(count);
}
Memory Leaks & Profiling
Memory leaks cause applications to slow down over time. Learn to identify and fix common memory issues using DevTools Memory panel.
Common Memory Leak Patterns
// ❌ Leak 1: Forgotten event listeners
class LeakyComponent {
constructor() {
this.handleScroll = () => this.onScroll();
window.addEventListener('scroll', this.handleScroll);
}
onScroll() { /* ... */ }
destroy() {
// Forgot to remove listener!
}
}
// ✅ Fix: Always clean up
class FixedComponent {
constructor() {
this.handleScroll = this.onScroll.bind(this);
window.addEventListener('scroll', this.handleScroll);
}
onScroll() { /* ... */ }
destroy() {
window.removeEventListener('scroll', this.handleScroll);
}
}
// ❌ Leak 2: Closures holding references
function createHandler(element) {
const hugeData = new Array(1000000).fill('data');
return function handler() {
console.log(element.id); // Holds reference to element AND hugeData
};
}
// ✅ Fix: Only capture what you need
function createHandlerFixed(element) {
const id = element.id; // Capture only the id
return function handler() {
console.log(id);
};
}
// ❌ Leak 3: Growing collections
const cache = {};
function processItem(item) {
cache[item.id] = expensiveComputation(item); // Never cleared!
return cache[item.id];
}
// ✅ Fix: Use WeakMap or LRU cache
const cache = new WeakMap(); // Auto-cleanup when key is GC'd
// Or implement LRU cache
class LRUCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
}
get(key) {
if (this.cache.has(key)) {
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value); // Move to end
return value;
}
return undefined;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
const oldest = this.cache.keys().next().value;
this.cache.delete(oldest);
}
this.cache.set(key, value);
}
}
// ❌ Leak 4: Timers not cleared
function startPolling() {
setInterval(() => {
fetchData(); // Runs forever even if component unmounts
}, 1000);
}
// ✅ Fix: Clear timers
function startPollingFixed() {
const intervalId = setInterval(() => {
fetchData();
}, 1000);
return () => clearInterval(intervalId); // Return cleanup function
}
Using Memory DevTools
// Memory Panel workflow:
// 1. Take Heap Snapshot
// - DevTools > Memory > Heap Snapshot > Take Snapshot
// - Shows all objects in memory
// 2. Record Allocation Timeline
// - DevTools > Memory > Allocation instrumentation on timeline
// - Shows memory allocation over time
// - Blue bars = allocated, gray = freed
// 3. Compare Snapshots
// - Take snapshot before action
// - Perform suspected leaky action
// - Take another snapshot
// - Compare to see what wasn't released
// Programmatic memory info (Chrome)
if (performance.memory) {
console.log('JS Heap Size Limit:', performance.memory.jsHeapSizeLimit);
console.log('Total JS Heap Size:', performance.memory.totalJSHeapSize);
console.log('Used JS Heap Size:', performance.memory.usedJSHeapSize);
}
// Force garbage collection (DevTools only)
// 1. Open DevTools
// 2. Click "Collect Garbage" button in Memory panel
// Or use: gc() in console (with --expose-gc flag)
// Memory-efficient patterns
// Use object pooling for frequently created objects
class ObjectPool {
constructor(factory, reset) {
this.factory = factory;
this.reset = reset;
this.pool = [];
}
acquire() {
return this.pool.pop() || this.factory();
}
release(obj) {
this.reset(obj);
this.pool.push(obj);
}
}
const particlePool = new ObjectPool(
() => ({ x: 0, y: 0, vx: 0, vy: 0 }),
(p) => { p.x = p.y = p.vx = p.vy = 0; }
);
- Growing memory: Memory usage increases over time without plateauing
- Slow performance: App gets slower the longer it runs
- Detached DOM nodes: Elements removed from DOM but still referenced
- Browser crashes: Tab runs out of memory on long-running pages
Summary
Console Methods
log, warn, error, table, group, time
Breakpoints
debugger keyword, line/conditional
DevTools
Sources, Network, Elements panels
Step Debugging
Step over, into, out, continue
Error Analysis
Read stack traces, check types
Strategies
Binary search, isolate, rubber duck