Understanding JavaScript Hoisting: Variables, Functions, and Scope Resolution

During the compilation phase of JavaScript execution, the engine processes all declarations before running any actual code. This behavior, commonly referred to as hoisting, affects how variables and function are initialized and accessed within their respective scopes.

1. Variable Declaration Lifting

When a variable is declared using the var keyword, only the declaration is moved to the top of its scope. The initialization or assignment remains exactly where it was written. Consider the following snippet:


console.log(currentStatus);
var currentStatus = "initialized";

The engine interprets this as two separate steps:


var currentStatus;
console.log(currentStatus); // Outputs: undefined
currentStatus = "initialized";

Because the assignment hasn't occurred yet, accessing the variable returns undefined rather than throwing a reference error.

2. Function Declarations Versus Expressions

JavaScript handles named function declarations and function expressions differently during the hoisting phase. A named function declaration is fully lifted, meaning both the name and the function body are available before execution begins. In contrast, a function expression only lifts the variable declaration, leaving the function assignment in place.


var formatResult = function() {
  console.log("Processed B");
};

function formatResult() {
  console.log("Processed A");
}

formatResult();

The execution context reorders this as follows:


function formatResult() {   // Full function declaration hoisted
  console.log("Processed A");
}

var formatResult;           // Variable declaration hoisted (ignored due to existing name)
formatResult = function() { // Assignment stays in original position
  console.log("Processed B");
}

formatResult();             // Outputs: "Processed B"

Even though the declaration appears first in the transformed order, the runtime assignment overwrites the function reference, causing the expression's logic to execute.

3. Precedence Rules and Name Conflicts

When a variable and a function share the exact same identifier within a scope, function declarations take precedence during the hoisting phase. However, runtime assignments can still override the function reference.


console.log(computeValue);
console.log(computeValue());
var computeValue = 42;
function computeValue() {
  console.log("Internal calculation");
}
computeValue = 85;
console.log(computeValue);
// computeValue(); // Throws TypeError: computeValue is not a function

The parser processes this in the following sequence:


function computeValue() {   // Function declaration hoisted first
  console.log("Internal calculation");
}
var computeValue;           // Variable declaration hoisted (skipped due to existing function)
console.log(computeValue);  // Prints the function definition
console.log(computeValue()); // Executes the function
computeValue = 42;          // Runtime assignment overwrites the function
computeValue = 85;          // Second assignment updates the value
console.log(computeValue);  // Prints 85

This demonstrates that while the function binding wins during the compilation phase, subsequent assignments completely replace it, leading to type errors if invoked as a function later.

4. Local Scope Shadowing During Execution

Hoisting also impacts how variables are resolved within nested or immediate execution contexts. When a variable is declared inside a local scope, it is lifted to the top of that specific function, creating a local binding that shadows any outer variable with the same name, regardless of where the declaration appears.


var environmentMode = "production";
(function() {
  console.log(environmentMode); // Outputs: undefined
  var environmentMode = "development";
})();

Internally, the engine treats this as:


var environmentMode = "production";
(function() {
  var environmentMode;          // Local variable lifted to top of IIFE
  console.log(environmentMode); // References local binding (undefined)
  environmentMode = "development"; // Assignment occurs later
})();

Because the local declaration exists, the engine does not traverse the scope chain to find the global environmentMode. Instead, it uses the uninitialized local binding, resulting in undefined until the assignment statement executes.

Tags: javascript hoisting execution-context variable-declaration function-expressions

Posted on Wed, 22 Jul 2026 16:10:39 +0000 by sava