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
vardeclarations - How
letsolves scoping issues - When and how to use
constfor 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 usevarinstead ofletorconst. -
Be careful with object constants: Remember that
constonly 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:
letgives you block-scoped variables that can be reassignedconstprovides 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
varto useletandconstappropriately. - Create a function that demonstrates the difference between function scope and block scope.
- Write a small program that uses
constfor object declaration, then modifies properties of that object. - Create a loop using
letthat correctly captures the loop variable in a closure.
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)