Web Development · Foundations
Variables
On this page 9 sections
In 30 seconds
In JavaScript, a variable is commonly introduced with let or const. Each declaration A statement that introduces a binding name, such as one beginning with let or const. Full entry → creates a named binding The association a program creates between an identifier and a value in a particular scope. Full entry →: a name the program can use to refer to a value. Use let when the binding will later be assigned a different value. Use const when the binding itself should not be reassigned after initialization. Both are block-scoped, so a name declared inside braces is available only within that block and nested blocks.
Why this matters
Variables give a program a way to keep track of changing information without losing the meaning of each value. The let/const distinction also makes an intention visible to a reader: is this name expected to change, or should a later assignment An operation that gives a new value to an existing assignable binding. Full entry → be treated as a mistake? block scope Visibility limited to a brace-delimited block and any blocks nested inside it. Full entry → prevents temporary names from leaking into unrelated code. These habits make small scripts easier to debug and scale into later JavaScript work, where data moves through functions, browser events, and page updates.
The college version
Names bind to values
JavaScript code needs a way to associate a meaningful name with a value. In this lesson, that association is called a binding. A declaration introduces the name; an initializer The expression that provides a declared binding's first value. Full entry → supplies its first value when one is present. For example, const campus = "North"; declares the binding campus and initializes it with a string value in one statement. let visits; declares a binding without giving it an initial value; JavaScript initially gives that binding the value undefined, and a later assignment such as visits = 1; changes it. The terms matter because declaration, initialization, and assignment are different operations. A program can assign to an already declared let binding, but it cannot declare the same let name twice in the same scope. Precise vocabulary makes error messages easier to interpret and helps a reader see what a line is intended to do. This lesson discusses bindings, not a physical storage box: the useful model is the language rule that a name refers to a value in a particular scope.
Choosing let or const
let and const are lexical declarations. For an introductory rule, choose const when a binding will keep the same assignment, and choose let when the program will later give that binding another value. For example, a score that changes after a correct answer can be introduced with let score = 0; and later updated with score = score + 1;. A label that the program does not reassign can use const label = "Practice";. A const declaration must have an initializer, so const label; is an error. Once a const binding is initialized, attempting label = "Quiz"; is also an error. This rule concerns the binding, not a broad promise that every value associated with it is permanently unchanged; later lessons on objects and arrays introduce a separate distinction about changing properties or elements. Here, keep the question narrow: will this name receive a new assignment? If yes, use let; if no, const communicates that plan and lets JavaScript reject an accidental reassignment An assignment that replaces the value previously associated with a binding. Full entry →.
Block scope controls where a name works
A block is code surrounded by braces, as in { ... }. Bindings declared with let and const are block-scoped. A binding introduced inside a block can be used in that block and in blocks nested within it, but not after the block closes. This supports local reasoning. A short-lived calculation can have a temporary name without risking a collision with code after the block. Consider let message = "outside"; { let message = "inside"; }. The second declaration creates a distinct inner binding. While execution is inside the braces, message names the inner value; after the braces, it names the outer value again. This is shadowing A nested declaration using the same name as an outer binding, so the inner binding is selected within its scope. Full entry →: an inner name temporarily hides an outer binding with the same spelling. Shadowing is legal in nested scopes but can make code harder to read, so use it intentionally and favor descriptive names when the relationship is not obvious. A binding declared within the braces does not become available outside them merely because code in the block ran.
Declaration order and the temporal dead zone
Lexical scope is not permission to use a binding before its declaration has been evaluated. Within the scope of a let or const declaration, the name is in a temporal dead zone The interval after a lexical scope begins but before a particular let or const declaration is evaluated, when that binding cannot be accessed. Full entry → from the start of that scope until the declaration executes. Reading the name during that interval throws a ReferenceError. For example, { console.log(total); let total = 4; } does not print undefined; it fails before the declaration initializes total. Put declarations before code that reads them. This ordering rule is one reason let and const avoid a class of confusing behavior associated with older declaration patterns. It also gives a practical debugging checklist: if a name is reported as unavailable even though a declaration appears later in the same block, look for a use above the declaration. The temporal-dead-zone idea does not mean the name is invisible forever. It means the block has begun to reserve that name, but the binding is not ready for access until its declaration has run.

Eli explains
The same idea, in plain words
Explain it like I’m 10
A variable name is like a label a program uses when it needs to find a value again. let is a label the program plans to replace later, such as a score that starts at zero and then goes up. const is a label the program promises not to replace, such as the title chosen for a page. Both kinds of labels follow the room made by curly braces. A label made inside one room is not on the table outside that room. If there is a smaller room inside, it can use a label with the same spelling, but while you are in the smaller room, that inner label is the one you see.
Picture it like this
Imagine color-coded sticky notes on folders. A let note can be moved to another folder later. A const note is attached with a rule that says it cannot be moved to a different folder. Curly braces make a temporary work area: sticky notes placed inside are usable there but are removed from the shared desk when the work area closes. A smaller work area can use a note with the same word, temporarily covering the view of the older one.
Where the picture stops working
JavaScript bindings are language-defined relationships, not physical notes. In particular, the analogy should not suggest that const makes every possible aspect of a value immutable; this lesson only establishes that the binding cannot be reassigned. JavaScript also enforces scope and early access rules mechanically, whereas people can read a sticky note before a meeting starts.
Worked example
Run this JavaScript in order: let score = 0; score = score + 1; const course = "Web Basics"; { let score = 10; console.log(score); } console.log(score); console.log(course);. The first assignment changes the outer score from 0 to 1, which is permitted because it was declared with let. The block then declares a separate inner score with value 10; the first log prints 10. When the block ends, that inner binding is no longer selected, so the next log prints the unchanged outer value 1. The final log prints Web Basics. Replacing the final course use with course = "Advanced Web"; would throw because a const binding cannot be reassigned. The example was executed with Bun: its output was 10, 1, and Web Basics on separate lines.
Key takeaway
Use let for a binding that will be reassigned and const for one that will not. Both are block-scoped, so declare names before using them and keep track of the braces that define where each name is available.
Quick check
3 questions here, of 5 in this lesson’s practice set. Answers stay hidden until you check.
Which statement correctly describes a
Which change is permitted after this declaration: let count = 2;?
What happens when this code runs? let label = "outer"; { let label = "inner"; } console.log(label);
Study tools & related lessonsYou’ll learn to · Common mistakes · Easily confused · Key vocabulary · Related
You’ll learn to
- Define a JavaScript binding and distinguish declaration from initialization.
- Choose
letorconstfor a simple binding based on reassignment intent. - Explain why a
constbinding cannot receive a new assignment. - Trace the visibility of a
letorconstname across nested blocks. - Identify a temporal-dead-zone error caused by using a lexical binding too early.
Common mistakes
Using
constfor a name that the program will deliberately update.Use
letwhen a later assignment is part of the design; otherwise the later assignment will fail.Thinking
letandconstnames declared inside braces exist after the closing brace.Treat the block as the boundary: a lexical binding is usable in its block and nested blocks, not outside it.
Expecting a
letorconstname used before its declaration to behave as if it were initialized toundefined.Put the declaration before the first use; early access is a temporal-dead-zone
ReferenceError.Assuming the inner and outer uses of the same name are automatically one binding.
A nested declaration creates an inner binding that shadows the outer one only within the nested block.
Easily confused
`let` vs. `const`
A let binding may receive a new assignment; a const binding must be initialized and may not be reassigned.
Declaration vs. Assignment
A declaration introduces a binding name, while an assignment gives a value to an already existing assignable binding.
Outer binding vs. Shadowing inner binding
The outer binding is selected outside the nested block; the inner binding with the same name is selected while execution is inside its own block.
Key vocabulary
- binding
- The association a program creates between an identifier and a value in a particular scope.
- declaration
- A statement that introduces a binding name, such as one beginning with
letorconst. - initializer
- The expression that provides a declared binding's first value.
- assignment
- An operation that gives a new value to an existing assignable binding.
- reassignment
- An assignment that replaces the value previously associated with a binding.
- block scope
- Visibility limited to a brace-delimited block and any blocks nested inside it.
- shadowing
- A nested declaration using the same name as an outer binding, so the inner binding is selected within its scope.
- temporal dead zone
- The interval after a lexical scope begins but before a particular
letorconstdeclaration is evaluated, when that binding cannot be accessed.
Sources & references
- let - JavaScript | MDN — MDN Web Docs
- const - JavaScript | MDN — MDN Web Docs
- ECMAScript Language Specification — Lexical Declarations — Ecma International / TC39
EliExplains lessons are original prose written from the open, credible references above. See Copyright & Licensing.
Researched 2026-08-20
Educational content only. It is not medical, legal or professional advice. Found an error? Tell us.

