JavaScript · Beginner
Variables in JavaScript: let, const and var
In short: A variable is a named slot for a value. let declares one you can reassign, const one you cannot, and var is the older form with function-level scope and hoisting. Use const by default, let when the value must change, and avoid var in new code.
Names for values
A variable gives a value a name so later statements can use it. Declaring a variable creates the name; assigning gives it a value. JavaScript has three keywords for declaring, and the difference between them is the subject of this lesson.
let declares a variable whose value may change. const declares one whose name is bound to a value once and cannot be reassigned; it must be given a value on the same line. var is the original keyword from before 2015 and still works, but it behaves in two surprising ways described below, so new code uses let and const.
Both let and const are block scoped: a variable declared inside a pair of braces exists only until the closing brace. A loop counter declared with let in a for header is gone after the loop. var ignores blocks and is scoped to the whole function it sits in (or the whole program), so a var inside an if leaks out of it. var is also hoisted: the engine treats the declaration as if it were at the top of the function, with the value undefined until the assignment line runs. let and const are hoisted too, but reading them before their declaration line throws a ReferenceError instead of quietly giving undefined, which is the better behaviour.
The important subtlety of const is that it freezes the binding, not the value. A const holding an object or an array still lets you change the contents; you just cannot point the name at a different object. A variable declared with let but not assigned holds undefined.
Names may contain letters, digits, underscores and dollar signs, may not start with a digit, and are case sensitive. The convention is camelCase for ordinary names (totalPrice) and UPPER_SNAKE_CASE for true constants such as MAX_SEATS. Reserved words such as let, if and class cannot be names. Assigning to a name that was never declared is an error in strict mode, which is how programs run on this site.
Why prefer const? A reader who sees const knows the name means the same thing from that line to the end of its block and can stop tracking it. Reach for let only where a value genuinely changes: counters, accumulators, a result found by a loop.
Syntax
let count = 0; // declare and assign; may be reassigned
count = count + 1; // reassignment: no keyword
let pending; // declared, holds undefined
const rate = 0.2; // must be assigned here; cannot be reassigned
const cart = []; // the array itself can still change
cart.push("pear"); // allowed: the binding still points at the same array
var legacy = 1; // function scoped, hoisted; avoid in new codeDeclare each variable once, in the smallest block that needs it. A const with an object or array value can still have its contents modified.
let, const and var compared
| let | const | var | |
|---|---|---|---|
| Can be reassigned | yes | no | yes |
| Must be assigned when declared | no | yes | no |
| Scope | block | block | function |
| Read before its declaration line | ReferenceError | ReferenceError | undefined |
| Redeclare in the same scope | SyntaxError | SyntaxError | allowed |
| Use in new code | when the value changes | default | avoid |
Declaring, reassigning and reading an unassigned variable
A ticket count changes, a venue name does not, and a seat is declared before it is known.
let tickets = 3;
console.log("Tickets:", tickets);
tickets = tickets + 2;
console.log("After buying two more:", tickets);
const venue = "Old Mill Theatre";
console.log("Venue:", venue);
let seat;
console.log("Seat before assignment:", seat);
seat = "B12";
console.log("Seat after assignment:", seat);Output
Tickets: 3 After buying two more: 5 Venue: Old Mill Theatre Seat before assignment: undefined Seat after assignment: B12
tickets = tickets + 2 reads the current value, adds two and stores the result back under the same name; only the first line needs the let keyword. venue never changes, so const documents that. seat is declared without a value and holds undefined, the value JavaScript uses for "nothing assigned yet", until the assignment on the next line.
Block scope: let stays inside its braces, var leaks out
The same name is declared with let both outside and inside a block; a var declared inside the block is visible after it.
let area = "lobby";
if (true) {
let area = "stage";
var announced = "yes";
console.log("Inside the block:", area);
}
console.log("After the block:", area);
console.log("var leaked out:", announced);
for (let i = 0; i < 3; i++) {
// i exists only inside this loop
}
console.log("typeof i after the loop:", typeof i);Output
Inside the block: stage After the block: lobby var leaked out: yes typeof i after the loop: undefined
The inner let area is a separate variable that shadows the outer one only between the braces; once the block closes, area means the outer variable again. announced was declared with var, so it ignores the block and is still there afterwards. The loop counter i was declared with let in the loop header and no longer exists after the loop: typeof on a name that does not exist gives the string "undefined" rather than an error, which is why it is used here.
const protects the binding, not the contents
An order object and a price list declared with const are modified in place; reassigning the name fails.
const order = { item: "espresso", qty: 1 };
order.qty = 2;
console.log(order);
const prices = [2.5, 3];
prices.push(4.25);
console.log(prices);
try {
order = { item: "latte", qty: 1 };
} catch (err) {
console.log("Error:", err.message);
}
console.log(order.item);Output
{ item: 'espresso', qty: 2 }
[ 2.5, 3, 4.25 ]
Error: Assignment to constant variable.
espressoChanging order.qty and pushing onto prices are both allowed because the names still refer to the same object and the same array. The line order = { ... } tries to point the name at a new object, which const forbids; the engine throws a TypeError whose message is shown. The try and catch wrapper only exists so the program can print the message and continue; the error handling lesson explains it. The last line confirms order still holds the original object.
Common mistakes
Using a let or const variable above its declaration
Why it goes wrong: The name is in a "temporal dead zone" from the start of its block until the declaration line, and reading it there throws ReferenceError: Cannot access 'total' before initialization.
Fix: Declare variables before the first line that uses them, ideally right where they are first needed.
JavaScript · fixlet total = 0; console.log(total);Expecting const to make an object unchangeable
Why it goes wrong:
constonly stops reassignment of the name.const settings = { dark: false }; settings.dark = true;is perfectly legal and changes the object.Fix: If you need a value that cannot be modified, call
Object.freeze(settings)on it;constalone is not enough.Declaring the same name twice in one scope
Why it goes wrong:
let count = 1; let count = 2;is a SyntaxError and the program does not start. Onlyvartolerates redeclaration, which is one reason it hides bugs.Fix: Declare once, then assign without a keyword:
count = 2;.Assigning to a name that was never declared
Why it goes wrong:
total = 5;with noletorconstanywhere throws ReferenceError in strict mode. Outside strict mode it silently creates a global variable, which is worse because the mistake stays hidden.Fix: Always start a new variable with
letorconst. Programs on this site run in strict mode, so the error shows up immediately.
Where you use this
Almost every exercise keeps a running result while it reads input: a total, a count, the largest value seen so far. Those are the natural let variables, because the loop reassigns them. Everything else, such as the number of lines to read, a price per unit or a parsed input value, is declared with const, so anyone reading the program can tell at a glance which names carry state and which are fixed facts. When a lint tool complains that a let is never reassigned, change it to const.
const n = Number(readline());
let total = 0;
for (let i = 0; i < n; i++) {
const value = Number(readline());
total += value;
}
console.log(total);Key points
constby default;letwhen the value must change; novarin new code.letandconstlive inside the nearest braces;varis scoped to the whole function.- A
constobject or array can still be modified; only the name is fixed. - Reading a
letorconstbefore its declaration line is a ReferenceError. - A declared but unassigned
letholdsundefined. - Names are case sensitive, use
camelCase, and cannot start with a digit.
Try it yourself
The program crashes because it reassigns a const. Fix the declaration so it prints the balance after a withdrawal of 45, then add a deposit of 30 and print the balance again.
const balance = 120;
balance = balance - 45;
console.log(balance);
75
105let balance = 120;
balance = balance - 45;
console.log(balance);
balance = balance + 30;
console.log(balance);
Practise this
Exercises for this lesson are in the JavaScript practice set.
Open the JavaScript playground
Related lessons
- JavaScript Syntax and Your First ProgramHow a JavaScript program is laid out: statements, semicolons, blocks, comments, console.log() for output and readline() for input, with runnable examples.8 min
- JavaScript Data Types: Primitives, Objects and typeofThe seven primitive types in JavaScript plus objects, what typeof reports, how numbers behave, and how to convert between strings, numbers and booleans.9 min
- Scope and Hoisting in JavaScriptHow JavaScript decides where a variable is visible: global, function and block scope, how var and function declarations hoist, and why let has a dead zone.10 min
Frequently asked questions
Should I use let or const in JavaScript?
Use const unless the variable will be reassigned, then use let. Most variables in a typical program never change after their first assignment, so const ends up being the majority, and each let then signals to the reader that a value moves. Neither is faster than the other in practice; the choice is about clarity.
What is the difference between var and let?
var is scoped to the enclosing function and is hoisted with the value undefined, so it can be read before its declaration line and it leaks out of if blocks and loops. let is scoped to the enclosing block and throws if read before its declaration. let was added in ES2015 to fix exactly those two problems, which is why var is avoided in new code.
Does const make a value immutable?
No. const prevents the name from being pointed at another value, but if the value is an object or an array its contents can still be changed. To stop the contents changing as well, use Object.freeze(), which makes the object's own properties read-only (nested objects inside it are not frozen).
How this page was checked. Every program on it was run with Node.js 22 at build time; runs in your browser in an isolated Web Worker by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.