Tutorials Logic, IN info@tutorialslogic.com

JavaScript Comments and Variables: const, let, var, and Scope

JavaScript Bindings

Comments explain decisions that code alone cannot make obvious. Variables give names to values so a program can read input, calculate results, track state, and pass data between operations. These two features appear in almost every JavaScript file, so using them clearly prevents confusion from the beginning.

Use const for a binding that will not be reassigned. Use let when the binding must receive a different value later. Treat var as legacy syntax that you still need to recognize because it has function scope and different hoisting behavior. Choose the narrowest useful scope and a name that describes the value rather than its type or storage location.

JavaScript Comment Syntax

A line comment begins with // and continues to the end of that line. A block comment begins with /* and ends with */. JavaScript ignores comment text while parsing executable statements, but comments inside strings and template literals remain ordinary text.

Write a comment when the reason for a decision, constraint, workaround, unit, or side effect is not clear from the code. Do not translate every statement into English. A comment that only says "increase count" above count += 1 adds noise and can become incorrect when the code changes.

  • Use // for short explanations beside or immediately above the relevant statement.
  • Use /* ... */ for a compact multi-line note, license header, or tool directive.
  • Keep comments close to the behavior they explain and update them with the code.
  • Use documentation syntax such as /** ... */ only when a documentation tool or editor type hint benefits from it.
  • Do not store passwords, tokens, personal data, or private operational details in comments.

Line and Block Comments

Line and Block Comments
// Prices arrive in cents to avoid floating-point rounding errors.
const priceInCents = 1299;

/*
 * The API permits at most three attempts for this operation.
 * Keep this value aligned with the server-side limit.
 */
const maxAttempts = 3;

const message = "// This is text, not a comment";
console.log(priceInCents, maxAttempts, message);

Declare Variables with const and let

A variable declaration creates a binding between an identifier and a value. The value may be a number, string, object, function, or any other JavaScript value. The binding is what const protects: const prevents assigning a different value to the same name, while let permits reassignment.

Declare a variable near its first use and inside the smallest block that needs it. This limits accidental changes and makes dependencies easier to see. Initialize the declaration immediately when a meaningful value is available; an uninitialized let binding contains undefined after its declaration executes.

  • Start with const when writing a declaration.
  • Change const to let only when reassignment is part of the intended algorithm.
  • Declare one logical value per statement so changes and debugging remain clear.
  • Avoid assigning a new type to the same let binding; use a separate, descriptive name after conversion.

Declarations and Reassignment

Declarations and Reassignment
const taxRate = 0.18;
const subtotal = 2500;
let status = "pending";

status = "paid"; // valid: status was declared with let

const tax = subtotal * taxRate;
const total = subtotal + tax;

console.log({ status, subtotal, tax, total });
// { status: "paid", subtotal: 2500, tax: 450, total: 2950 }

const vs let vs var

const and let are block-scoped. A binding declared inside an if statement, loop, or standalone block is unavailable outside that block. var is function-scoped, so a var declaration inside a block remains visible throughout the containing function. This wider scope is a common source of accidental reuse in older code.

All three declarations are processed before execution reaches their line, but their access rules differ. Reading let or const before the declaration throws a ReferenceError because the binding is in the temporal dead zone. Reading var before its declaration returns undefined, which can hide an ordering mistake.

Declaration Scope Reassign Redeclare in same scope Before declaration Recommended use
const Block No No ReferenceError Default for bindings that do not change
let Block Yes No ReferenceError Counters, state transitions, and accumulators
var Function Yes Yes undefined Maintain existing legacy code when required

Block Scope and Function Scope

Block Scope and Function Scope
function inspectScope(isReady) {
  if (isReady) {
    const message = "Ready";
    let attempts = 1;
    var legacyFlag = true;

    attempts += 1;
    console.log(message, attempts); // Ready 2
  }

  console.log(legacyFlag); // true: var escaped the if block
  // console.log(message); // ReferenceError
  // console.log(attempts); // ReferenceError
}

inspectScope(true);

Reassignment Is Not Object Mutation

A const binding cannot point to a different value after initialization. However, an object or array stored in that binding may still be mutable. Updating a property, pushing an item, or deleting a key changes the value itself without reassigning the binding.

Use Object.freeze when shallow runtime protection is useful, but remember that nested objects remain mutable unless they are frozen as well. In application code, immutability is often maintained by creating a new object with spread syntax instead of modifying shared state in place.

  • const user = {...} prevents user = anotherUser, not user.name = "Asha".
  • Prefer a new array or object when code relies on change detection or predictable state updates.
  • Use let for binding reassignment, not merely because an object may change internally.

Mutation and Immutable Updates

Mutation and Immutable Updates
const user = { name: "Mira", role: "viewer" };
user.role = "editor"; // valid mutation

// user = { name: "Mira" }; // TypeError: reassignment

const updatedUser = {
  ...user,
  role: "admin"
};

console.log(user.role);        // editor
console.log(updatedUser.role); // admin

Hoisting and the Temporal Dead Zone

A declaration is hoisted in the sense that JavaScript creates its binding while preparing the scope. That does not mean every binding is safe to read before the declaration statement. let and const stay inaccessible from the beginning of their block until execution reaches the declaration. This interval is called the temporal dead zone.

Function declarations have their own hoisting behavior and can usually be called earlier in the same scope. Function expressions stored in const or let follow the access rules of that variable declaration. Keep declarations before their first use even when a language rule technically permits another order.

  • A temporal-dead-zone error usually means code read a binding too early or shadowed an outer variable.
  • var returning undefined before its assignment does not mean the intended value exists.
  • Do not use hoisting as an application control-flow technique.

Access Before Declaration

Access Before Declaration
console.log(legacyTotal); // undefined
var legacyTotal = 20;

// console.log(modernTotal); // ReferenceError
const modernTotal = 20;

function calculateTotal(price, quantity) {
  return price * quantity;
}

console.log(calculateTotal(modernTotal, 2)); // 40

Variable Naming Rules

An identifier can start with a letter, underscore, or dollar sign. Later characters may also contain digits. JavaScript identifiers are case-sensitive, cannot contain spaces or hyphens, and cannot be reserved keywords such as const, class, return, or import. Unicode letters are valid, but teams commonly use ASCII names for consistency across tools and keyboards.

A valid name is not automatically a useful name. Prefer a name that communicates meaning, unit, and state. userCount is clearer than n, timeoutMs is clearer than timeout, and isAuthenticated is clearer than flag. Avoid encoding a type that may change, such as userArray, when users describes the domain adequately.

  • Use camelCase for variables and functions: orderTotal and calculateTax.
  • Use PascalCase for classes and constructor-like components: UserAccount.
  • Use UPPER_SNAKE_CASE selectively for fixed configuration constants: MAX_RETRIES.
  • Start booleans with is, has, can, should, or another predicate: hasAccess.
  • Use short names such as i only where their meaning is obvious and their scope is very small.

Valid and Descriptive Names

Valid and Descriptive Names
const customerName = "Ravi";
const orderItems = ["book", "pen"];
const MAX_RETRIES = 3;
let retryCount = 0;
let isPaymentComplete = false;

// Invalid declarations:
// const 2ndUser = "...";  // starts with a digit
// const user-name = "..."; // hyphen is subtraction
// const class = "...";     // reserved keyword

console.log({ customerName, orderItems, retryCount, isPaymentComplete });

Shadowing and Global Variables

Shadowing occurs when an inner scope declares the same identifier as an outer scope. JavaScript then resolves the inner name until that block ends. Intentional shadowing can be valid, but repeated names make it easy to update or inspect the wrong value.

Top-level declarations live longer and are accessible to more code than local declarations. In classic browser scripts, some var declarations also become properties of window, while top-level let and const do not. JavaScript modules have their own module scope. Keep values local and export only the bindings another module actually needs.

  • Avoid implicit globals created by assigning to an undeclared name; strict mode reports them as errors.
  • Do not reuse configuration names for temporary local values.
  • Prefer function parameters and return values over reading and changing shared global state.
  • Use lint rules such as no-undef and no-shadow to catch scope mistakes early.

Keep State Inside the Function

Keep State Inside the Function
const currency = "INR";

function formatInvoice(amount) {
  const formattedAmount = amount.toFixed(2);
  return `${currency} ${formattedAmount}`;
}

console.log(formatInvoice(1250)); // INR 1250.00

// total = 1250; // ReferenceError in strict mode: undeclared name

Comments And Variables Failure Cases

When a declaration fails, read the exact error first. "Identifier has already been declared" means the same let or const name exists in that scope. "Cannot access before initialization" points to the temporal dead zone. "Assignment to constant variable" means code attempted to reassign a const binding. "x is not defined" means the name is misspelled, outside its scope, or was never declared.

Inspect the smallest relevant scope and search for every declaration and assignment of the name. Browser developer tools can pause at the failing line and show local, block, closure, module, and global bindings. A linter catches undeclared variables and suspicious var usage before the code runs.

  • Check spelling and letter case because total, Total, and TOTAL are different identifiers.
  • Check whether braces ended the block where a let or const binding was declared.
  • Check whether a const binding is being reassigned when only an object property should change.
  • Check declaration order when a ReferenceError mentions initialization.
  • Check repeated script loading when the browser reports a duplicate top-level declaration.

Practical Variable Pattern

The following example uses const for input and derived values, let for a status that intentionally changes, descriptive names with units, and comments only where they explain a business rule. This is the declaration style to aim for in everyday JavaScript.

Calculate an Order Total

Calculate an Order Total
const itemPriceInCents = 799;
const quantity = 3;
const freeShippingThresholdInCents = 2000;

const subtotalInCents = itemPriceInCents * quantity;
const hasFreeShipping = subtotalInCents >= freeShippingThresholdInCents;

// Standard shipping is charged only below the published threshold.
const shippingInCents = hasFreeShipping ? 0 : 150;
const totalInCents = subtotalInCents + shippingInCents;

let orderStatus = "draft";
orderStatus = "ready";

console.log({
  orderStatus,
  subtotalInCents,
  shippingInCents,
  totalInCents
});
Before you move on

JavaScript Comments and Variables: const, let, var, and Scope Mastery Check

8 checks
  • Use comments to explain decisions, constraints, units, and non-obvious side effects.
  • Choose const by default and let only when reassignment is intentional.
  • Recognize var as function-scoped legacy syntax and avoid adding it to new code.
  • Keep each binding inside the smallest block or function that needs it.
  • Remember that const prevents reassignment but does not make objects deeply immutable.
  • Declare bindings before use and treat temporal-dead-zone errors as ordering or shadowing problems.
  • Use valid, descriptive names that communicate meaning, state, and units.
  • Run a linter to catch undeclared names, accidental globals, shadowing, and unnecessary var declarations.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.