Closures vs Scope in JavaScript

KO

Original: Read on Medium This is a migrated version of the Medium post for this blog.

Introduction

When developing with JavaScript, you frequently encounter the terms closure and scope. They are both core concepts, and understanding them clearly can improve code readability and performance.

In this post, we will look at the differences between scope and closure in detail, with practical examples. We will cover patterns such as private state with closures, this binding, and recursive closure behavior.

What Are Scope and Closure?

Scope is the region of code where a variable is defined and accessible. In JavaScript, scope is determined by where a variable is declared.

  • Global scope: Variables declared outside functions or blocks can be accessed anywhere.
  • Local scope: Variables declared inside functions or blocks are only accessible there.

Closure, on the other hand, is a function that can access both its own scope and its parent function's scope. A closure is created when a function is defined inside another function, and it can still "remember" outer variables even after the outer function returns.

The following code demonstrates this clearly:

image

In this example, inner is a closure because it can still remember and access x after outer has returned.

In short: scope defines where variables are accessible, while closures allow a function to retain access to variables from its creation environment.

Closure Examples

1. Returning a closure that returns an argument from the outer function

Example:

function outer(arg) {
  return function inner() {
    return arg;
  };
}

outer receives arg and returns a new function inner. Through closure, inner can access and return arg.

const getArg = outer('hello');
console.log(getArg()); // prints 'hello'

Even after outer has returned, inner can still access arg.

2. Creating private variables with closures

Closures are a common way to build private variables in JavaScript through scope and encapsulation.

image

In this pattern, Counter returns an object with methods such as increment and getCount, while privateCount remains inaccessible outside the function scope.

This is a standard encapsulation approach in JavaScript for private-like state management.

3. How closures interact with this

this refers to the current execution context of a function. It can point to different objects depending on how the function is called.

When a closure is created, it captures lexical scope, and this behavior still depends on invocation context unless explicitly bound.

Closure and this interaction example:

image

Execution points:

  • outer sets this.name to 'outer'.
  • inner is created as a closure inside outer.
  • When innerFunc is called, this refers to the object passed to call(), such as { name: 'newContext' }.
  • Even though inner was created in another context, the invocation context controls the current this unless it is lexically bound (for example, with arrow functions).

Key points:

  • Closures preserve lexical variables from their creation scope.
  • this is not automatically preserved in the same way as lexical variables.
  • Use bind(), call(), or apply() when explicit this binding is required.

4. Returning a closure that logs call count

Here is an example that logs how many times a wrapped function is called:

image

How it works:

  • logCallCount receives a function fn.
  • It initializes callCount to 0.
  • It returns a wrapper function.
  • Each wrapper invocation increments callCount and logs it.
  • The original function is called via apply() to preserve context and arguments.

Usage example:

const loggedAdd = logCallCount(function add(a, b) {
  return a + b;
});
 
console.log(loggedAdd(1, 2)); // logs call count, then returns 3
console.log(loggedAdd(3, 4)); // logs call count, then returns 7

loggedAdd wraps the original add function. Every call logs invocation count and returns the original result.

5. Closures in recursive functions

Closures in recursive functions work similarly to non-recursive closures. The important difference is that recursive calls create new scopes repeatedly, and closures can access state across those calls.

image

How it works:

  1. recursiveClosure is called, sets x, initializes count, defines inner, and returns it.
  2. inner accesses variables from recursiveClosure through closure.
  3. recursiveFunc stores the returned inner.
  4. Calling recursiveFunc(3) executes inner, increments count, and logs state.
  5. inner recursively calls itself while decrementing y.
  6. Recursion ends when y reaches 0.

Final output sequence:

  • First call: Call 1: x = 5, y = 3
  • Second call: Call 2: x = 5, y = 2
  • Third call: Call 3: x = 5, y = 1
  • Fourth call: Call 4: x = 5, y = 0