JavaScript Comments
Comments are notes you leave in your code for humans — your teammates, your future self, or the maintainer six months from now who has no idea what the original author meant. The JavaScript engine completely ignores them. Used well, they clarify intent; used badly, they become outdated lies that mislead readers.
The Two Syntaxes
JavaScript has exactly two comment styles:
// Single-line comment — from // to end of line
/* Multi-line comment —
can span as many lines
as you need */
| Style | When to Use |
|---|---|
// … |
Quick notes, end-of-line annotations, most everyday comments |
/* … */ |
Multi-line explanations, license headers, temporarily disabling blocks |
Single-Line Comments
Everything after // to the end of the line is ignored.
// Calculate the tax owed at the current bracket
const tax = income * 0.22;
const tax = income * 0.22; // assumes 22% bracket — update yearly
You can comment out a single line of code while debugging:
// console.log("debug:", user); // temporarily silenced
runJob(user);
Multi-Line Comments
/* … */ is useful for paragraphs of explanation or for disabling a chunk of code:
/*
Pricing rules:
- Base price = unit cost * quantity
- Discount of 10% if quantity >= 100
- Discount of 15% if customer is on the "gold" tier
*/
function priceFor(unit, qty, tier) {
let total = unit * qty;
if (qty >= 100) total *= 0.9;
if (tier === "gold") total *= 0.85;
return total;
}
/* Disable the legacy retry path until we redesign it
function legacyRetry(fn) {
...
}
*/
Warning: Multi-line comments don’t nest. /* outer /* inner */ outer */ ends at the first */ and leaves the rest as syntax garbage. Use // per line if you need to comment out code that already contains /* */.
JSDoc — Structured Doc Comments
JSDoc is a convention that turns /** … */ comments into machine-readable documentation. Editors like VS Code parse JSDoc to give you intellisense, type hints, and hover docs — even in plain JavaScript.
/**
* Calculates the total price for an order.
*
* @param {number} unit Per-unit cost in dollars
* @param {number} qty Quantity ordered
* @param {"bronze"|"silver"|"gold"} tier Customer loyalty tier
* @returns {number} Total price after discounts
*/
function priceFor(unit, qty, tier) {
// ...
}
Common JSDoc tags:
| Tag | Purpose |
|---|---|
@param |
Document a function parameter |
@returns |
Document the return value |
@throws |
Errors the function can throw |
@example |
A usage example |
@deprecated |
Mark API as deprecated |
@see |
Cross-reference to related code |
@todo |
Pending work |
@type |
Annotate a variable’s type |
/** @type {Map<string, number>} */
const counts = new Map();
/**
* @deprecated Use {@link fetchUser} instead.
*/
function getUser(id) { /* ... */ }
Tip: Even without TypeScript, JSDoc gives you ~80% of the editor support. It’s the cheapest way to make your codebase self-documenting.
When NOT to Comment
The best code is self-documenting — clear names, small functions, sensible structure. A comment that just restates the code is noise.
// ❌ Useless — the code already says this
// Increment counter by 1
counter = counter + 1;
// ❌ Noise — the function name is the comment
// Get the user's name
function getUserName() { return user.name; }
// ❌ Lies waiting to happen — code drifts, comment doesn't
// Loops 5 times
for (let i = 0; i < 10; i++) { ... } // someone changed 5 → 10
Comment the WHY, Not the WHAT
The code shows the what. The comment should explain the why.
// ✅ Explains a non-obvious decision
// Use a 250ms debounce — anything faster causes the API to throttle us
const search = debounce(fetchResults, 250);
// ✅ Explains a workaround
// Safari 14 returns NaN here instead of throwing; coerce to 0
const value = parseFloat(raw) || 0;
// ✅ Explains business logic
// Free trial extended to 30 days for users who signed up before 2026-01-01
if (signupDate < new Date("2026-01-01")) trialDays = 30;
TODO / FIXME / NOTE Conventions
Most teams adopt three tag prefixes that IDEs highlight:
| Tag | Meaning |
|---|---|
// TODO: |
Future work — not blocking |
// FIXME: |
Known bug — needs fixing |
// HACK: |
Intentional ugly workaround |
// NOTE: |
Important context worth flagging |
// XXX: |
Danger / “here be dragons” |
// TODO: support multi-currency once the FX service ships
// FIXME: race condition when two saves overlap (CXPL-1234)
// HACK: workaround for Safari 14 IndexedDB bug
// NOTE: this order matters — auth must run before logging
Commenting Out Code While Debugging
Both styles work, with trade-offs:
// Approach 1: line-by-line with //
// console.log("a");
// console.log("b");
// console.log("c");
// Approach 2: block with /* ... */
/*
console.log("a");
console.log("b");
console.log("c");
*/
Tip: Most editors have a “toggle line comment” shortcut (Cmd+/ on Mac, Ctrl+/ on Windows). It uses // and handles selections cleanly. Use it.
Comments in HTML vs JavaScript
A subtle gotcha when JavaScript is embedded in HTML:
<!-- This is an HTML comment -->
<script>
// This is a JavaScript single-line comment
/* This is a JavaScript multi-line comment */
<!-- ❌ This is NOT a JavaScript comment! -->
</script>
| Where | Single-line | Multi-line |
|---|---|---|
| HTML | — | <!-- ... --> |
| CSS | — | /* ... */ |
| JavaScript | // |
/* ... */ |
License Headers
Many projects put a license block at the top of source files:
/*!
* MyLibrary v2.4.1
* Copyright (c) 2026 Example Corp.
* Released under the MIT License.
*/
The leading /*! is a convention: most minifiers preserve comments that start with /*! so the legal notice survives the build.
Real-World Example
Here’s what well-commented production code looks like:
/**
* Polls an endpoint until it returns a non-202 status,
* with exponential backoff capped at 30s between attempts.
*
* @param {string} url URL to poll
* @param {object} options
* @param {number} options.maxAttempts Default 10
* @returns {Promise<Response>}
*/
async function pollUntilDone(url, { maxAttempts = 10 } = {}) {
let delay = 500;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const res = await fetch(url);
// 202 means "still processing" — keep waiting
if (res.status !== 202) return res;
await new Promise(r => setTimeout(r, delay));
// Exponential backoff, but capped so we don't wait forever
delay = Math.min(delay * 2, 30_000);
}
// TODO: surface a structured "TimeoutError" instead of throwing a string
throw new Error(`Polling timed out after ${maxAttempts} attempts`);
}
Every comment earns its place: the JSDoc enables editor tooltips, the inline // 202 means… explains a magic number, the // Exponential backoff… clarifies intent, and the // TODO is honest about a known gap.
Best Practices Checklist
- ✅ Use
//for everyday inline comments - ✅ Use
/** … */JSDoc for public functions and exported APIs - ✅ Explain why, not what — let code show the what
- ✅ Use
TODO:/FIXME:consistently so they’re greppable - ✅ Update comments when you change the code they describe
- ❌ Don’t restate the obvious
- ❌ Don’t leave commented-out code in committed files —
gitremembers - ❌ Don’t write comments that lie (an outdated comment is worse than no comment)
What’s Next?
Comments document your code, but the real building blocks of a program are variables. Next we’ll see how to declare them with var, let, and const, and why two of those should never be used.