Exploring Closures with JavaScript Functions

Uriel Rodriguez
3 min readFeb 2, 2021

Continuing with the theme of functions and their many lesser-known features, closures are one of the more important and powerful features to be aware of. In understanding them, we first have to start with the idea of a namespace, which is a space where variables bindings are made. These bindings are protected from any outer bindings that may share the same variable names and risk overwriting the bindings.

// global namespace
let scope = "global"; // first variable binding
if (true) { // if statement namespace
let scope = "local";
console.log(scope); // => local
}
console.log(scope); // => global

In this example, a variable binding is made in the global namespace, however another variable binding is made with the same variable name “scope.” Because the second “scope” variable binding is made in a nested if-statement namespace, it does not affect the global namespace variable binding. In other words, a namespace can be thought of as an environment which is made up of variable bindings, also known as a scope. These scopes are defined by the block syntax which encapsulates code making anything defined within that block unique to that namespace.

{ // block syntax }

Control logic, like if-statements, loops, all create namespaces which again, encapsulates variable bindings and make them unique to that environment. Functions also create namespaces, or scopes which can be remembered by their curly brackets block syntax. When a function is declared, if that function is defined with a parameter and other local variables, then those variables become a part of that function’s namespace.

let scope = "global";function funcScope(scope) {
scope += " scope";
console.log(scope);
}
console.log(scope); // => global
funcScope("func"); // => func scope

The function funcScope() defined its own “scope” variable and manipulated it to concatenate the “ scope” string and it did not affect the outer global namespace variable binding. Again, this is to iterate that namespaces create a protected environment but also establishes a scope.

Another important idea to understand is that of lexical scoping. In short, this means that functions refer to the variable bindings that were in place when they were defined, and not the variable bindings that are in place when they are invoked. So, an example:

let scope = "global";function funcScope(scope) {
console.log(scope);
}
funcScope(); // => undefined

By default when a parameter is defined or declared, like any other variable, JavaScript assigns it the value of “undefined”. When funcScope() is invoked, it referred to variable binding that was present during its definition, its own “scope” variable, the parameter. Because the value was left at the default value or “undefine”, it referred to that variable binding when invoked.

let scope = "global";function funcScope() {
console.log(scope);
}
funcScope(); // => global

In this example, because the only “scope” variable binding present during the funcScope() definition is the global namespace “scope” variable binding, it refers to that value when invoked and logs it.

Functions are able to keep tract of the variable bindings present during their definitions which help them to refer back to those bindings when invoked. The combination of a function, it’s block code and its reference to the variable bindings, or the scope in which it is defined, is known as a closure.

let scope = "global";function outerFunc() {
let scope = "outerFunc";
return () => console.log(scope);
}
outFunc()(); // => outerFunc

This example demonstrates a closure, which is the environment is the combination of the inner function’s code block which logs to the console and the variable bindings present during the functions definition. The return inner function maintain a reference to its scope, when it is defined, which includes the “scope” variable binding to the value “outerFunc.” And so, despite the fact that when the inner function is invoked, there exists a “scope” variable binding to “global”, it will log the variable binding value present during its definition.

Before JavaScript had a built-in system for modules, closures were used to create them. In fact closures are foundational to functional programming which is how modern React is carried out. Understanding closures help to carry out many powerful techniques, including protecting data from outside manipulation. To read more about closures, the link below which provides great documentation, and helps to get you started.

--

--

Uriel Rodriguez

Flatiron School alumni and Full Stack web developer.