JavaScript • Debugging and Testing

Console Debugging — Use console.log and console.table for debugging

Learn to inspect your code's behavior in real time using JavaScript's built-in console methods.

JavaScript emblem with code brackets, braces, and a code file.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

Every programmer, from absolute beginner to seasoned professional, writes code that doesn't work as expected. The challenge of finding and fixing those mistakes — called debugging — is as old as programming itself. In the earliest days of computing, engineers literally searched for physical bugs trapped inside vacuum-tube machines. As programming languages evolved, developers needed better tools to peek inside their running code and understand what was happening step by step.

When JavaScript was created in 1995 by Brendan Eich at Netscape, it ran entirely inside web browsers with almost no debugging support. Developers relied on clunky alert() pop-up boxes to display variable values — a slow and frustrating process. Over the next decade, browser makers introduced the developer console, a built-in panel where messages from your code could appear without interrupting the user. This transformed how developers investigate bugs.

1995 • JavaScript Is Born

Brendan Eich creates JavaScript for Netscape Navigator. Debugging relies on alert() pop-ups — each one freezes the entire page until dismissed.

2006 • Firebug Introduces the Console

The Firebug extension for Firefox introduces a dedicated console panel with console.log(), giving developers a non-intrusive way to inspect values while code runs.

2008 • Chrome DevTools Launches

Google Chrome ships with built-in Developer Tools, including a fast JavaScript console. Other browsers quickly follow, making console methods a universal standard.

2010 • Node.js Brings Console to Servers

Node.js allows JavaScript to run outside the browser. The console object works in terminal windows, making console debugging universal across front-end and back-end code.

2015+ • Rich Console Methods Expand

Modern browsers add console.table(), console.group(), console.time(), and more — turning the console into a powerful debugging dashboard.

Today, console debugging remains the most common first step when something goes wrong in a JavaScript program. Even with advanced debugger tools available, the simplicity and speed of printing values to the console makes it an indispensable skill. The central question is: how do you use console.log() and console.table() effectively to track down bugs and understand your code's behavior?

SECTION 2

Core Principles of Console Debugging

Console debugging is built on a straightforward idea: you insert temporary output statements into your code so you can see what values your variables hold, which branches of logic execute, and whether functions produce the results you expect. Think of it as placing checkpoints along a road to confirm your car is still heading in the right direction. Before diving into specific methods, it helps to understand the foundational principles that make console debugging effective.

1

Observe, Don't Guess

Instead of guessing why code fails, use console.log() to observe actual values at specific points. Data beats assumptions every time.
2

Isolate the Problem

Place log statements strategically — before and after suspicious lines — to narrow down exactly where behavior deviates from what you expected.
3

Label Your Output

Always include a descriptive string label alongside the value you're logging, like console.log("score:", score). Unlabeled output quickly becomes confusing.
4

Choose the Right Method

Use console.log() for simple values and console.table() for arrays and objects. Picking the right tool saves time and makes patterns visible.
5

Clean Up After Yourself

Remove or comment out debug statements once you've fixed the bug. Leftover logs clutter the console and can accidentally reveal data in production applications.
✦

KEY TAKEAWAY

Console debugging is like turning on the lights in a dark room. Your code runs in an invisible process — you can't see the values changing. Inserting console.log() statements is like flipping light switches at different spots in the room so you can see exactly what's there. console.table() is like turning on a bright overhead light that illuminates an entire collection of items at once, neatly organized on a shelf.

SECTION 3

How Console Output Flows

Understanding where your console messages actually appear is essential. When you write a console.log() statement in your JavaScript code, the browser's JavaScript engine executes it, and the output is sent to the Developer Tools Console panel. The diagram below illustrates this flow from your source code through the engine to the console display.

How console.log() Output FlowsYOUR CODElet x = 5;let y = x * 2;console.log("y:", y);let z = y + 3;JS ENGINEExecutes code line by lineEvaluates expressionsCONSOLE PANEL▸ y: 10Output appears herewithout interruptingthe running programconsole.table() — Array Examplelet scores = [85, 92, 78];console.table(scores);(index) Value 0 85 1 92 2 78console.table() — Object Arrayconsole.table(students);(index) name grade 0 "Alice" 90 1 "Bob" 85 2 "Carol" 92Columns auto-created from keys
Top row: Code flows through the JavaScript engine to the console panel. Bottom row: console.table() renders arrays and object arrays as formatted tables with auto-generated index and value columns.

Notice that console.log() prints values in a single line of text, which is perfect for checking one or two variables at a time. In contrast, console.table() automatically organizes data into rows and columns, making it far easier to scan when you're working with arrays or arrays of objects. The console panel is typically opened with the keyboard shortcut F12 (or Ctrl+Shift+J on Windows, Cmd+Option+J on Mac) and remains docked alongside your webpage.

SECTION 4

How console.log and console.table Work

Both console.log() and console.table() are methods on the global console object, which is available in every JavaScript environment — browsers, Node.js, and code editors with built-in terminals. They don't return values or affect your program's logic; they simply send information to the output panel. Understanding their syntax and behavior helps you use them efficiently.

console.log() — The Versatile Printer

The console.log() method accepts any number of arguments, separated by commas. It converts each argument to a readable string and prints them on a single line, separated by spaces. You can pass strings, numbers, booleans, arrays, objects, and even expressions that get evaluated before printing.

💡

Syntax Patterns

console.log(value) — prints a single value. console.log("label:", value) — prints a label followed by the value. console.log(value1, value2, value3) — prints multiple values on one line. console.log(`Total: ${a + b}`) — uses a template literal to embed expressions inside a string.

console.table() — The Data Organizer

The console.table() method takes one required argument — an array or an object — and an optional second argument specifying which columns to display. It renders the data as a sortable, interactive table in the console. For a simple array like [10, 20, 30], it creates an "(index)" column and a "Value" column. For an array of objects, each object property becomes its own column. This is incredibly useful when you need to compare values across multiple items.

💡

Syntax Patterns

console.table(array) — displays an array as a table with index and value columns. console.table(arrayOfObjects) — displays each object's properties as separate columns. console.table(arrayOfObjects, ["name", "score"]) — displays only the specified columns, hiding the rest.

What Data Types Can You Log?

Data Typeconsole.log() Outputconsole.table() Output
String"Hello" → HelloSingle-cell table (not very useful)
Number42 → 42Single-cell table
Booleantrue → trueSingle-cell table
ArrayPrints inline: [1, 2, 3]Formatted table with index column ✓
ObjectExpandable tree: {name: "Ali", age: 16}Table with key/value columns ✓
Array of ObjectsCompact but hard to scanBeautiful multi-column table ✓✓

Comparison of output formats for different data types

SECTION 5

Debugging Techniques & Patterns

Knowing the syntax of console.log() and console.table() is only half the battle. The real skill is knowing where and when to place them. This section covers practical patterns that experienced developers use daily.

Five Console Debugging Patterns1. CHECKPOINT LOGGING// Before calculationconsole.log("a:", a);let result = a * b;// After calculationconsole.log("res:", result);2. CONDITIONAL LOGGINGif (score < 0) { console.log("BUG! negative:", score);}Only logs when something is wrong3. LOOP INSPECTIONfor (let i = 0; i < arr.length; i++){ console.log(i, arr[i]);}See every iteration's values4. TABLE FOR COLLECTIONSlet students = [ { name: "Ali", grade: 95 }, { name: "Sam", grade: 82 }, { name: "Mia", grade: 91 }];console.table(students);5. FUNCTION TRACINGfunction add(a, b) { console.log("add called", {a, b}); let sum = a + b; console.log("returning:", sum); return sum;When to Use Each PatternCheckpoint: narrow down which line causes the bugConditional: detect unexpected states without flooding the consoleLoop: verify every iteration produces correct valuesTable: compare multiple data items at a glanceFunction tracing: confirm a function is called with correct arguments and returns expected values
Five essential console debugging patterns: checkpoint logging, conditional logging, loop inspection, table display for collections, and function tracing. Each pattern addresses a different type of bug.

The checkpoint pattern is the most basic technique: place a log before and after a suspicious line to see how a value changes. The conditional pattern is smarter — it only prints when an impossible or unexpected condition is detected, keeping the console clean. When debugging loops, logging the loop counter alongside the current element reveals whether your loop runs the correct number of times and accesses the right data. For structured data, console.table() is almost always better than console.log() because scanning a formatted table is far faster than reading a jumble of bracketed text.

💡

Pro Tip: Object Shorthand

Instead of writing console.log("x:", x, "y:", y), use the shorthand console.log({x, y}). JavaScript automatically creates an object {x: 5, y: 10} where the variable names become keys. This is faster to type and easier to read!

SECTION 6

Worked Example — Finding a Bug with Console Methods

Let's walk through a realistic debugging scenario. Imagine you wrote a function to calculate the average grade for a class, but it keeps returning the wrong answer. Here's the buggy code, followed by how you'd use console.log() and console.table() to find and fix the problem.

Debugging a Grade Calculator

  1. 1

    Step 1 — Identify the Problem

    You have this function, but getAverage([85, 92, 78, 90]) returns NaN instead of 86.25. The original buggy code is: function getAverage(grades) { let total; for (let i = 0; i <= grades.length; i++) { total += grades[i]; } return total / grades.length; }
    Expected 86.25, got NaN
  2. 2

    Step 2 — Add console.table to Inspect the Data

    First, verify that the input data looks correct by adding console.table(grades) at the top of the function. The console shows a clean table with indices 0–3 and the values 85, 92, 78, 90. The input data is fine, so the bug is in the calculation logic.
    Input data confirmed correct ✓
  3. 3

    Step 3 — Add console.log Inside the Loop

    Next, add console.log("i:", i, "grades[i]:", grades[i], "total:", total) inside the loop. The output reveals two problems. On the first iteration (i = 0), total is NaN because it was declared without an initial value (so it's undefined), and undefined + 85 equals NaN. Additionally, when i = 4, grades[4] is undefined because the loop condition uses <= instead of <, going one index past the end of the array.
    Two bugs found: (1) total not initialized to 0, (2) off-by-one error in loop condition
  4. 4

    Step 4 — Fix the Bugs

    Change let total; to let total = 0; and change i <= grades.length to i < grades.length. The fixed code is: function getAverage(grades) { let total = 0; for (let i = 0; i < grades.length; i++) { total += grades[i]; } return total / grades.length; }
    getAverage([85, 92, 78, 90]) now returns 86.25 ✓
  5. 5

    Step 5 — Verify and Clean Up

    Run the function one more time with the debug statements still in place. Confirm the console output shows correct running totals: 85, 177, 255, 345. The final result is 345 ÷ 4 = 86.25. Now remove all console.log() and console.table() statements since the debugging is complete.
    Debug statements removed. Code is clean and correct.

SECTION 7

console.log vs. console.table vs. Other Methods

While console.log() and console.table() are the two most frequently used console methods, JavaScript provides several other methods worth knowing about. Each has strengths and limitations that make it suited for different debugging situations.

MethodBest ForStrengthsLimitations
console.log()General-purpose value inspectionWorks with any data type; accepts multiple arguments; universally supportedOutput can be hard to scan with large objects or many logs
console.table()Arrays and arrays of objectsAutomatic formatting; sortable columns; easy visual comparisonNot useful for simple values like numbers or strings
console.warn()Potential issues that aren't errorsDisplays with a yellow warning icon; stands out in consoleFunctionally identical to log except for visual styling
console.error()Actual errors or critical failuresRed text with stack trace; impossible to missOveruse makes real errors harder to spot
console.group()Organizing related log messagesCreates collapsible sections in the consoleRequires matching console.groupEnd() call

Comparison of common console methods

✦

KEY TAKEAWAY

Think of console methods like different tools in a toolbox. console.log() is your trusty screwdriver — it handles most jobs. console.table() is a specialized wrench for structured data — it makes the specific task of inspecting collections dramatically easier. And console.warn() / console.error() are like colored tape that marks important spots so you don't miss them.

SECTION 8

Connection to Advanced Debugging Tools

Console debugging is a fantastic starting point, but as your programs grow more complex, you'll want to explore more powerful tools. The built-in debugger in browser DevTools lets you pause code execution at any line using breakpoints, step through code one line at a time, and inspect every variable without inserting a single log statement. Think of console debugging as the manual approach and the debugger as the power tool.

FeatureConsole DebuggingBuilt-in Debugger (Breakpoints)
Setup timeInstant — just type console.log()Requires clicking to set breakpoints in DevTools
Modifies codeYes — you add and later remove log statementsNo — breakpoints are set in the DevTools, not in your code
Variables inspectableOnly the ones you explicitly logAll variables in scope are visible at the paused line
Step-through executionNot supportedYes — step over, step into, step out controls
Best forQuick checks, logging in loops, verifying data flowComplex logic, hard-to-reproduce bugs, understanding unfamiliar code

Console debugging vs. breakpoint debugging

In practice, experienced developers use both approaches. They start with a quick console.log() to get a general sense of what's happening, and if the bug isn't obvious, they switch to the debugger for a deeper investigation. As you advance in your programming journey, you may also encounter automated testing frameworks like Jest and Mocha that can detect bugs before you even run your program. Console debugging is the foundation — once you master it, every advanced tool will feel like a natural extension.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Why is it better to write console.log("score:", score) instead of just console.log(score)? Explain what could go wrong if you leave out the label, especially when your program has many log statements.
Show answer
Including a label string like "score:" tells you which variable produced that output. Without a label, if you have multiple console.log() statements printing numbers, the console might show something like 5, 10, 15, 20 — and you'd have no idea which value belongs to which variable. Labels act like name tags, making it possible to trace each value back to its source. This is especially critical in large programs or loops where dozens of values might appear in rapid succession.

Question 2: BASIC

Given the code below, what will appear in the console? Write the exact output. let fruits = ["apple", "banana", "cherry"]; console.log("Count:", fruits.length); console.log(fruits[1]);
Show answer
The first console.log prints Count: 3 because the fruits array has three elements and .length returns 3. The second prints banana because array indices start at 0, so index 1 is the second element. The label "Count:" is printed as literal text followed by the number, separated by a space. Remember that console.log() automatically puts a space between comma-separated arguments.

Question 3: INTERMEDIATE

You have this code but the function returns undefined instead of the expected result. Add console.log() statements to find the bug, explain what the bug is, and fix it. function double(num) { let result = num * 2; } console.log(double(5));
Show answer
Adding console.log("result:", result) inside the function before the closing brace would show result: 10, proving the calculation is correct. The bug is that the function never uses return to send the value back to the caller. In JavaScript, a function without a return statement automatically returns undefined. The fix is to add return result; as the last line inside the function. This is a classic bug that console.log() helps catch by showing that the value exists inside the function but never makes it out.

Question 4: APPLIED

You have an array of student objects and need to find which students scored below 70. Write the code using both console.table() and console.log() to (a) display all students in a table, and (b) log only the names of students who scored below 70. let students = [ {name: "Alex", score: 88}, {name: "Jordan", score: 65}, {name: "Taylor", score: 72}, {name: "Morgan", score: 59} ];
Show answer
First, use console.table(students) to display all students in a formatted table, letting you visually scan the scores. Then use a loop: for (let i = 0; i < students.length; i++) { if (students[i].score < 70) { console.log("Below 70:", students[i].name, students[i].score); } }. This combines the conditional logging pattern with labeled output. The console would display the full table first, then two separate log lines: Below 70: Jordan 65 and Below 70: Morgan 59. You could also use console.table(students.filter(s => s.score < 70)) to show only the failing students as a filtered table.

Question 5: CRITICAL THINKING

A classmate argues: "Why bother with console.log() at all? I can just read my code carefully and find the bugs." Write a response that (a) acknowledges when reading code is enough, (b) gives a specific example where console.log() reveals something that's almost impossible to spot by reading, and (c) explains why professional developers still use console debugging daily.
Show answer
Reading code carefully does catch many bugs — especially syntax errors and simple logic mistakes. However, some bugs only appear at runtime with specific data. For example, if a function receives data from user input or an API, you can't predict the exact value by reading the code. A line like let price = item.cost * quantity looks correct, but if item.cost is the string "5" instead of the number 5, JavaScript would concatenate rather than multiply in some contexts. Only console.log(typeof item.cost) reveals this hidden type mismatch. Professional developers use console debugging because real-world programs involve asynchronous operations, external data, and hundreds of interacting functions — the runtime state is simply too complex to track in your head. Console debugging bridges the gap between what you think your code does and what it actually does.

SUMMARY

Lesson Summary

Summary

Console debugging is the practice of inserting temporary output statements into your JavaScript code to observe its behavior at runtime. The console.log() method is the most versatile tool, capable of printing any data type with descriptive labels. For structured data like arrays and arrays of objects, console.table() renders data as a formatted, sortable table that makes patterns and anomalies easy to spot.

Effective debugging follows key patterns: checkpoint logging narrows down which line causes a problem, conditional logging catches unexpected states, loop inspection verifies iteration behavior, and function tracing confirms correct inputs and outputs. Always label your output and clean up debug statements when finished. Console debugging is the foundation upon which more advanced tools like breakpoint debuggers and automated testing are built.

Varsity Tutors • JavaScript • Console Debugging — Use console.log and console.table for debugging