Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

A Comparative Guide to JavaScript Variable Declarations: var, let, and const

Tech May 14 1

Understanding Scope and Binding Rules in Modern JavaScript

JavaScript provides three distinct keywords for creating identifiers. Although they all allocate memory slots, their interactions with hoisting, reusability, and lexical boundaries differ fundamentally.

Legacy Declaration: var

The traditional mechanism relies on function-level scoping and exhibits unpredictable lookup patterns during compilation.

Function-Level Hoisting

Identifiers created with var are elevated to the top of their containing scope during the parse phase. Accessing them before assignment yields undefined instead of throwing an error.

// Output: undefined
console.log(switchState);
var switchState = true;

This behavior mirrors the internal engine transformation:

var switchState;
console.log(switchState); // undefined
switchState = true;

Redeclaration Flexibility

var allows multiple declarations of the same name within a single scope. Later executions simply overwrite earlier allocations.

var threshold = 50;
threshold = 90; // Direct overwrite across the same scope
console.log(threshold); // 90

Scope Boundary Leakage

Declaring inside a function restricts visibility. Omitting the keyword inadvertently attaches the identifier to the global object (window or globalThis).

var globalFlag = false;

function toggleConfig() {
    // Implicitly targets the global environment
    console.log(globalFlag); // false
    globalFlag = true;
}

toggleConfig();
console.log(globalFlag); // true

Modern Block Scoping: let

Added in ES6, let enforces stricter lexical containment and eliminates accidental namespace pollution.

Strict Temporal Dead Zone (TDZ)

Referencing a let identifier before its definition line throws a ReferenceError. The slot exists from block entry until the initialization instruction runs.

// Throws: ReferenceError
console.log(loopIndex);
let loopIndex = 0;

Block-Level Encapsulation

Curly braces establish hard execution boundaries. External environments cannot perceive identifiers declared internally.

{
    let sessionToken = "abc-123";
}
// Throws: ReferenceError
console.log(sessionToken);

Prohibition on Duplicate Declarations

The parser rejects any attempt to declare the same name twice within a shared lexical context, preventing ambiguous overrides.

{
    let retryCount = 1;
    // let retryCount = 5; // SyntaxError: Identifier 'retryCount' has already been declared
}
let retryCount = 2; // Permitted in the outer scope
console.log(retryCount); // 2

Nested Lookup Chains

Inner scopes inherit visibility of outer variables, but reverse access is forbidden. Child environments cannot leak into parent namespaces.

// Valid inner-to-outer access
function initializeData() {
    let baseValue = 42;
    if (true) {
        console.log(baseValue); // 42
    }
}
initializeData();

// Invalid outer access to inner state
function setupEnvironment() {
    let parentId = 1;
    console.log(parentId); // 1
    function allocateChild() {
        let childId = 2;
    }
    // Throws: ReferenceError
    console.log(childId);
}
setupEnvironment();

Immutable Bindings: const

Apply const for identifiers that must maintain their initial reference throughout execution.

Read-Only Assignment Semantics

Reassigning a constant after creation immediately interrupts runtime execution.

const limit = 100;
limit = 150; // TypeError: Assignment to constant variable.

Mandatory Initialization at Definition

Omitting an initializer during declaration violates syntax rules.

const config;          // SyntaxError
const config = { key: "value" }; // Valid

Distinguishing Reassignment from Mutation

const safeguards the binding itself, not the referenced data structure. Objects and arrays remain fully mutable unless explicitly frozen.

const registry = { version: 1 };

// Safe: modifying properties of the targeted object
registry.version = 2; 
console.log(registry.version); // 2

// Forbidden: redirecting the identifier to a new object
registry = { version: 3 }; // TypeError
Tags: javascript

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.