Frontend engineer path · The language · Topic 2 of 17

Scope and closures

Not started

Which variables a function can see is decided by where it is written, not where it is called. let and const live in their block, var in the whole function. A function that uses outer variables keeps them alive for as long as the function itself can still be called: that pairing is a closure.

Why it matters: Every callback, event handler and private counter you write relies on it, and so does the classic loop-with-var bug.

1

How it works

The explanation, with examples you can run.

Lexical scope

Scopes nest like the braces in your file. A function can read the variables of every scope around it, and nobody outside can read the ones inside it. let and const exist from the start of their block, but reading them before the line that declares them throws a ReferenceError (the "temporal dead zone"); var is hoisted to the top of the function and reads as undefined.

The loop-with-var bug

⌘/Ctrl + Enter
OutputNot run yet

Press Run to see what your code prints, in order.

Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.

There is one i for the whole script, and all three arrows read it after the loop has set it to 3. A let in a for head is special: every iteration gets a fresh binding, so each arrow keeps its own j.

Private state

A closure is the simplest way to keep data private. Nothing outside counter can reach count except through the two functions it hands back.

⌘/Ctrl + Enter
OutputNot run yet

Press Run to see what your code prints, in order.

Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.

The same thing explains many memory leaks: a long-lived callback that closes over a large object keeps that object alive too.

2

Check yourself

2 questions. Get them all right to finish this topic.

  1. 1.What does this print?
    const fns = []for (var i = 0; i < 3; i++) fns.push(() => i)console.log(fns.map((f) => f()))
  2. 2.What keeps a closed-over variable alive after its function has returned?

0 of 2 right so far. Get all two to finish this topic.