Data Structures and Algorithms

Hoisting, Scope, and Execution Context in JavaScript Explained for Interviews

JavaScript is a powerful yet quirky language, and mastering its core mechanics—hoisting, scope, and execution context—is essential for writing reliable code and shining in technical interviews. These concepts dictate how variables and functions are accessed, how code executes, and how JavaScript manages its runtime environment. In this comprehensive guide, we’ll explore each topic in depth, with practical examples, common interview questions, and best practices to help you confidently tackle JavaScript challenges.

Want access to free JavaScript interview prep materials and course updates? Sign up here.

Introduction

JavaScript’s dynamic nature makes it both flexible and challenging. Misunderstanding hoisting, scope, or execution context can lead to bugs that are hard to trace, such as accessing undefined variables or unexpected function behavior. For interview preparation, these topics are critical, as they’re often tested to assess your grasp of JavaScript’s runtime behavior. This post will break down each concept, provide clear examples, and offer actionable advice to help you write better code and ace your interviews.

By the end, you’ll understand:

  1. Hoisting: How JavaScript moves declarations and why it affects code execution.
  2. Scope: How variable accessibility works across global, function, and block scopes.
  3. Execution Context: The environment where code runs, including the call stack and this keyword.

Let’s dive in!

Hoisting in JavaScript

What is Hoisting?

Hoisting is JavaScript’s behavior of moving variable and function declarations to the top of their containing scope during the compilation phase, before the code executes. This allows you to use variables or call functions before their declaration in the code, but the behavior depends on the declaration type.

According to MDN Web Docs, hoisting isn’t a term defined in the ECMAScript specification but describes how declarations appear to be “moved” to the top of their scope.

What is Hoisting

Hoisting with Variables

Variable hoisting varies by keyword:

var: Variables declared with var are hoisted and initialized to undefined. You can access them before their declaration, but their value will be undefined until assigned.

				
					 console.log(x); // undefined
var x = 10;
console.log(x); // 10

				
			
  •  Here, x is hoisted to the top of the scope, initialized as undefined, and later assigned 10.

 let and const: These are hoisted but not initialized, residing in a “temporal dead zone” (TDZ) until their declaration. Accessing them before declaration throws a ReferenceError.


console.log(y); // ReferenceError: Cannot access ‘y’ before initialization

let y = 20;

  •  The TDZ ensures safer variable usage by preventing access before initialization.

Hoisting with Functions

Function declarations and expressions behave differently:

Function Declarations: Fully hoisted, including the function body, so you can call them before their definition.

				
					 greet(); // "Hello!"
function greet() {
  console.log("Hello!");
}

				
			

Function Expressions:

 Only the variable declaration is hoisted, not the function assignment. Calling before assignment results in a TypeError.

				
					
 sayHi(); // TypeError: sayHi is not a function
var sayHi = function() {
  console.log("Hi!");
};

				
			

Common Interview Questions on Hoisting

  • What is hoisting in JavaScript?
    • Answer: Hoisting is JavaScript’s mechanism of moving variable and function declarations to the top of their scope before execution. var variables are initialized to undefined, while let and const are in the TDZ until declared.
  • What’s the difference between var, let, and const in hoisting?
    • Answer: var is hoisted and initialized to undefined; let and const are hoisted but not initialized, causing a ReferenceError if accessed before declaration.

Best Practices for Hoisting

To avoid hoisting-related bugs:

  • Declare variables at the top of their scope to make code predictable.
  • Use let and const over var to leverage block scope and avoid TDZ issues.
  • Define functions before calling them or use function expressions consistently.
  • Use tools like ESLint with the no-use-before-define rule.

Declaration Type

Hoisted?

Initialized?

Behavior

var

Yes

undefined

Accessible before declaration, returns undefined.

let/const

Yes

No (TDZ)

Throws ReferenceError if accessed before declaration.

Function Declaration

Yes

Fully hoisted

Callable before definition.

Function Expression

Yes (variable only)

undefined

Throws TypeError if called before assignment.

Scope in JavaScript

What is Scope?

Scope determines where variables, functions, and objects are accessible in your code. It’s the context or environment where these identifiers are “visible”. JavaScript has three primary scope types: global, function, and block.

Global Scope

Variables declared outside any function or block are in the global scope, accessible from anywhere in the program.

				
					var globalVar = "I'm global";
function accessGlobal() {
  console.log(globalVar); // "I'm global"
}
accessGlobal();
console.log(globalVar); // "I'm global"

				
			

Caution: Overusing global variables can lead to naming conflicts and unintended modifications. Variables declared with var in global scope attach to the window object in browsers, unlike let and const.

Function Scope

Variables declared inside a function are local to that function and inaccessible outside it.

				
					function localScope() {
  var localVar = "I'm local";
  console.log(localVar); // "I'm local"
}
localScope();
console.log(localVar); // ReferenceError: localVar is not defined


				
			

Function scope promotes encapsulation, reducing the risk of variable conflicts.

Block Scope

Introduced in ES6, block scope confines variables declared with let or const to the nearest enclosing block (e.g., {} in if, for, or while statements). var does not respect block scope.

				
					if (true) {
  let blockVar = "I'm in block";
  var notBlockVar = "I'm not block-scoped";
}
console.log(blockVar); // ReferenceError: blockVar is not defined
console.log(notBlockVar); // "I'm not block-scoped"

				
			

Block scope enhances modularity and prevents variable leakage.

Scope Chain

The scope chain is JavaScript’s mechanism for resolving variable names. When a variable is referenced, JavaScript searches the current scope, then parent scopes, up to the global scope.

				
					var outer = "outer";
function outerFunc() {
  var inner = "inner";
  console.log(outer); // "outer" (from global scope)
  console.log(inner); // "inner" (from function scope)
}
outerFunc();

				
			
Scope Chain

 

This follows lexical scoping, where the scope is determined by the code’s structure at compile time.

Closures

A closure is a function that retains access to its outer scope’s variables, even after the outer function has returned. Closures are powerful for data encapsulation and state management.

				
					function outer() {
  var outerVar = "I'm outer";
  return function inner() {
    console.log(outerVar); // "I'm outer"
  };
}
var closure = outer();
closure();

				
			

Closures are commonly used in event handlers, module patterns, and React hooks.

Common Interview Questions on Scope

  • What is the scope chain in JavaScript?

    • Answer: The scope chain is how JavaScript resolves variables by searching from the current scope up to the global scope, following lexical scoping.
  • What is a closure, and how does it work?

    • Answer: A closure is a function that retains access to its outer scope’s variables. It’s created when a function is defined inside another function, allowing the inner function to access outer variables even after the outer function returns.

Best Practices for Scope

  • Minimize global variables to avoid conflicts.
  • Use let and const for block scope to enhance modularity.
  • Avoid variable shadowing (redeclaring a variable in a nested scope with the same name).
  • Use strict mode (“use strict”;) to prevent undeclared variables from becoming global.

Scope Type

Accessibility

Keywords

Notes

Global

Everywhere

var, let, const

Use sparingly to avoid conflicts.

Function

Within function

var, let, const

Promotes encapsulation.

Block

Within block

let, const

Introduced in ES6, prevents leakage.

Execution Context in JavaScript

What is Execution Context?

Execution context is the environment where JavaScript code executes, managing the code, variables, scope chain, and this keyword. It’s like a container that holds everything needed to run a piece of code.

Types of Execution Context

  • Global Execution Context (GEC): Created when a script starts, handling code outside functions. There’s only one GEC per script.
  • Function Execution Context (FEC): Created for each function call, managing function-specific code and variables.
Execution Context in JavaScript

Creation Phase

The creation phase sets up the execution environment:

  • For GEC:

    • Creates the global object (window in browsers, global in Node.js).
    • Binds this to the global object.
    • Sets up the variable object, hoisting var variables to undefined and storing function declarations.
  • For FEC:

    • Creates an arguments object with function parameters.
    • Sets this based on how the function is called (e.g., global object for regular functions, object for methods).
    • Sets up the variable object, hoisting variables and functions within the function.

Example:

				
					var x = 10;
function timesTen(a) {
  return a * 10;
}
var y = timesTen(x);
console.log(y); // 100

				
			

In the creation phase, x and y are hoisted to undefined, and timesTen is stored. In the execution phase, x is assigned 10, timesTen(10) returns 100, and y is assigned 100.

Execution Phase

The execution phase runs the code line by line, assigning values to variables, executing function calls, and handling returns.

Call Stack

The call stack tracks all execution contexts in a Last-In-First-Out (LIFO) order. When a function is called, its FEC is pushed onto the stack; when it returns, it’s popped off. This ensures JavaScript’s single-threaded nature.

Call Stack

Example:

				
					function first() {
  console.log("First");
  second();
}
function second() {
  console.log("Second");
}
first();


				
			

 

The call stack processes first(), pushes second() onto the stack, then pops them off in reverse order, logging “First” then “Second”.

Common Interview Questions on Execution Context

  • What is the execution context in JavaScript?
    • Answer: It’s the environment where code executes, managing variables, the scope chain, and this. It has a creation phase (setting up the environment) and an execution phase (running the code).
  • What is the call stack, and how does it work?
    • Answer: The call stack is a LIFO structure that tracks execution contexts. It pushes a new context for each function call and pops it off when the function returns.

Best Practices for Execution Context

  • Avoid deep recursion to prevent stack overflows.
  • Understand how this is set in different contexts (e.g., global, object methods, arrow functions).
  • Use debugging tools to visualize the call stack and identify execution issues.

Execution Context

Components

Creation Phase

Execution Phase

Global

Global object, this, variable object

Sets up window/global, hoists variables/functions

Executes top-level code

Function

arguments, this, variable object

Sets up parameters, hoists variables/functions

Executes function code

Conclusion

Hoisting, scope, and execution context are the backbone of JavaScript’s runtime behavior. Hoisting explains why you can use variables and functions before their declaration, scope governs variable accessibility, and execution context manages how code runs. Mastering these concepts will help you write cleaner code, debug effectively, and answer interview questions with confidence.

Actionable Advice:

  • Practice writing code that demonstrates hoisting, scope, and execution context.
  • Use tools like JavaScript Visualizer to see how code executes.
  • Prepare for interviews by explaining code snippets involving these concepts.

Have questions or want to share your insights? Leave a comment below!

FAQs

What is the difference between scope and execution context in JavaScript?

Scope defines where variables are accessible. Execution context defines how and when code is run.

Yes, but they live in the Temporal Dead Zone and throw a ReferenceError if accessed before declaration.

How are closures used in real-world applications?

Closures are widely used in React hooks, private state management, and event listeners.

No. Arrow functions are not hoisted like function declarations—they’re treated like expressions.

Why should I avoid using var in modern JavaScript?

var has function scope and allows hoisting with undefined, which often leads to bugs. Prefer let or const.

DSA, High & Low Level System Designs

Buy for 60% OFF
₹25,000.00 ₹9,999.00

Accelerate your Path to a Product based Career

Boost your career or get hired at top product-based companies by joining our expertly crafted courses. Gain practical skills and real-world knowledge to help you succeed.

Reach Out Now

If you have any queries, please fill out this form. We will surely reach out to you.

Contact Email

Reach us at the following email address.

Phone Number

You can reach us by phone as well.

+91-97737 28034

Our Location

Rohini, Sector-3, Delhi-110085

WhatsApp Icon

Master Your Interviews with Our Free Roadmap!

Hi Instagram Fam!
Get a FREE Cheat Sheet on System Design.

Hi LinkedIn Fam!
Get a FREE Cheat Sheet on System Design

Loved Our YouTube Videos? Get a FREE Cheat Sheet on System Design.