Computer Programming 1 (JavaScript) • Environment, Syntax & Types

let & const — I can declare variables with let and const and explain when each is appropriate.

Master modern JavaScript variable declarations and learn the rules that keep your code safe and predictable.

A JavaScript data file with code brackets and a laptop.
Inside this lesson 10 sections 5 questions 6 figures

SECTION 1

Historical Context & Motivation

When JavaScript was first created in 1995, it had only one keyword for creating variables: var. While var worked, it came with quirky behavior that confused beginners and tripped up experienced developers alike. Variables declared with var could be accidentally overwritten, leaked out of code blocks, and were "hoisted" in unexpected ways. As JavaScript grew from a small scripting language into the backbone of modern web development, the community demanded better tools for managing data.

1995 • JavaScript Is Born

Brendan Eich creates JavaScript in just 10 days at Netscape. The var keyword is the only way to declare variables, and it uses function-level scoping.

2009 • ES5 Standardized

ECMAScript 5 adds strict mode to catch common mistakes, but var remains the sole variable declaration keyword. Developers increasingly voice frustration with its scoping rules.

2015 • ES6 Introduces let & const

ECMAScript 2015 (ES6) adds let and const, providing block scoping and immutable bindings. This is a major leap forward for code safety.

2020+ • Modern Best Practice

Industry style guides (Airbnb, Google) recommend defaulting to const and using let only when reassignment is necessary. The use of var is strongly discouraged.

So what exactly was wrong with var, and how do let and const fix those problems? Understanding this history helps you appreciate why modern JavaScript code looks the way it does—and why your teacher and future employers will expect you to use these newer keywords.

SECTION 2

Core Principles & Definitions

A variable is a named container that holds a value in your program's memory. When you declare a variable, you tell the JavaScript engine to reserve a label for some piece of data. The keyword you choose—let or const—determines the rules that govern how that variable can be used throughout your code.

1

Block Scoping

Both let and const are block-scoped: they exist only within the nearest pair of curly braces { } where they are declared.
2

let — Reassignable Binding

Use let when the value of a variable will need to change after its initial assignment, such as a counter in a loop or a running total.
3

const — Immutable Binding

Use const when the variable should never be reassigned after its initial value is set. This signals intent and prevents accidental overwrites.
4

No Hoisting Surprises

Unlike var, using let or const before their declaration line causes a ReferenceError, helping you catch bugs early.
5

const ≠ Deep Freeze

With const, the binding (the name-to-value connection) cannot change, but if the value is an object or array, its internal contents can still be modified.
✦

KEY TAKEAWAY

Think of const like a permanent marker writing a name on a box—you cannot scratch out that name and write it on a different box. But you can still open the box and rearrange what's inside. let is like a sticky note label—you can peel it off one box and stick it on another whenever you need to.

SECTION 3

Visual Explanation — Block Scope in Action

Block Scope: let & const vs varfunction greet() {var message = "Hello";let count = 0;if (true) {let count = 1; // new block variableconst pi = 3.14; // block-only constant}}Function Scopemessage ✅ (var)count = 0 ✅ (let)pi ❌ (not here)if Block Scopemessage ✅ (var)count = 1 ✅ (let)pi = 3.14 ✅ (const)Outside Functionmessage ❌count ❌pi ❌var leaks through blocks but stops at functions. let & const stop at the nearest { }.The inner "count" (let) is a completely separate variable from the outer "count".Rule: Always prefer the smallest possible scope → use let & const
This diagram shows how block scope works. The let and const variables are confined to the curly-brace block where they are declared, while var ignores block boundaries and only respects function boundaries.

In the diagram above, notice how the inner count (declared with let inside the if block) is a completely separate variable from the outer count. This is called shadowing—the inner variable temporarily "shadows" the outer one within its block. Once the if block ends, the outer count is accessible again with its original value of 0. This predictable behavior is one of the biggest advantages of let and const over var.

SECTION 4

How It Works — Declaration, Initialization & the Temporal Dead Zone

To truly understand let and const, you need to know what happens behind the scenes when JavaScript runs your code. Every variable goes through three phases: declaration (the engine registers the name), initialization (memory is allocated and a default value may be assigned), and assignment (your specified value is stored).

The Temporal Dead Zone (TDZ)

With var, declaration and initialization happen together at the top of the function (this is called hoisting), so you can reference the variable before its line in the code—it will just be undefined. With let and const, the declaration is hoisted to the top of the block, but initialization does not happen until the engine reaches the actual line of code. The gap between the top of the block and the declaration line is called the Temporal Dead Zone (TDZ). Any attempt to read or write the variable in this zone throws a ReferenceError.

Variable Lifecycle: var vs let/constvar behaviorlet / const behaviorHOISTED: Declaration + Initializationvalue = undefined ✅ accessibleconsole.log(x); // undefinedASSIGNMENT LINEvar x = 42;console.log(x); // 42 ✅HOISTED: Declaration onlyno value assigned yet⚠ TEMPORAL DEAD ZONEconsole.log(y);// ReferenceError! ❌INITIALIZATION + ASSIGNMENTlet y = 42;console.log(y); // 42 ✅Key Differencevar gives you "undefined" silently — a hidden bug. let/const throws an error immediately — a helpful guard.Errors that appear early are easier to fix!
The Temporal Dead Zone is the region where let and const variables exist but cannot be accessed. This strict behavior prevents silent bugs that var would allow.
💡

Quick Rule of Thumb

If you try to use a let or const variable before its declaration line, JavaScript will throw a ReferenceError. This is a feature, not a flaw—it forces you to write cleaner, more organized code.

SECTION 5

Choosing Between let and const — A Decision Guide

The single most important question to ask yourself when declaring a variable is: "Will this value ever need to change?" If the answer is no, use const. If the answer is yes—or even maybe—use let. Modern JavaScript style guides recommend defaulting to const because it makes your code easier to reason about. When another developer (or future-you) reads a line with const, they immediately know that binding won't change.

ScenarioKeywordWhy
Storing a user's name from inputconstOnce captured, the name variable doesn't need reassignment in that scope.
A loop counter (i)letThe counter changes with every iteration, so reassignment is required.
A DOM element referenceconstThe reference to the element stays the same, even if you modify the element's content.
A running score totalletThe total is updated each time the user earns points.
An array of to-do itemsconstThe array variable always points to the same array; you push/pop items inside it, not reassign it.
A toggle (true/false flag)letBoolean flags are flipped between true and false, which requires reassignment.

Common scenarios and which keyword fits best

Decision Flowchart: let vs constDeclaring a new variable?Will the variable be reassigned?NOYESconstDefault choice ✦letOnly when neededExamples:const name = "Alice";const colors = ["red", "blue"];Examples:let score = 0;let isLoggedIn = false;
Follow this simple flowchart every time you declare a variable. Start with const as your default, and switch to let only if you know the value must be reassigned.

SECTION 6

Worked Example — Building a Score Tracker

Let's walk through a small program that tracks a player's score in a quiz game. We'll identify which variables should use const and which need let, then observe how block scoping keeps our data safe.

Quiz Score Tracker

  1. 1

    Step 1 — Identify values that never change

    Our quiz has a fixed number of questions and a fixed points-per-question value. These will never be reassigned, so they should be declared with const.
    const totalQuestions = 5; and const pointsPerQuestion = 10;
  2. 2

    Step 2 — Identify values that will change

    The player's score starts at 0 and increases each time they answer correctly. Because the value is reassigned, we need let.
    let score = 0;
  3. 3

    Step 3 — Use block scope in a loop

    Inside a for loop, the counter i changes each iteration, so it uses let. Each iteration also creates a const isCorrect to hold whether the current answer is right—it doesn't change within a single iteration.
    for (let i = 0; i < totalQuestions; i++) { const isCorrect = checkAnswer(i); ... }
  4. 4

    Step 4 — Reassign only when needed

    Inside the loop, if isCorrect is true, we add points to score. The += operator reassigns score, which is why let was the right choice.
    if (isCorrect) { score += pointsPerQuestion; }
  5. 5

    Step 5 — Output the result

    After the loop, we log the final score. The const variable message is created using a template literal. It doesn't need to change, so const is correct.
    const message = `You scored ${score} out of ${totalQuestions * pointsPerQuestion}!`;
ℹ

Notice the Pattern

Out of six variables in this example (totalQuestions, pointsPerQuestion, isCorrect, message, score, i), four used const and only two used let. In real-world codebases, const typically outnumbers let by a wide margin.

SECTION 7

var vs let vs const — Side-by-Side Comparison

Now that you understand let and const individually, let's put all three declaration keywords side by side to crystallize the differences. This comparison table is something you can refer back to anytime you're unsure which keyword to reach for.

Featurevarletconst
ScopeFunction-scopedBlock-scopedBlock-scoped
HoistingYes — initialized as undefinedYes — but in TDZ until declarationYes — but in TDZ until declaration
Reassignment✅ Allowed✅ Allowed❌ Not allowed
Redeclaration✅ Allowed (dangerous!)❌ SyntaxError❌ SyntaxError
Must initialize?No (defaults to undefined)No (can declare then assign later)✅ Yes — must assign a value at declaration
Modern recommendationAvoidUse when neededDefault choice

Complete comparison of JavaScript variable declaration keywords

✦

KEY TAKEAWAY

Think of your code like a chemistry lab. const is a sealed, labeled container—everyone knows exactly what's inside and no one can swap it out. let is a reusable beaker with a label—you can pour out the old contents and add something new. var is an unlabeled container sitting out on the open counter—anyone could grab it, and you might accidentally confuse it with someone else's work. The more specific and controlled your containers are, the fewer accidents you'll have.

SECTION 8

Connection to Advanced Concepts

Understanding let and const is your gateway to more advanced JavaScript concepts. As you progress, you'll encounter topics like closures (functions that "remember" the variables from their enclosing scope), modules (files that export and import named values), and immutable data patterns (programming styles that avoid changing data after creation). All of these build directly on the scoping and reassignment concepts you've learned here.

What You Learned NowWhere It Leads
Block scoping with let / constClosures — functions that capture variables from their surrounding block scope, enabling powerful patterns like private counters and callbacks.
const prevents reassignmentImmutable data patterns — using Object.freeze() and spread operators to prevent changes to object contents, not just bindings.
Declaring at the narrowest scopeES Modules — using export and import to share const values between files while keeping internal variables private.
Temporal Dead Zone awarenessDebugging skills — understanding execution context, the call stack, and how the engine processes your code line by line.

How today's lesson connects to future topics

One particularly important connection is with closures inside loops. A classic JavaScript bug occurs when var is used in a for loop with asynchronous callbacks—all callbacks share the same variable and see only its final value. Replacing var with let fixes this entirely because each loop iteration gets its own block-scoped copy of the counter. You'll explore this in depth when you study closures and asynchronous programming.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why a developer should default to using const instead of let when declaring variables. What advantage does this provide when reading or debugging code?
Show answer
Defaulting to const communicates intent: it tells anyone reading the code that this particular binding will never point to a different value. This makes the code easier to reason about because you don't have to mentally track whether the variable gets reassigned later. If you see let in a codebase that follows this convention, you immediately know to watch for reassignment. It also prevents accidental overwrites—JavaScript will throw a TypeError if you try to reassign a const variable, catching the mistake before it causes a bug.

Question 2: BASIC

For each variable below, decide whether it should use let or const: (a) ___ PI = 3.14159; (b) ___ temperature = 72; where the temperature is updated every minute. (c) ___ greeting = "Hello, World!";
Show answer
(a) const PI = 3.14159; — PI is a mathematical constant that should never change. (b) let temperature = 72; — since the temperature is updated every minute, the variable must be reassigned, which requires let. (c) const greeting = "Hello, World!"; — the greeting string is set once and not changed, so const is the best choice.

Question 3: INTERMEDIATE

What will the following code output, and why? Trace through the execution step by step. let x = 10; if (true) { let x = 20; console.log("inside:", x); } console.log("outside:", x);
Show answer
The output is inside: 20 followed by outside: 10. The let x = 20 inside the if block creates a brand-new variable that shadows the outer x. Within the block, x refers to the inner variable (20). Once the block ends, the inner x is destroyed, and the outer x (still 10) is accessible again. This demonstrates block scoping and variable shadowing.

Question 4: APPLIED

You are building a shopping cart. The cart holds an array of items, and you also track the total price. Write the variable declarations (with appropriate keywords) for: (1) the items array (starts empty), (2) the total price (starts at 0), and (3) a tax rate of 8.25%. Then explain your keyword choices.
Show answer
const items = []; — The array reference never changes; you'll use .push() and .splice() to modify its contents, but you won't reassign the variable to a new array. let totalPrice = 0; — The total is recalculated each time an item is added or removed, requiring reassignment. const TAX_RATE = 0.0825; — The tax rate is a fixed value that should not be modified during the program's execution. Using const with an uppercase name following convention signals to other developers that this is a true constant.

Question 5: CRITICAL THINKING

A classmate says: "const makes things immutable, so if I write const colors = ['red', 'blue']; I can't add 'green' to the array." Is your classmate correct? Explain the difference between an immutable binding and an immutable value. How could you truly prevent the array from being modified?
Show answer
The classmate is incorrect. const creates an immutable binding, meaning the variable name cannot be reassigned to point to a different value (e.g., colors = ['yellow'] would throw a TypeError). However, the value itself—the array—is still mutable. You can call colors.push('green') because that modifies the array's contents without reassigning the variable. To truly make the array immutable, you would use Object.freeze(colors), which prevents adding, removing, or modifying elements. However, Object.freeze() is only a shallow freeze—nested objects inside the array could still be modified.

SUMMARY

Lesson Summary

Summary

JavaScript provides two modern keywords for variable declarations: const for bindings that should never be reassigned, and let for bindings that will change. Both are block-scoped, meaning they exist only within the nearest { } where they are declared. Unlike the older var keyword, both let and const enforce the Temporal Dead Zone, throwing a ReferenceError if you try to access them before their declaration line. This catches bugs early and makes your code more predictable.

The modern best practice is to default to const and only reach for let when you know a variable's value must change—such as loop counters, running totals, or boolean flags. Remember that const prevents reassignment of the binding, not mutation of the value itself—objects and arrays declared with const can still have their contents modified. By mastering let and const, you build a foundation for understanding closures, modules, and immutable data patterns in more advanced JavaScript.

Varsity Tutors • Computer Programming 1 (JavaScript) • let & const