Javascript JS Foundation & Core Concepts JavaScript Comments

In our last lesson on JavaScript Syntax Fundamentals, we broke down how JavaScript reads your code: statements, expressions, and the whole semicolons debate. You now understand the structural side of JavaScript pretty well.

This lesson is a natural continuation of that, but it’s less about rules the engine enforces, and more about rules you enforce on yourself. Comments are text that JavaScript completely ignores at runtime. No output, no behavior, no effect on how your code runs. And yet, they might be one of the most impactful things you write in any project.

Here’s a situation a lot of developers have been in: you open a file you wrote four months ago, look at some clever-seeming logic, and your first thought is “what does this even do?” That moment is exactly what good commenting prevents. Same story when a teammate opens your code, or when you’re onboarding someone new. The code might be perfect. But if nobody can understand why it works the way it does, it becomes a liability.

Let’s cover all of it: the two comment syntaxes, JSDoc for real documentation, strategies that actually help, and, equally important, what you should stop commenting right now.


01Single-Line JavaScript Comments: Quick Notes That Carry Weight

A single-line comment uses two forward slashes: //. Everything on that line after those slashes is ignored by the JavaScript engine. The syntax is simple, and you’ll find yourself using it the most out of all comment types.

// Calculate the final price after applying the member discount
const finalPrice = originalPrice * 0.85;

let activeSessions = 0; // resets on every server restart, not per user

Notice two placements here. The first comment sits on its own line, above the code it describes. The second sits at the end of the same line. Both are valid. The rule of thumb: use the above-line style when your note needs more than a few words. Use the end-of-line style when it’s short and directly tied to that specific value or variable.

Single-line comments are also the go-to tool when debugging: you can temporarily disable a line by commenting it out.

function loadUserProfile(id) {
  // console.log("Loading profile for:", id); // disabled in production builds
  return fetchFromDatabase(id);
}

Commenting out code while debugging is totally normal. But don’t leave it there permanently. If you’re not using it, delete it. Your version control history (Git) stores every line you’ve ever written, so you can always get it back. Commented-out code that lingers in a file just adds noise. We’ll talk more about this in a bit.


02Multi-Line Comments in JavaScript: When Context Takes More Than One Line

Multi-line comments open with /* and close with */. Everything between those two markers is ignored, no matter how many lines it spans.

/*
  This function handles the complete order processing flow.
  It runs three steps in sequence: inventory check, promotion
  application, and payment processing.

  Important: the inventory check MUST run before payment.
  Running payment first causes ghost charges when items are out of stock.
  This was discovered in production and fixed in commit a3f9d2.
*/
function processOrder(order) {
  checkInventory(order);
  applyPromotions(order);
  processPayment(order);
}

The comment above the function doesn’t just say “this processes an order.” It documents a sequence rule that isn’t obvious from the code, explains why that rule exists, and even references the production incident that led to this decision. That’s the kind of context a developer joining mid-project would genuinely need.

Multi-line comments also work well as section separators in larger files:

/*
  ================================================
  Authentication Module
  Handles login, logout, token refresh, and
  session expiry detection.
  ================================================
*/

You’ll see patterns like this often in real codebases. Long files with multiple responsibilities use these as visual dividers, making it easy to jump to the right section without reading every line.

If you went through our CSS Comments lesson, the /* */ syntax will look familiar. JavaScript borrowed the same block comment format. The only difference is JavaScript also gives you // for single-liners, while CSS doesn’t.

Three JavaScript Comment Types at a Glance
Single-Line
// comment
One line. Quick notes, inline explanations, and disabling code during debugging.
// Resets on server restart
let sessions = 0;
const tax = price * 0.08; // 8% sales tax
Use for short, focused notes near the code they describe.
Multi-Line
/* comment */
Multiple lines. Long explanations, section separators, and complex context.
/*
Inventory must be checked
before payment processing.
Order matters here.
*/
function checkout() { … }
Use for context that needs room to breathe.
JSDoc
/** @tags */
Structured documentation. Works with editors and doc generators. Typed, formal, and powerful.
/**
* @param {number} price
* @returns {number}
*/
function applyTax(price) {
return price * 1.08;
}
Use on all public functions and shared utilities.

03JSDoc Comments: The Right Way to Document JavaScript Functions

Here’s where JavaScript commenting gets genuinely powerful. JSDoc is a documentation format built on top of multi-line comments. The only visual difference is that you open with /** (two asterisks) instead of /*. That extra asterisk signals to editors and doc tools that this block follows JSDoc conventions.

And here’s the key thing that beginner tutorials often skip: JSDoc isn’t just for humans reading a file. It’s for your code editor. VS Code, WebStorm, and most modern editors read JSDoc and show it as a tooltip the moment someone calls your function, anywhere in the project, without needing to open the file where it’s defined.

That’s genuinely useful. Your team can understand what a function expects and returns without context-switching. Let me show you the basic structure:

/**
 * Calculates the discounted price for a user.
 *
 * @param {number} originalPrice - The full price before discount.
 * @param {number} discountRate - A decimal between 0 and 1 (e.g., 0.15 for 15% off).
 * @returns {number} The final price after the discount is applied.
 */
function applyDiscount(originalPrice, discountRate) {
  return originalPrice * (1 - discountRate);
}

The structure breaks down like this: the first line (before any tags) is the plain description. Then you use structured tags starting with @ to document specifics. A developer writing applyDiscount( anywhere in the project will immediately see the parameter names, their types, and what the function returns, right inside their editor.

The JSDoc Tags You Will Actually Reach For

JSDoc has dozens of tags, but a small handful covers the vast majority of real use cases. Here are the ones worth learning first.

@param: Documents each parameter a function accepts. The format is @param {type} name - description. Wrapping the name in square brackets marks it as optional.

/**
 * Sends a welcome email to a newly registered user.
 *
 * @param {string} email - The user's email address.
 * @param {string} name - The user's display name.
 * @param {boolean} [sendAdminCopy=false] - Whether to CC the admin team. Optional.
 */
function sendWelcomeEmail(email, name, sendAdminCopy = false) {
  // ...
}

@returns: Documents the return value. Especially useful when a function might return different types depending on conditions.

/**
 * Looks up a product by its ID.
 *
 * @param {number} productId - The unique product identifier.
 * @returns {Object|null} The product object if found, or null if it doesn't exist.
 */
function findProduct(productId) {
  return catalog.find(p => p.id === productId) || null;
}

@type: Used on variables and properties (not functions). This is how you hint at types without using TypeScript.

/** @type {string[]} */
const allowedRoles = ["admin", "editor", "viewer"];

/** @type {number} */
let retryCount = 0;

@throws: Documents errors a function might throw. This is often overlooked, but if a function can throw under certain conditions, your callers deserve to know that.

/**
 * Parses a raw JSON string into a JavaScript object.
 *
 * @param {string} jsonString - A valid JSON string.
 * @returns {Object} The parsed object.
 * @throws {SyntaxError} If the input is not valid JSON.
 */
function safeParseJSON(jsonString) {
  return JSON.parse(jsonString);
}

@example: Shows real usage. This one is underused and underrated. When someone reads your utility function and just wants to know “what does calling this look like?”, the @example tag gives them that instantly.

/**
 * Formats a number as a localized currency string.
 *
 * @param {number} amount - The numeric value to format.
 * @param {string} [currency="USD"] - ISO 4217 currency code.
 * @returns {string} Formatted currency string.
 *
 * @example
 * formatCurrency(1499.99);           // "$1,499.99"
 * formatCurrency(1499.99, "EUR");    // "€1,499.99"
 * formatCurrency(0);                 // "$0.00"
 */
function formatCurrency(amount, currency = "USD") {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency
  }).format(amount);
}

One more thing worth knowing: JSDoc is also heavily used in TypeScript-adjacent projects. If you ever move into TypeScript (which many developers do), you’ll recognize all of this syntax. The concepts carry over directly.

JS

JSDoc Tag Explorer: Click a Tag to See How It Works

@param
Documents a function parameter
Describes one input the function accepts. Includes the type in curly braces, the parameter name, and a short description. Square brackets mark optional parameters.
@param {type} name – description
@param {type} [name=default] – optional

utils.js

/**
* @param {string} email – User email
* @param {string} name – Display name
* @param {boolean} [notify=true] – Optional
*/
function createUser(email, name, notify = true) {
// …
}
@returns
Documents the return value
Tells readers what the function gives back. Use a union type like {string|null} when the function can return different types depending on conditions.
@returns {type} What is returned
@returns {Object|null} or null if not found

users.js

/**
* @param {number} id – User ID
* @returns {Object|null}
* User object, or null
*/
function findUser(id) {
return db.find(u => u.id === id)
|| null;
}
@type
Annotates a variable or property
Used on variables, not functions. Tells editors what type a variable holds, which enables type-checking and autocomplete without needing TypeScript.
/** @type {type} */
const variableName = value;

config.js

/** @type {string[]} */
const roles = [“admin”, “editor”];
/** @type {number} */
let retryCount = 0;
/** @type {boolean} */
let isLoading = false;
@throws
Documents errors the function can throw
Often skipped, but genuinely important. If a function can throw under certain conditions, callers need to know so they can wrap it in a try/catch when needed.
@throws {ErrorType} When this happens
@throws {RangeError} If value is negative

parser.js

/**
* @param {string} raw – JSON string
* @returns {Object}
* @throws {SyntaxError}
* If string is not valid JSON
*/
function parseJSON(raw) {
return JSON.parse(raw);
}
@example
Shows real usage with output
The most underrated JSDoc tag. Shows a caller exactly what the function looks like in practice, with expected output. Saves time better than any description.
@example
functionName(arg1, arg2); // returns X
functionName(arg3); // returns Y

format.js

/**
* Formats bytes to readable size.
* @param {number} bytes
* @returns {string}
*
* @example
* formatBytes(1024); // “1 KB”
* formatBytes(1048576); // “1 MB”
*/
function formatBytes(bytes) { … }

04JavaScript Commenting Strategies That Actually Help Your Team

Comment the “Why,” Not the “What”

This is the single most valuable rule in all of commenting, and it’s one of those things that sounds simple until you actually start applying it. Your code already shows what it’s doing. What code can never show is why a specific decision was made.

// BAD: Just restates what the code already says clearly
const timeout = 3000; // timeout is 3000

// GOOD: Explains the reasoning, which the code can't communicate on its own
const timeout = 3000; // 3 seconds: minimum required to avoid rate-limiting
                      // on the payment provider's API (their threshold is 2.8s)

The second comment tells a story. It tells you that 3000 isn’t a random number, that changing it could silently break the payment integration, and there’s a specific technical reason behind the value. If someone bumps that to 1000 “for performance,” they’ll break production and not understand why. The comment protects the codebase.

Use TODO, FIXME, HACK, and NOTE as Standard Markers

These four keywords are a widely understood convention in software development. Most editors recognize them, highlight them visually, and let you search for them across the entire codebase. They turn your comments into a lightweight task system.

// TODO: Add server-side input validation before v1 launch
function submitContactForm(data) {
  sendToServer(data);
}

// FIXME: Crashes when the cart array is empty, needs a length check
function getFirstCartItem(cart) {
  return cart[0].name;
}

// HACK: Safari parses date strings differently, this forces consistent behavior
// Revisit when Safari 18 lands with proper date parsing support
const date = new Date(rawDateString.replace(/-/g, "/"));

// NOTE: This function is shared between the mobile and desktop checkout flows
// Changes here affect both. Test both paths before merging.
function calculateShipping(weight, destination) {
  // ...
}

The labels carry meaning. FIXME signals a known bug. HACK signals “this is intentionally awkward, here’s the reason and a plan to fix it.” TODO is a future task. NOTE is context that doesn’t belong in a description but absolutely matters. None of these are vague. They’re promises to the reader.

Keep Comments Close to the Code They Explain

A comment at the top of a 60-line function that explains something happening on line 55 is close to useless by the time you reach line 55. Put comments right where they’re relevant.

function syncUserData(userId) {
  const user = getUser(userId);

  // We fetch a fresh token here because sync operations can run
  // as long background jobs, and cached tokens expire at 15 minutes.
  // Using a stale token causes the upload to fail silently.
  const freshToken = refreshToken(userId);

  return uploadData(user, freshToken);
}

That comment explains exactly why a “fresh” token is fetched rather than using a cached one. It’s right above the line that matters. A developer changing this code has all the context they need without scrolling anywhere.

A Stale Comment Is Worse Than No Comment

When you update what a function does, update the comment too. A comment that no longer reflects reality is an active source of misinformation. It erodes trust in your documentation overall, because once developers find one wrong comment, they start doubting all the others.

This is also a practical argument for keeping your comment count reasonable. The more comments you have, the more comments you have to maintain. Write the ones worth maintaining, and cut the rest.

Comment Quality: Same Function, Two Versions

// function to process checkout
function processCheckout(cart) {
// set total to 0
let total = 0;
// loop through cart items
cart.forEach(item => {
// multiply price by qty and add to total
total += item.price * item.qty;
});
const tax = total * 0.08; // multiply by 0.08
// Old code, kept just in case
// const discount = applyPromo(cart);
// total = total – discount;
return total + tax;
}

The function-level comment just restates the function name. Zero new information added.
Three comments inside the loop translate the code into plain English. Anyone reading this already knows what total += price * qty does.
0.08 has no context. Is that a fixed tax rate? Which region? What if it changes? The comment says nothing useful.
Commented-out dead code sitting in the middle of the function. Confusing, unexplained, and should be in version control history instead.
/**
* Calculates the total for a cart including applicable sales tax.
* Note: All prices are stored in cents. Do NOT divide by 100 here,
* only at the display layer to avoid floating-point drift.
*
* @param {Array<{price: number, qty: number}>} cart – Line items.
* @returns {number} Final total in cents, tax included.
*/

function processCheckout(cart) {
let subtotal = cart.reduce((sum, item) => sum + item.price * item.qty, 0);
// 8% is the flat rate for our primary sales region (US-CA).
// TODO: Replace with a region-aware lookup once we expand internationally.
const tax = Math.round(subtotal * 0.08);
return subtotal + tax;
}
JSDoc documents inputs, output types, and a critical “prices in cents” convention that would otherwise require reading the rest of the codebase to discover.
The 0.08 now has context: it’s not a magic number, it’s a region-specific rate, and there’s an honest TODO acknowledging it needs to scale.
No commented-out code. The logic is clean, the function is half the length, and every comment earns its place.

05What Not to Comment in Your JavaScript Code

Nobody talks about this part enough. Most tutorials tell you what to comment. Far fewer tell you what you should stop writing. But learning this is just as important, because unnecessary comments are noise, and noise trains your team to ignore comments altogether.

Stop Translating Your Code Into English

If anyone can read your code and write the comment themselves without thinking, the comment doesn’t need to exist.

// Avoid these completely:
let count = 0;       // set count to zero
i++;                 // increment i by 1
arr.push(item);      // add item to arr
return result;       // return result

Comments like these fill up your files and train people to skip comments. When everything is commented including the obvious stuff, the genuinely important comments get lost in the noise. They’re not helpful, they’re clutter.

Don’t Leave Commented-Out Code in Your Files

This one is extremely common and it creeps into codebases slowly. You comment out a block of code, tell yourself “I might need this later,” and six months pass. Now it’s sitting there confusing every developer who opens the file.

// This is a codebase smell:
function getUsers() {
  // Old API version, keeping it just in case
  // const res = await fetch("/api/v1/users");
  // return res.json();

  const res = await fetch("/api/v2/users");
  return res.json();
}

If you’re using version control (and the Introduction to JavaScript series assumes you will be), your old code lives in your commit history. You can always get it back. Commented-out code in a file has no reason to be there. Delete it. Your codebase will thank you.

Don’t Let a Wrong Comment Live Longer Than a Day

// Returns the user's first name
function getUserDisplayName(userId) {
  // Function was updated to return full name, but the comment wasn't
  const user = findUser(userId);
  return `${user.firstName} ${user.lastName}`;
}

The comment says “first name.” The code returns the full name. Someone reads this, trusts the comment, and writes code downstream that assumes the value is just a first name. That bug might not show up for a while, and when it does, it won’t be obvious where it came from.

Wrong comments are an active hazard. If you don’t have time to update a comment, the safest move is often to delete it rather than leave it wrong.

Don’t Use Comments to Excuse Unclear Code: Fix the Code Instead

// Avoid this pattern: using a comment to explain a bad variable name
// x is the total number of completed orders in the current billing period
let x = orders.filter(o => o.status === "completed" && o.billingPeriod === current).length;

// Do this instead: make the name carry the meaning
let completedOrdersThisPeriod = orders.filter(
  o => o.status === "completed" && o.billingPeriod === current
).length;

If you find yourself writing a comment to explain what a variable name means, that’s a signal to rename the variable. Good naming is the first line of documentation. Comments exist to explain context that can’t be expressed in the code itself, not to cover for names that don’t do their job.

This connects directly to what we covered in JavaScript Syntax Fundamentals: readable code and readable comments work together. One doesn’t replace the other.


06A Realistic Well-Commented JavaScript Module

Let’s tie everything together with a real example. Not a toy function, but something that looks like an actual module you’d find in a project:

/*
  cart.js
  Manages shopping cart state for the storefront.

  NOTE: All monetary values are stored in cents (integers) to avoid
  floating-point arithmetic issues. Convert to dollars ONLY at the
  display layer, never inside these functions.
*/

/** @type {Array<{id: number, name: string, priceCents: number, qty: number}>} */
let cartItems = [];

/**
 * Adds a product to the cart.
 * If the product already exists, its quantity is increased by 1 instead
 * of creating a duplicate entry.
 *
 * @param {number} id - Product ID from the catalog API.
 * @param {string} name - Display name shown in the cart UI.
 * @param {number} priceCents - Product price in cents (e.g., 1999 for $19.99).
 * @returns {void}
 */
function addToCart(id, name, priceCents) {
  const existing = cartItems.find(item => item.id === id);

  if (existing) {
    existing.qty++;
    return;
  }

  cartItems.push({ id, name, priceCents, qty: 1 });
}

/**
 * Calculates the cart subtotal, optionally applying a discount code.
 * Discount validation happens server-side at checkout. This function
 * only applies known UI-level codes for immediate user feedback.
 *
 * @param {string|null} [discountCode=null] - Optional promo code.
 * @returns {number} Total in cents after discount.
 *
 * @example
 * getCartTotal();            // 4998
 * getCartTotal("SAVE10");    // 4498  (10% off)
 */
function getCartTotal(discountCode = null) {
  const subtotal = cartItems.reduce(
    (sum, item) => sum + item.priceCents * item.qty,
    0
  );

  // Only known UI codes get client-side preview; all codes validated server-side
  const discountRate = discountCode === "SAVE10" ? 0.10 : 0;

  return Math.round(subtotal * (1 - discountRate));
}

// TODO: Implement removeFromCart and clearCart before the v1 launch
// FIXME: Cart state is lost on page refresh; needs localStorage persistence

Every comment in that module earns its place. The file-level note explains a convention that affects every function. The JSDoc documents types, edge cases, and real usage. The inline comment explains a decision that looks suspicious on the surface (client-side discount logic) but is actually intentional. The TODO and FIXME are honest about what’s still missing.

That’s a module you can hand to any developer and they’ll understand the full picture in a few minutes.


07Quick Reference: JavaScript Commenting Rules to Keep Nearby

Here’s a short summary to bookmark:

  • Use // for short, inline notes and temporary debugging toggles.

  • Use /* */ for multi-line explanations, important context blocks, and section separators in long files.

  • Use /** */ JSDoc on every function that’s called from outside its immediate file. Always include @param and @returns.

  • Comment the why, not the what. The code shows what. You explain why.

  • Use TODO, FIXME, HACK, NOTE as structured markers. Be specific inside them.

  • Delete commented-out code. Git has the history.

  • Update or delete wrong comments. A stale comment is misinformation.

  • If you need a comment to explain a variable name, rename the variable instead.


08What’s Coming Next

In the next lesson, we’re covering JavaScript Versions: ES5, ES6, and the ESNext Evolution. If you’ve ever seen code with arrow functions, const and let, or template literals and wondered where those came from, that’s what ES6 brought to JavaScript. It’s one of those lessons that suddenly makes a lot of things click into place.

For now, go back to a piece of code you wrote recently and ask yourself: does every comment here explain something the code can’t express on its own? If yes, you’re already thinking the right way.

The goal isn’t more comments. It’s better ones.

JavaScript Comments

Choose a difficulty to load your challenge

Loading challenge…

Ready to Practice?

Select a difficulty level above to load your challenge.

Easy

1
Live Preview
Great Work!