Block Scoping and Constants in JavaScript ES6: let and const

The let Declaration

Basic Usage

The let keyword introduces block-scoped variables. Unlike var, variables declared with let are not accessible outside their containing block.

// Example with var
{
  var globalVar = 'leaks';
}
console.log(globalVar); // Outputs: leaks

// Example with let
{
  let blockScoped = 'does not leak';
}
console.log(blockScoped); // ReferenceError: blockScoped is not defined

Hoisting Differences

Variables declared with var are hoisted to the top of their scope and initialized with undefined. let declarations are also hoisted but not initialized, leading to a ReferenceError if accessed before declaration (Temporal Dead Zone).

// var hoisting
console.log(varExample); // Outputs: undefined
var varExample = 'hoisted';

// let hoisting (TDZ)
console.log(letExample); // ReferenceError: Cannot access 'letExample' before initialization
let letExample = 'not hoisted';

Temporal Dead Zone (TDZ)

The TDZ is the period from the start of a block until a let variable is declared. Accessing the variable in this zone results in an error.

{
  // Entering TDZ for 'tdzVar'
  // console.log(tdzVar); // Would throw ReferenceError
  let tdzVar = 'safe';
  // Exiting TDZ
  console.log(tdzVar); // Outputs: safe
}

No Redeclarasion

let does not allow redeclaring a variable within the same scope, even with var.

let uniqueId = 10;
// let uniqueId = 20; // SyntaxError: Identifier 'uniqueId' has already been declared

var anotherId = 30;
// let anotherId = 40; // SyntaxError: Identifier 'anotherId' has already been declared

Block-Level Scope in ES6

Issues Without Block Scope

Prior to ES6, JavaScript had function and global scopes. This led to common issues like variable leakage and unexpected hoisting.

// Loop variable leakage
var collectedFuncs = [];
for (var i = 0; i < 3; i++) {
  collectedFuncs.push(function() { return i; });
}
console.log(i); // Outputs: 3 (leaked!)
console.log(collectedFuncs[0]()); // Outputs: 3, not 0

// Hoisting in conditional
var testValue = 5;
function checkScope() {
  if (false) {
    var testValue = 10;
  }
  console.log(testValue); // Outputs: undefined (due to hoisting)
}
checkScope();

Implementing Block Scope with let

Using let in loops and conditionals creates a new binding for each iteration, preventing leakage.

function demonstrateBlockScope() {
  let outerVar = 'visible outside';
  if (true) {
    let innerVar = 'only inside';
    console.log(outerVar); // Outputs: visible outside
  }
  // console.log(innerVar); // ReferenceError: innerVar is not defined
  console.log(outerVar); // Outputs: visible outside
}
demonstrateBlockScope();

The const Declaration

Basic Usage

const declares a read-only reference to a value. The varible identifier cannot be reassigned. It must be initialized upon declaration and is block-scoped.

// Correct usage
const MAX_SIZE = 100;
console.log(MAX_SIZE); // Outputs: 100

// Attempting reassignment
// MAX_SIZE = 200; // TypeError: Assignment to constant variable.

// Must be initialized
// const UNINITIALIZED_CONST; // SyntaxError: Missing initializer in const declaration

Underlying Principle

const prevents reassignment of the variable identifier, but does not make the assigned value immutable. For objects and arrays, their contents can be modified.

// With objects
const userProfile = { name: 'Alice' };
userProfile.name = 'Bob'; // Allowed
console.log(userProfile); // Outputs: { name: 'Bob' }
// userProfile = { name: 'Charlie' }; // TypeError: Assignment to constant variable.

// With arrays
const numberList = [1, 2, 3];
numberList.push(4); // Allowed
console.log(numberList); // Outputs: [1, 2, 3, 4]
numberList[0] = 99; // Allowed
console.log(numberList); // Outputs: [99, 2, 3, 4]
// numberList = [5, 6, 7]; // TypeError: Assignment to constant variable.

Deep Freezing

Object.freeze() can make an object immutable, but it only performs a shallow freeze. Nested objects remain muttable.

const shallowFrozen = Object.freeze({ key: 'value' });
// shallowFrozen.key = 'new value'; // Fails silently in non-strict mode, throws in strict mode

Tags: javascript ES6 let const Block Scope

Posted on Sun, 10 May 2026 22:47:34 +0000 by Adam W