Skip to main content

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:

javascript
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:

javascript
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:

javascript
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:

javascript
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:

javascript
// 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:

javascript
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

javascript
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:

javascript
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:

javascript
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:

javascript
// 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:

javascript
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

javascript
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

  1. Default to const: Start by declaring variables with const. If you later find you need to reassign the variable, change it to let.

  2. Avoid var: In modern JavaScript code, there's rarely a good reason to use var instead of let or const.

  3. Be careful with object constants: Remember that const only prevents reassignment, not mutation of objects or arrays.

  4. Use block scope to your advantage: Limit variable declarations to the smallest scope they're needed in.

  5. Descriptive naming: Use meaningful variable names, especially for constants representing significant values.

javascript
// 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 reassigned
  • const 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

  1. Convert a piece of code using var to use let and const appropriately.
  2. Create a function that demonstrates the difference between function scope and block scope.
  3. Write a small program that uses const for object declaration, then modifies properties of that object.
  4. 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! :)