Scope, Hoisting & Closures in JavaScript

Understanding Scope, Hoisting, and Closures in JavaScript

These three concepts — scope, hoisting, and closures — form the foundation of how JavaScript code is interpreted and executed. Mastering them is essential for writing predictable and bug-free JavaScript.


Table of Contents

  1. What is Scope?
  2. Types of Scope in JavaScript
  3. Variable Declarations: var, let, and const
  4. Hoisting Explained
  5. Lexical Scope
  6. Closures: The Heart of JavaScript
  7. Common Use Cases of Closures
  8. Best Practices & Gotchas
  9. Conclusion

1. What is Scope?

Scope determines the accessibility (visibility) of variables. In JavaScript, scope defines where variables and functions can be accessed.


2. Types of Scope in JavaScript

a. Global Scope

Variables declared outside any function or block have global scope.

let globalVar = "I'm global";

function printGlobal() {
console.log(globalVar); // accessible here
}

b. Function Scope

Variables declared with var inside a function are function-scoped.

function example() {
var a = 10;
console.log(a); // 10
}
console.log(a); // Error: a is not defined

c. Block Scope

Variables declared with let and const are block-scoped.

{
let x = 5;
const y = 10;
}
// console.log(x); // Error

3. Variable Declarations: var, let, and const

KeywordScope TypeHoistedReassignableRedeclarable
varFunctionYesYesYes
letBlockYes*YesNo
constBlockYes*NoNo
* let and const are hoisted but not initialized.

4. Hoisting Explained

Hoisting is JavaScript’s behavior of moving declarations to the top of their scope before code execution.

console.log(a); // undefined
var a = 5;

Here, var a is hoisted but not the assignment. The JS engine interprets it as:

var a;
console.log(a); // undefined
a = 5;

Let’s look at let and const:

console.log(b); // ReferenceError
let b = 10;

They’re hoisted but kept in a temporal dead zone (TDZ) until the line where they’re defined.


5. Lexical Scope

JavaScript uses lexical scoping, meaning the scope of a variable is determined by its location in the source code.

function outer() {
let outerVar = "I’m outer";

function inner() {
console.log(outerVar); // accessible due to lexical scope
}

inner();
}

6. Closures: The Heart of JavaScript

A closure is a function that “remembers” variables from its lexical scope even when the function is executed outside that scope.

function outerFunc() {
let count = 0;

return function innerFunc() {
count++;
console.log(count);
};
}

const counter = outerFunc();
counter(); // 1
counter(); // 2

Even though outerFunc has finished executing, innerFunc retains access to count. That’s closure.


7. Common Use Cases of Closures

  • Data encapsulation
  • Function factories
  • Maintaining private variables
  • Event handlers and callbacks

Example: Private variables with closures

function createCounter() {
let count = 0;

return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
}
};
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2

8. Best Practices & Gotchas

  • Always use let and const to avoid issues with hoisting and scope leakage.
  • Be cautious when using closures in loops — always prefer let over var.
  • Avoid overusing closures as they can lead to memory leaks if not handled properly.

9. Conclusion

Scope, hoisting, and closures are concepts that underpin how JavaScript runs your code. While they can be tricky at first, understanding them enables you to write more powerful and predictable applications.

In the next module, we’ll explore ES6+ Features — a modern set of features that changed how we write JavaScript today.