Closures and the Scope Chain in JavaScript
Scope refers to where in your code a variable is visible or can be used. In JavaScript, each function creates a new scope, and since ES6, blocks ( { } ) can also create scope with let and const . As MDN puts it, “the scope is the current context of execution in which values and expressions are ‘visible’ or can be referenced” . For example, a variable declared inside a function only exists inside that function (function scope), while a variable declared outside any function is in the global scope. Child scopes have access to their parent scopes, but parent scopes cannot see into child scopes . In practice, this means you have Global Scope, Function Scope, and Block Scope (with let / const ). For instance: Global Scope: Declared outside any function, e.g. const x = 5; at the top level. Accessible anywhere. Function Scope: Declared inside a function, e.g. function f() { var y = 10; } . Variable y is only visible inside f() . Block Scope (ES6+): Declared inside { } with let or const , e.g Here z is only visible inside the if block. This hierarchical structure of scopes is sometimes called the scope chain. When your code refers to a variable, JavaScript looks in the current scope frst; if it’s not found, it looks in the parent scope, and so on up to the global scope. In MDN’s words, nested functions’ scopes form “a chain of function scopes” . For example: Here, when inner() looks up color , it fnds red in its parent function paint() before looking at the global color What is a Closure? A closure is when an inner function keeps access to variables from its outer function’s scope even after the outer function has fnished running. In other words, the inner function remembers the environment in which it was created. MDN describes it as “the combination of a function and the lexical environment within which that function was declared” . This may sound formal, but the idea is that the function carries around (“closes over”) the variables it needs. For example: In this code, greet() is an inner function that uses message from makeGreeting() . Even though makeGreeting() has fnished executing by the time we call sayHello() , the function greet still remembers the variable message . As MDN notes, the returned greet function “maintains a reference to its lexical environment, within which the variable name exists” (in this case, message exists) . In simpler terms, “a closure is a function that remembers variables from the scope where it was created, even after that scope has exited” . Each time you create a function, JavaScript automatically gives it access to its surrounding scope. A closure is simply this feature in action: the inner function still has access to its outer variables. For instance, after makeGreeting() runs, we get back the greet function. When we later call sayHello() , it logs message correctly, because the closure kept message alive . How Closures Work (Scope Chain) Closures rely on lexical scoping. This means JavaScript determines the scope of variables based on where functions are written in the source code. When you refer to a variable, the engine follows the scope chain: it checks the local scope, then the parent scope, then the next outer scope, and so on . For example: Here, the inner function returned by sum(5) has access to a (which is 5) and also to e from the global scope. The scope chain for that inner function includes its own local scope (which has b ), then the sum function’s scope (which has a ), and then the global scope (which has e ). JavaScript fnds each variable by searching up this chain . If a variable isn’t found in the current scope, JS goes up one level. If it still doesn’t fnd it, it keeps going until the global scope. If no variable is found, you get a ReferenceError. In short, scope chain is just this chain of nested scopes that JavaScript follows to resolve variables. Practical Use Cases of Closures Closures are not just a theoretical concept; they have many practical uses in JavaScript: Data Encapsulation / Private Variables: Closures allow you to hide variables from the outside world. For example, you can create a function that returns an object with methods that access a private variable. That variable can’t be changed except through those methods. MDN and other tutorials often show how closures create “private” data. Kodaschool explains: “the count variable is not accessible from outside the createCounter scope, making it private to the returned object” . For example: Here, count is enclosed by the functions in the returned object. No outside code can directly modify count ; it can only be changed by calling increment() or decrement() . This achieves data encapsulation because the variable is hidden inside the closure . Event Handlers and Callbacks: Closures are especially common in web code. When you set up an event listener or a callback, the function you provide retains access to the outer scope. For example: In this snippet, the click handler is a closure

Scope refers to where in your code a variable is visible or can be used. In JavaScript, each function
creates a new scope, and since ES6, blocks ( { } ) can also create scope with let and const . As
MDN puts it, “the scope is the current context of execution in which values and expressions are ‘visible’
or can be referenced” . For example, a variable declared inside a function only exists inside that
function (function scope), while a variable declared outside any function is in the global scope. Child
scopes have access to their parent scopes, but parent scopes cannot see into child scopes . In
practice, this means you have Global Scope, Function Scope, and Block Scope (with let / const ).
For instance:
Global Scope: Declared outside any function, e.g. const x = 5; at the top level. Accessible
anywhere.
Function Scope: Declared inside a function, e.g. function f() { var y = 10; } . Variable
y is only visible inside f() .
Block Scope (ES6+): Declared inside { } with let or const , e.g
Here z is only visible inside the if block.
This hierarchical structure of scopes is sometimes called the scope chain. When your code refers to a
variable, JavaScript looks in the current scope frst; if it’s not found, it looks in the parent scope, and so
on up to the global scope. In MDN’s words, nested functions’ scopes form “a chain of function
scopes” . For example:
Here, when inner() looks up color , it fnds red in its parent function paint() before looking at the global color
What is a Closure?
A closure is when an inner function keeps access to variables from its outer function’s scope even after
the outer function has fnished running. In other words, the inner function remembers the environment
in which it was created. MDN describes it as “the combination of a function and the lexical environment
within which that function was declared” . This may sound formal, but the idea is that the function
carries around (“closes over”) the variables it needs.
For example:
In this code, greet() is an inner function that uses message from makeGreeting() . Even though
makeGreeting() has fnished executing by the time we call sayHello() , the function greet still
remembers the variable message . As MDN notes, the returned greet function “maintains a
reference to its lexical environment, within which the variable name exists” (in this case, message exists)
. In simpler terms, “a closure is a function that remembers variables from the scope where it was
created, even after that scope has exited” .
Each time you create a function, JavaScript automatically gives it access to its surrounding scope. A
closure is simply this feature in action: the inner function still has access to its outer variables. For
instance, after makeGreeting() runs, we get back the greet function. When we later call
sayHello() , it logs message correctly, because the closure kept message alive .
How Closures Work (Scope Chain)
Closures rely on lexical scoping. This means JavaScript determines the scope of variables based on
where functions are written in the source code. When you refer to a variable, the engine follows the
scope chain: it checks the local scope, then the parent scope, then the next outer scope, and so on
.
For example:
Here, the inner function returned by sum(5) has access to a (which is 5) and also to e from the
global scope. The scope chain for that inner function includes its own local scope (which has b ), then
the sum function’s scope (which has a ), and then the global scope (which has e ). JavaScript fnds
each variable by searching up this chain .
If a variable isn’t found in the current scope, JS goes up one level. If it still doesn’t fnd it, it keeps going
until the global scope. If no variable is found, you get a ReferenceError. In short, scope chain is just this
chain of nested scopes that JavaScript follows to resolve variables.
Practical Use Cases of Closures
Closures are not just a theoretical concept; they have many practical uses in JavaScript:
Data Encapsulation / Private Variables: Closures allow you to hide variables from the outside
world. For example, you can create a function that returns an object with methods that access a
private variable. That variable can’t be changed except through those methods. MDN and other
tutorials often show how closures create “private” data. Kodaschool explains: “the count
variable is not accessible from outside the createCounter scope, making it private to the returned
object” . For example:
Here, count is enclosed by the functions in the returned object. No outside code can directly modify
count ; it can only be changed by calling increment() or decrement() . This achieves data
encapsulation because the variable is hidden inside the closure .
Event Handlers and Callbacks: Closures are especially common in web code. When you set up
an event listener or a callback, the function you provide retains access to the outer scope. For
example:
In this snippet, the click handler is a closure that uses the name variable from setup() . Even after
setup() has fnished running, the handler still “remembers” name . As one source notes, closures
allow event handler functions to “retain access to variables from their outer lexical scope” so that the
handler can use that data later . This way, you can keep state (like name ) alive for use when the
event occurs.
Function Factories (Partial Application): Another common pattern is creating function
factories using closures. For example, you might write a function that takes an argument and
returns a new function pre-flled with that argument. The inner function closes over the
argument. For instance:
Here, makeAdder(10) returns a closure that “remembers” x = 10 . When we later call add10(5) ,
the returned function still has x = 10 in its scope, so it correctly computes 15. This is the same idea
shown in tutorials where a createMultiplier(factor) function returns a new function that
multiplies by that factor . As noted in one example, the inner function “retains access to the factor
variable” . Using closures this way lets you build specialized functions on the fly.
In summary, closures let you keep variables alive in an inner function even after the outer function has
fnished. This enables powerful patterns like private data and dynamic function creation .
Common Mistakes and Pitfalls
When you’re learning closures, beginners often run into a few common issues:
Using var in loops: Before ES6, using var inside a loop could cause all closures to share the
same variable. For example, if you attach callbacks inside a for loop with var i , each
closure refers to the same i , which ends up being the last value after the loop ends. MDN
illustrates this with an example where every event handler shows the last item’s help text,
because each handler “shares the same single lexical environment” and item points to the last
entry . The fx is to use let (which is block-scoped) instead of var , or to create a new
function (an IIFE or factory function) each iteration so each closure captures its own copy of the
variable.
Assuming closures copy values: Closures capture variables, not static copies. If the captured
variable changes later, the closure sees the new value. This can be surprising in loops or if you
modify a variable after creating the closure. In the var loop example above, the problem was
exactly that all closures saw the fnal item value. With let , each iteration has a fresh item ,
so closures work as expected.
Memory use: A closure keeps its outer variables alive as long as the closure exists. In practice
this usually isn’t a problem, but if you store very large data in a closure that persists, it could
keep that data in memory longer than expected. In such cases, make sure you remove
references to the closure when you’re done with it, or avoid capturing large objects unless
needed.
By understanding scope and closures, and being careful with variable declarations (prefer let /
const ), you can use these features correctly. In essence, remember that an inner function always has
access to the scope where it was defined (lexical scope), and it keeps that scope’s variables around (a
closure). Used wisely, closures enable encapsulation and flexible code patterns, but watch out for the
loop-and- var case and similar gotchas