JavaScript + Node.js track

Lesson 12 · Part 3: Functional & ES6+

Closures & Scope

Lexical scope, closures and the module pattern

Part 3: Functional & ES6+2 min readFree lesson

Level: Intermediate | Player: 11_Array_Higher-order function

Learning Objectives

  • Explains the meaning of lexical scope.
  • Know what a closure is and when it is created.
  • Create a counter with a private state using a closure.
  • Separate the Public/Private member with a module pattern (IIFE).
  • Solve common loop variable capture bugs with let.

1. Lexical scope

The scope of a function is determined by its declared position. This is not a call location.

const x = 1;
function outer() {
  const y = 2;
  function inner() {
    return x + y; //outer/global variable reference
  }
  return inner;
}

2. Closure

A closure is a phenomenon in which a function remembers the environment (variable) in which it was created. Even after outer() is returned, inner can still use y.

const fn = outer();
fn(); // 3

3. Private status: counter

Closures allow you to create states that cannot be directly accessed from the outside.

function makeCounter() {
  let count = 0;
  return {
    inc() { count += 1; return count; },
    get() { return count; },
  };
}
const c = makeCounter();
c.inc(); c.inc(); c.get(); // 2

4. Module pattern (IIFE)

An immediate function expression creates a scope that executes only once and returns only the members you want to make public.

const Logger = (() => {
  let level = "info";
  function setLevel(l) { level = l; }
  function log(msg) { console.log(`[${level}] ${msg}`); }
  return { setLevel, log };
})();

Core example

file Contents covered
01_scope.js Block/Function scope, let/const/var
02_closure.js Closure Basics
03_counter.js Private status counter
04_module_pattern.js IIFE module pattern

⚠️ Common Mistakes

  1. The loop variable declared as var is incorrectly captured in the setTimeout callback → Use let.
  2. Forget that GC is not possible because closures are holding memory (be careful of large amounts of data).
  3. Trying to change the closure variable directly from outside (impossible).
  4. function Confusing the difference between declaration hoisting and expression hoisting.
  5. Confusion due to shadowing external and internal variables of the same name.

❓ Frequently Asked Questions (FAQ)

Q1. When is a closure created? A. The moment a function is defined, if it references the external environment, a closure is created.

Q2. Are closures bad for performance? A. Generally negligible. However, if you hold a large object, memory leaks are possible.

Q3. Block scope of let and const? A. The effective scope is limited to { } units.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_scope.jsJavaScript
/**
 *scope
 */
"use strict";

const g = "global";

function outer() {
  const o = "outer";
  if (true) {
    const b = "block";
    console.log(g, o, b);
  }
  // console.log(b); // ReferenceError
}

outer();

//let is block scope
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log("i =", i), 0);
}
src/02_closure.jsJavaScript
/**
 *Closure Basics
 */
"use strict";

function makeGreeter(greeting) {
  return function (name) {
    return `${greeting}, ${name}!`;
  };
}

const hi = makeGreeter("Hi");
const hello = makeGreeter("Hello");
console.log(hi("Sun"));
console.log(hello("Moon"));
src/03_counter.jsJavaScript
/**
 *Private status counter
 */
"use strict";

function makeCounter(start = 0) {
  let count = start;
  return {
    inc() { return ++count; },
    dec() { return --count; },
    get() { return count; },
  };
}

const c = makeCounter(10);
c.inc();
c.inc();
c.dec();
console.log("count =", c.get()); // 11
src/04_module_pattern.jsJavaScript
/**
 *IIFE module pattern
 */
"use strict";

const Logger = (() => {
  let level = "info";
  function setLevel(l) { level = l; }
  function log(msg) { console.log(`[${level}] ${msg}`); }
  return { setLevel, log };
})();

Logger.log("start");
Logger.setLevel("warn");
Logger.log("careful");

Assignment

homework_01.js

Please write makeAdder(n). When called, it returns a function that takes x and returns x + n.

homework_02.js

Please write makeAccount(initial).

  • Expose deposit(amount), withdraw(amount), balance()
  • Balance cannot be accessed directly from outside
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

homework_01.jsJavaScript
/**
 *Task 1: makeAdder
 */
"use strict";

/**
 * @param {number} n
 */
function makeAdder(n) {
  return (x) => x + n;
}

const add5 = makeAdder(5);
console.log(add5(3));  // 8
console.log(add5(10)); // 15
homework_02.jsJavaScript
/**
 *Task 2: Private Balance Account
 */
"use strict";

/**
 * @param {number} initial
 */
function makeAccount(initial) {
  let balance = initial;
  return {
    deposit(amount) { balance += amount; },
    withdraw(amount) {
      if (amount > balance) throw new Error("insufficient");
      balance -= amount;
    },
    balance() { return balance; },
  };
}

const acc = makeAccount(1000);
acc.deposit(500);
acc.withdraw(300);
console.log(acc.balance()); // 1200

Learn this lesson with a mentor

Work through the JavaScript + Node.js track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now