JavaScript Let and Const
Introduction
Prior to ES6 (ECMAScript 2015), JavaScript developers had only one way to declare variables: the var
keyword. While functional, var
had several quirks that often led to unexpected behavior and bugs. ES6 introduced two new variable declaration keywords—let
and const
—which provide more predictable scoping rules and help write cleaner, more maintainable code.
In this lesson, we'll explore:
- The limitations of
var
declarations - How
let
solves scoping issues - When and how to use
const
for constants - Best practices for modern JavaScript variable declarations
Problems with Var
Before diving into let
and const
, let's understand why they were needed in the first place:
1. Function Scope vs. Block Scope
Variables declared with var
are function-scoped, not block-scoped:
function example() {
if (true) {
var x = 10;
}
console.log(x); // Outputs 10, even though x was declared inside the if block
}
example();
This behavior can be confusing and lead to unintended consequences.
2. Hoisting Issues
var
declarations are hoisted to the top of their scope:
console.log(hoistedVar); // Outputs: undefined (not an error!)
var hoistedVar = 5;
// The code above is interpreted as:
// var hoistedVar;
// console.log(hoistedVar);
// hoistedVar = 5;
3. Redeclaration
You can redeclare the same variable multiple times with var
:
var user = "Alice";
// ... 100 lines of code later ...
var user = "Bob"; // No error, original value is silently overwritten
The Let Keyword
The let
keyword was introduced to address the limitations of var
by providing block-scoped variables.
Block Scoping
Variables declared with let
are limited to the block in which they're defined:
function blockScopeExample() {
if (true) {
let blockScoped = "I exist only in this block";
console.log(blockScoped); // Outputs: "I exist only in this block"
}
// This will throw a ReferenceError
// console.log(blockScoped);
}
blockScopeExample();
Temporal Dead Zone
Unlike var
, accessing a let
variable before declaration results in a ReferenceError:
// This will throw a ReferenceError
// console.log(letVariable);
let letVariable = "I cannot be accessed before declaration";
This behavior helps catch potential bugs earlier in development.
No Redeclaration
You cannot redeclare the same variable with let
in the same scope:
let user = "Alice";
// let user = "Bob"; // SyntaxError: Identifier 'user' has already been declared
// However, you can reassign values:
user = "Bob"; // This works fine
The Const Keyword
The const
keyword creates block-scoped constants that cannot be reassigned after declaration.
Basic Usage
const PI = 3.14159;
console.log(PI); // Outputs: 3.14159
// This will cause an error
// PI = 3.14; // TypeError: Assignment to constant variable
Object Constants
An important distinction: while const
prevents reassignment of the variable itself, it doesn't make objects immutable:
const user = {
name: "Alice",
age: 25
};
// This is allowed because we're modifying a property, not reassigning user
user.age = 26;
console.log(user); // Outputs: { name: "Alice", age: 26 }
// This would cause an error:
// user = { name: "Bob" }; // TypeError: Assignment to constant variable
Array Constants
Similarly, arrays declared with const
can have their elements modified:
const colors = ["red", "green", "blue"];
colors.push("yellow"); // This works!
console.log(colors); // Outputs: ["red", "green", "blue", "yellow"]
// But reassignment is not allowed:
// colors = ["purple", "orange"]; // TypeError
Real-World Examples
Example 1: Loop Counter with Let
Using let
in loops creates a new variable for each iteration:
// Each iteration gets its own 'i' variable
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Outputs: 0, 1, 2
// Compare with var:
for (var j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 100);
}
// Outputs: 3, 3, 3 (because all callbacks refer to the same variable)
Example 2: Configuration Objects with Const
A common use case for const
is defining configuration objects:
const API_CONFIG = {
baseURL: "https://api.example.com",
timeout: 5000,
headers: {
"Content-Type": "application/json"
}
};
// Later in the code:
function fetchUserData(userId) {
return fetch(`${API_CONFIG.baseURL}/users/${userId}`, {
timeout: API_CONFIG.timeout,
headers: API_CONFIG.headers
});
}
Example 3: Managing State in Frontend Applications
function updateUserProfile() {
// Use let for variables that will change
let isLoading = true;
let errorMessage = null;
// Use const for reference values that won't be reassigned
const userData = { name: "", email: "" };
// Simulating data fetching
fetchUserData()
.then(data => {
userData.name = data.name; // Modifying properties is fine
userData.email = data.email;
isLoading = false; // Reassigning let variables is fine
})
.catch(error => {
errorMessage = error.message;
isLoading = false;
});
}
Best Practices
-
Default to
const
: Start by declaring variables withconst
. If you later find you need to reassign the variable, change it tolet
. -
Avoid
var
: In modern JavaScript code, there's rarely a good reason to usevar
instead oflet
orconst
. -
Be careful with object constants: Remember that
const
only prevents reassignment, not mutation of objects or arrays. -
Use block scope to your advantage: Limit variable declarations to the smallest scope they're needed in.
-
Descriptive naming: Use meaningful variable names, especially for constants representing significant values.
// Bad
const x = 86400000;
// Good
const MILLISECONDS_IN_DAY = 86400000;
Summary
ES6's let
and const
declarations provide significant improvements over the traditional var
keyword:
let
gives you block-scoped variables that can be reassignedconst
provides block-scoped variables that cannot be reassigned- Both help prevent common bugs related to hoisting and scope
- Both provide clearer intent about how variables are meant to be used
Using these modern variable declarations is a fundamental aspect of writing clean, maintainable JavaScript code.
Additional Resources
Exercises
- Convert a piece of code using
var
to uselet
andconst
appropriately. - Create a function that demonstrates the difference between function scope and block scope.
- Write a small program that uses
const
for object declaration, then modifies properties of that object. - Create a loop using
let
that correctly captures the loop variable in a closure.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)