JavaScript Tutorial menu

JavaScript Scope, Closures and Hoisting

Scope decides where in your code a variable can be seen and used.

Three Kinds of Scope

ScopeCreated byVisible where
GlobalA variable outside every function and blockEverywhere
FunctionA variable inside a functionOnly inside that function
Blocklet or const inside { } (if, for, while)Only inside those brackets

Example

javascript
const shop = "Karachi Mart";        // global

function showBill() {
  const total = 1250;               // function scope
  if (total > 1000) {
    let discount = 100;             // block scope
    console.log(shop, total - discount);
  }
  // console.log(discount);  would be an error here
}

showBill();
// console.log(total);  would be an error here
Output
Karachi Mart 1150

Code inside can look out and see outer variables. Code outside can never look in.

var Ignores Blocks

The old keyword var has function scope, not block scope. A var inside an if or for leaks out of the brackets:

Example

javascript
if (true) {
  var a = 1;
  let b = 2;
}
console.log(a);          // 1, var leaked out
console.log(typeof b);   // "undefined", b does not exist here

This is one of the main reasons modern code uses let and const.

Hoisting

Before running your code, JavaScript scans it and sets up every declaration first. This is called hoisting. It behaves differently for each kind of declaration:

DeclarationCan you use it before the line?
function name() {}Yes, the whole function is ready
var xYes, but its value is undefined
let x / const xNo, you get a ReferenceError

Example

html
<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
let out = "";

out += greet("Ayesha") + "<br>";    // works: function declarations are hoisted
out += "price is " + price + "<br>"; // undefined: var is hoisted without its value
var price = 500;

try {
  out += city;                       // let is not ready yet
  let city = "Lahore";
} catch (error) {
  out += "Error: " + error.name;
}

function greet(name) {
  return "Hello " + name;
}

document.getElementById("demo").innerHTML = out;
</script>

</body>
</html>
Try it Yourself »
Output
Hello Ayesha
price is undefined
Error: ReferenceError

The time between the start of a block and the let or const line is called the temporal dead zone. The variable exists but cannot be touched yet.

Simple rule: declare your variables at the top of their block, before you use them. Then hoisting never surprises you.

Closures

A function remembers the variables that were around it when it was created, even after the outer function has finished. This is called a closure.

Example

javascript
function makeCounter() {
  let count = 0;
  return function () {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter());   // 1
console.log(counter());   // 2
console.log(counter());   // 3

makeCounter has finished running, but the inner function still has access to count. Nobody else can change count, so it is safe and private.

A Practical Closure

Closures are great for making small "factories" of functions. Here, each button has its own private tally:

Example

html
<!DOCTYPE html>
<html>
<body>

<h3>Class Vote</h3>
<button id="pizza">Pizza</button>
<button id="biryani">Biryani</button>
<p id="result"></p>

<script>
function makeVoter(food) {
  let votes = 0;
  return function () {
    votes++;
    document.getElementById("result").textContent = food + " has " + votes + " vote(s)";
  };
}

document.getElementById("pizza").addEventListener("click", makeVoter("Pizza"));
document.getElementById("biryani").addEventListener("click", makeVoter("Biryani"));
</script>

</body>
</html>
Try it Yourself »

Each call to makeVoter creates a new votes variable, so Pizza and Biryani count separately.

Closures in Loops

With let, every loop round gets its own copy of the variable, so closures work as you expect. With var, all rounds share one variable:

Example

javascript
for (let i = 1; i <= 3; i++) {
  setTimeout(() => console.log("let:", i), 0);
}
for (var j = 1; j <= 3; j++) {
  setTimeout(() => console.log("var:", j), 0);
}
Output
let: 1
let: 2
let: 3
var: 4
var: 4
var: 4

Exercise

Exercise

What does this show? function outer() { let x = 5; return () => x * 2; } const f = outer(); console.log(f());

Learn JavaScript with a teacher

Join the Web Development course at Workownix, online or in Orangi Town, Karachi, and get a certificate.

See the course Ask on WhatsApp