Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Block-Scoped Declarations: let vs const in JavaScript

Tech 1

Unlike var, identifiers declared with let are not initialized during the compilation phase. Accessing them before the declaration line triggers a ReferenceError. This behavior eliminates traditional hoisting and introduces the Temporal Dead Zone (TDZ).

// Traditional var hoisting
console.log(alpha); // undefined
var alpha = 10;

// let TDZ behavior
console.log(beta); // ReferenceError: Cannot access 'beta' before initialization
let beta = 20;

let enforces strict block-level scoping. Variables declared inside {} remain isolated from outer scopes, preventing accidental overwrites and loop counter leakage.

// Scope isolation example
function processItems() {
  let counter = 5;
  if (true) {
    let counter = 15; // Shadows outer counter
    console.log(counter); // 15
  }
  console.log(counter); // 5
}

Loop variables declared with let are recreated per iteration and do not pollute the enclosing scope:

for (let idx = 0; idx < 3; idx++) {
  // iteration logic
}
console.log(typeof idx); // "undefined"

Redeclaring an identifier within the same lexical environment is strictly prohibited.

function setup() {
  let config = true;
  // let config = false; // SyntaxError: Identifier 'config' has already been declared
  // var config = false; // SyntaxError
}

The const keyword creates a read-only reference to a value. Like let, its block-scoped, non-hoisted, subject to the TDZ, and forbids redeclaration. The critical distinction is that a const binding must be initialized at declaration and cannot be reassigned.

const MAX_RETRIES = 3;
// MAX_RETRIES = 5; // TypeError: Assignment to constant variable.

When const holds a reference type (objects, arrays), the binding itself is immutable, but the underlying data structure remains mutable. Reassignment fails, but property mutation succeeds.

const settings = { theme: 'dark' };
settings.theme = 'light'; // Valid
settings.version = 2;     // Valid

// settings = {}; // TypeError: Invalid reassignment

Arrays behave identically:

const queue = [];
queue.push('task1', 'task2'); // Valid mutation
console.log(queue.length);    // 2

// queue = ['new']; // TypeError

To prevent modification of the underlying data structure, apply Object.freeze(). This seals the object, blocking additions, deletions, and value changes.

const frozenConfig = Object.freeze({ mode: 'production' });
frozenConfig.mode = 'development'; // Silently fails (or throws in strict mode)
console.log(frozenConfig.mode);    // 'production'

Modern JavaScript provides six declaration mechanisms: var, function, let, const, import, and class. When choosing between let and const, consider thier shared constraints and distinct behaviors:

  • Shared: Block-scoped, TDZ-enforced, no hoisting initialization, no same-scope redeclaration.
  • Distinct: let allows reassignment and deferred initialization. const requires immediate initialization and prohibits reassignment. For reference types, const only locks the memory address, not the contents.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.