JavaScript Scope Closures

Help us to keep this website almost Ad Free! It takes only 10 seconds of your time:
> Step 1: Go view our video on YouTube: EF Core Bulk Extensions
> Step 2: And Like the video. BONUS: You can also share it!

Example

When a function is declared, variables in the context of its declaration are captured in its scope. For example, in the code below, the variable x is bound to a value in the outer scope, and then the reference to x is captured in the context of bar:

var x = 4; // declaration in outer scope

function bar() {
    console.log(x); // outer scope is captured on declaration
}

bar(); // prints 4 to console

Sample output: 4

This concept of "capturing" scope is interesting because we can use and modify variables from an outer scope even after the outer scope exits. For example, consider the following:

function foo() {
    var x = 4; // declaration in outer scope

    function bar() {
        console.log(x); // outer scope is captured on declaration
    }

    return bar;
    
    // x goes out of scope after foo returns
}

var barWithX = foo();
barWithX(); // we can still access x

Sample output: 4

In the above example, when foo is called, its context is captured in the function bar. So even after it returns, bar can still access and modify the variable x. The function foo, whose context is captured in another function, is said to be a closure.

Private data

This lets us do some interesting things, such as defining "private" variables that are visible only to a specific function or set of functions. A contrived (but popular) example:

function makeCounter() {
    var counter = 0;

    return {
        value: function () {
            return counter;
        },
        increment: function () {
            counter++;
        }
    };
}

var a = makeCounter();
var b = makeCounter();

a.increment();

console.log(a.value());
console.log(b.value());

Sample output:

1
0

When makeCounter() is called, a snapshot of the context of that function is saved. All code inside makeCounter() will use that snapshot in their execution. Two calls of makeCounter() will thus create two different snapshots, with their own copy of counter.

Immediately-invoked function expressions (IIFE)

Closures are also used to prevent global namespace pollution, often through the use of immediately-invoked function expressions.

Immediately-invoked function expressions (or, perhaps more intuitively, self-executing anonymous functions) are essentially closures that are called right after declaration. The general idea with IIFE's is to invoke the side-effect of creating a separate context that is accessible only to the code within the IIFE.

Suppose we want to be able to reference jQuery with $. Consider the naive method, without using an IIFE:

var $ = jQuery;
// we've just polluted the global namespace by assigning window.$ to jQuery

In the following example, an IIFE is used to ensure that the $ is bound to jQuery only in the context created by the closure:

(function ($) {
    // $ is assigned to jQuery here
})(jQuery);
// but window.$ binding doesn't exist, so no pollution

See the canonical answer on Stackoverflow for more information on closures.



Got any JavaScript Question?