Skip to main content

Front-End Development

Scoping, Hoisting and Temporal Dead Zone in JavaScript

Istock 2186780950

Before mastering JavaScript, it’s crucial to understand how it thinks behind the scenes. Concepts like scope, hoisting, and the temporal dead zone might sound tricky at first, but they form the backbone of how your code behaves.
In this blog, we’ll break down these key ideas in the simplest way possible—so you can write cleaner code, avoid unexpected bugs, and truly understand what’s happening when your script runs.

What is Scope in JavaScript?

Think of scope like a boundary or container that controls where you can use a variable in your code.

In JavaScript, a variable is only available in the part of the code where it was created. If you try to use it outside that area, it won’t work—that’s because of scope.

This helps in two big ways:

  • Keeps your code safe – Only the right parts of the code can access the variable.
  • Avoids name clashes – You can use the same variable name in different places without them interfering with each other.

JavaScript mainly uses two types of scope:

1.Global Scope – Available everywhere in your code.

2.Local Scope – Available only inside a specific function or block.

 

Global Scope

When you start writing JavaScript code, you’re already in the global scope—this is like the outermost area of your code where variables can live.

If you create a variable outside of any function or block, it’s considered global, which means it can be used anywhere in your code.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var a = 5; // Global variable
functionadd(){
return a + 10; // Using the global variable inside a function
}
console.log(window.a); // 5
var a = 5; // Global variable function add() { return a + 10; // Using the global variable inside a function } console.log(window.a); // 5
var a = 5; // Global variable
function add() {
  return a + 10; // Using the global variable inside a function
}
console.log(window.a); // 5

In this example, a is declared outside of any function, so it’s globally available—even inside add().

A quick note:

  • If you declare a variable with var, it becomes a property of the window object in browsers.
  • But if you use let or const, the variable is still global, but not attached to window.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let name = "xyz";
functionchangeName(){
name = "abc"; // Changing the value of the global variable
}
changeName();
console.log(name); // abc
let name = "xyz"; function changeName() { name = "abc"; // Changing the value of the global variable } changeName(); console.log(name); // abc
let name = "xyz";
function changeName() {
  name = "abc";  // Changing the value of the global variable
}
changeName();
console.log(name); // abc

In this example, we didn’t create a new variable—we just changed the value of the existing one.

👉 Important:
If you redeclare a global variable inside a function (using let, const, or var again), JavaScript treats it as a new variable in a new scope—not the same one. We’ll cover that in more detail later.

 

 Local Scope

In JavaScript, local scope means a variable is only accessible in a certain part of the code—usually inside a function or a block.

There are two types of local scope:

1.Functional Scope

Whenever you create a function, it creates its own private area for variables. If you declare a variable inside a function, it only exists inside that function.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let firstName = "Shilpa"; // Global
functionchangeName(){
let lastName = "Syal"; // Local to this function
console.log(`${firstName}${lastName}`);
}
changeName();
console.log(lastName); // ❌ Error! Not available outside the function
let firstName = "Shilpa"; // Global function changeName() { let lastName = "Syal"; // Local to this function console.log (`${firstName} ${lastName}`); } changeName(); console.log (lastName); // ❌ Error! Not available outside the function
let firstName = "Shilpa"; // Global
function changeName() {
  let lastName = "Syal"; // Local to this function
console.log (`${firstName} ${lastName}`);
}
changeName();
console.log (lastName); // ❌ Error! Not available outside the function

You can even use the same variable name in different functions without any issue:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
functionmathMarks(){
let marks = 80;
console.log(marks);
}
functionenglishMarks(){
let marks = 85;
console.log(marks);
}
function mathMarks() { let marks = 80; console.log (marks); } function englishMarks() { let marks = 85; console.log (marks); }
function mathMarks() {
  let marks = 80;
  console.log (marks);
}
function englishMarks() {
  let marks = 85;
  console.log (marks);
}

Here, both marks variables are separate because they live in different function scopes.

 

2.Block Scope

Thanks to let and const, you can now create variables that only exist inside a block (like an if, for, or {}).

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
functiongetMarks(){
let marks = 60;
if(marks > 50){
const points = 10;
console.log(marks + points); // ✅ Works here
}
console.log(points); // ❌ Uncaught Reference Error: points is not defined
}
function getMarks() { let marks = 60; if (marks > 50) { const points = 10; console.log (marks + points); // ✅ Works here } console.log (points); // ❌ Uncaught Reference Error: points is not defined }
function getMarks() {
  let marks = 60;
  if (marks > 50) {
    const points = 10;
    console.log (marks + points); // ✅ Works here
  }
  console.log (points); // ❌ Uncaught Reference Error: points is not defined
}

 As points variable is declared in if block using the let keyword, it will not be only accessible outside as shown above. Now try the above example using var keyword i.e declare “points” variable with var and spot the difference.

LEXICAL SCOPING & NESTED SCOPE:

When you create a function (outer function) that contains another function (inner function), then the inner function has access to the outer function’s variables and methods. This is known as Lexical Scoping.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
functionouterFunction(){
let outerVar = "I’m outside";
functioninnerFunction(){
console.log(outerVar); // ✅ Can access outerVar
}
innerFunction();
}
function outerFunction() { let outerVar = "I’m outside"; function innerFunction() { console.log (outerVar); // ✅ Can access outerVar } innerFunction(); }
function outerFunction() {
  let outerVar = "I’m outside";
  function innerFunction() {
      console.log (outerVar); // ✅ Can access outerVar
  }
  innerFunction();
}

In other terms, variables & methods defined in parent function are automatically available to its child functions. But it doesn’t work the other way around—the outer function can’t access the inner function’s variables.

 

VARIABLE SCOPE OR VARIABLE SHADOWING:

You can declare variables with the same name at different scopes. If there’s a variable in the global scope and you create variable with the same name in a function, then you will not get any error. In this case, local variables take priority over global variables. This is known as Variable shadowing, as inner scope variables temporary shadows the outer scope variable with the same name.

If the local variable and global variable have the same name then changing the value of one variable does not affect the value of another variable.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let name = "xyz"
functiongetName(){
let name = "abc"// Redeclaring the name variable
console.log(name) ; //abc
}
getName();
console.log(name) ; //xyz
let name = "xyz" function getName() { let name = "abc" // Redeclaring the name variable console.log (name) ; //abc } getName(); console.log (name) ; //xyz
let name = "xyz"
function getName() {
  let name = "abc"            // Redeclaring the name variable
      console.log (name)  ;        //abc
}
getName();
console.log (name) ;          //xyz

To access a variable, JS Engine first going to look in the scope that is currently in execution, and if it doesn’t find there, it will look to its closest parent scope to see if a variable exist there and that lookup process will continue the way up, until JS Engine reaches the global scope. In that case, if the global scope doesn’t have the variable, then it will throw a reference error, as the variable doesn’t exist anywhere up the scope chain.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let bonus = 500;
functiongetSalary(){
if(true){
return10000 + bonus; // Looks up and finds bonus in the outer scope
}
}
console.log(getSalary()); // 10500
let bonus = 500; function getSalary() { if(true) { return 10000 + bonus; // Looks up and finds bonus in the outer scope } } console.log (getSalary()); // 10500
let bonus = 500;
function getSalary() {
 if(true) {
     return 10000 + bonus;  // Looks up and finds bonus in the outer scope
  }
}
   console.log (getSalary()); // 10500

 

Key Takeaways: Scoping Made Simple

Global Scope: Variables declared outside any function are global and can be used anywhere in your code.

Local Scope: Variables declared inside a function exist only inside that function and disappear once the function finishes.

Global Variables Last Longer: They stay alive as long as your program is running.

Local Variables Are Temporary: They’re created when the function runs and removed once it ends.

Lexical Scope: Inner functions can access variables from outer functions, but not the other way around.

Block Scope with let and const: You can create variables that exist only inside {} blocks like if, for, etc.

Same Name, No Clash: Variables with the same name in different scopes won’t affect each other—they live in separate “worlds.” 

Hoisting

To understand Hoisting in JS, it’s essential to know how execution context works. Execution context is an environment where JavaScript code is executed.

It has two main phases:

1.Creation Phase: During this phase JS allocated memory or hoist variables, functions and objects. Basically, hoisting happens here.

2.Execution Phase: During this phase, code is executed line by line.

-When js code runs, JavaScript hoists all the variables and functions i.e. assigns a memory space for those variables with special value undefined.

 

Key Takeaways from Hoisting and let’s explore some examples to illustrate how hoisting works in different scenarios:

  1. functions– Functions are fully hoisted. They can invoke before their declaration in code.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
foo(); // Output: "Hello, world!"
functionfoo(){
console.log("Hello, world!");
}
foo (); // Output: "Hello, world!" function foo () { console.log ("Hello, world!"); }
foo (); // Output: "Hello, world!"
 function foo () {
     console.log ("Hello, world!");
 }
  1. var – Variables declared with var are hoisted in global scope but initialized with undefined. Accessible before the declaration with undefined.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(x); // Output: undefined
var x = 5;
console.log (x); // Output: undefined var x = 5;
console.log (x); // Output: undefined
 var x = 5;

This code seems straightforward, but it’s interpreted as:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
var x;
console.log(x); // Output: undefined
x = 5;
var x; console.log (x); // Output: undefined x = 5;
var x;
console.log (x); // Output: undefined
 x = 5;

3.Let, Const – Variables declared with Let and const are hoisted in local scope or script scope but stay in TDZ. These variables enter the Temporal Dead Zone (TDZ) until their declaration is encountered. Accessing in TDZ, results is reference Error

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(x); // Throws Reference Error: Cannot access 'x' before initialization
let x = 5;
console.log (x); // Throws Reference Error: Cannot access 'x' before initialization let x = 5;
console.log (x); // Throws Reference Error: Cannot access 'x' before initialization
 let x = 5;


What is Temporal Dead Zone (TDZ)?

In JavaScript, all variable declarations—whether made using var, let, or const—are hoisted, meaning the memory for them is set aside during the compilation phase, before the code actually runs. However, the behaviour of hoisting differs based on how the variable is declared.

For variables declared with let and const, although they are hoisted, they are not initialized immediately like var variables. Instead, they remain in an uninitialized state and are placed in a separate memory space. During this phase, any attempt to access them will result in a Reference Error.

This period—from the start of the block until the variable is initialized—is known as the Temporal Dead Zone (TDZ). It’s called a “dead zone” because the variable exists in memory but cannot be accessed until it has been explicitly declared and assigned a value in the code.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
console.log(x); //x is not defined -- Reference Error.
let a=10; //b is undefined.
var b= 100; // you cannot access a before initialization Reference Error.
console.log (x); //x is not defined -- Reference Error. let a=10; //b is undefined. var b= 100; // you cannot access a before initialization Reference Error.
console.log (x); //x is not defined -- Reference Error.
let a=10; //b is undefined.
var b= 100; // you cannot access a before initialization Reference Error.

👉 Important: The Temporal Dead Zone helps prevent the use of variables before they are properly declared and initialized, making code more predictable and reducing bugs.

 

🧾 Conclusion

JavaScript hoisting and scoping are foundational concepts that can feel tricky at first, but once you understand them, they make your code more structured and predictable. Hoisting helps explain why some variables and functions work even before they’re declared, while scoping defines where your variables live and how accessible they are. By keeping these concepts in mind and practicing regularly, you’ll be able to write cleaner, more reliable JavaScript. The more you experiment with them, the more confident you’ll become as a developer. Keep learning, keep building, and everything will start to click. Happy coding! 🙌

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Apeksha Handore

Apeksha is a front-end developer with expertise in React.js and Next.js. She crafts performant, scalable, and accessible web applications. With a strong background in modern UI development and state management, she specializes in building intuitive user experiences and robust front-end architectures.

More from this Author

Follow Us