DEV Community

Cover image for A Quick Guide To: Closures
Adriana DiPietro
Adriana DiPietro

Posted on

A Quick Guide To: Closures

Hi programmers! Today we will discussing closures in JavaScript. Every heard of them? Whether you have or haven't, I am positive you have used them.

Without giving too much away, closures are an undeniably necessary concept in order to understand your code and JavaScript as a whole.

Curious to know why? Let's get started.


Learning Goals

  1. What is a Closure?
  2. The Benefits of using Closures
  3. Closure Examples
  4. Summary + Recap

What is a Closure

A closure is the combination of a function and the environment in which it was declared.

Considered one of the pillars of JavaScript, closures allow a function to access variables from an enclosing scope.

In other words, a closure gives you access to an outer function's scope from an inner function.

When I said that I was positive that you have used closures before, it is because every function in JavaScript forms a closure.

Lexical Scope

You may have noticed I used the term "enclosing" in my description of closure -- let's unravel this by learning what lexical scope is.

Simply, lexical scope is the accessibility of variables dependant on where the function is declared; NOT where the function is invoked (or called).

For example:


// declare function myFunc()
function myFunc(){
    // declare and assign variable myName
    // myName is in the scope of myFunc()
    const myName = 'Adriana'

    // declare printName()
    function printName(){
      // log the value of variable myName to the console
      console.log(myName)
    }
    return printName()
}


// invoke myFunc()
myFunc()
Enter fullscreen mode Exit fullscreen mode

In the above snippet, the function printName() is lexically scoped. printName() has access to the variable "myName" because we invoke and return printName() within the myFunc()'s scope.

So, when we invoke myFunc(), the console outputs "Adriana".

JavaScript is cool in the way that we can access a variable declared outside of a function within that function. A lot of languages actually do not allow this behavior.

Functions as First-Class Citizens

Want to know what else is cool about functions in JavaScript?

Functions in JavaScript are First-Class Citizens. What does this mean? It is a fancy way to say that:

  • Functions can be passed as a parameter to another function.
  • Functions can be the return value of a function.

Ultimately, functions are flexible and extremely worth utilizing!


The Benefits of using Closures

Now that we got the idea of what a closure is and how they work -- what are the benefits?

Because of closures, the JavaScript Engine (V8) ensures that the function has access to all of the variables by keeping all the variables declared outside of the innermost function.

JavaScript is lexically and statically scoped -- so when JavaScript compiles and looks through your code the first time, the JavaScript Engine (V8) catalogs what needs to be closured even before the code is executed or ran.

So, the benefits?

  • Closures save memory space.
  • Closures allow for encapsulation.
  • Closures reduce bugs.

Encapsulation

Encapsulation is the hiding of information so as to not be manipulated by the "outside" world or global scope.

We use encapsulation because we do NOT want to give access to certain functions to users nor allow certain variables (such as something that stores state) to be modified.

By nesting an inner function within another function, like our example above, we make sure it is not totally accessible.

Basically, encapsulation ensures that some data is not exposed. Encapsulation promotes the idea of privilege in terms of access to data (or state) or the functions that have access to that data.


Closure Examples

Now that we have acknowledged a textbook definition of closures, let's code this out (we are programmers, aren't we?).

Example 1

First, let's declare a function "a" and within "a" declare a variable "cat" set to a string "Charlie":

function a(){
    let cat = 'Charlie'
}
Enter fullscreen mode Exit fullscreen mode

Since, we are not returning anything, if we invoked a() we would get undefined. However, our "cat" variable was declared within the function's scope and thus, it is in our memory heap (thanks V8 engine!).

In order to be a closure, we must return ** and **declare a function within another function:

function a(){
    let cat = 'Charlie'
    return function b(){
        let dog = 'Zoie'
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's declare AND return function "b" below our cat variable declaration. Within function "b" we declare another variable "dog" and set it to the string "Zoie". Function "b"'s scope now contains access to variable "dog" while also containing access to variable "cat".

So, if we wanted to return the value of variables "cat" and "dog" within the scope of function "b", we could do this:

function a(){
    let cat = 'Charlie'
    return function b(){
        let dog = 'Zoie'
        return `${cat} - ${dog}`
    }
}
Enter fullscreen mode Exit fullscreen mode

In your console or sandbox, paste the above code snipped and then invoke/call the functions in this order:

  1. a()
  2. b()
  3. a()()

What did your console output?

  • Invoking a() returns function b().
  • Invoking b() returns an error "b is not defined".
  • Invoking a()() returns our string literal.

Example 2

Let's code out another example:


function outer(){
   let state = 0

}
Enter fullscreen mode Exit fullscreen mode

Here I have declared a function "outer" and declared and assigned a variable "state" to the value of 0.

If we declare and return another function within outer(), can we access "state"?


function outer(){
   let state = 0
   return function inner() {
      return `Our state is currently at: ${state}.`
   }
}
Enter fullscreen mode Exit fullscreen mode

Yes! If we paste this code snippet into our sandbox and call outer() and inner(), what is our output?

  • Invoking outer() returns the function inner().
  • Invoking inner() returns an error "inner is not defined".
  • Invoking outer()() returns our string literal with the value of "state."

In both examples, we are able to access the inner function by invoking the outer function. When we double-invoke the outer function, we are able to access the string literals. Why? By enclosing the inner function in the outer function's scope, we can only invoke it internally. So, we must use the outer scope as a bridge to receive the return value of the inner scope.

So, how were we able to access the variables in the return statement? JavaScript's Engine (V8) reads through the code from outer function to inner function and shoves the variables into the closure (think of a box). The variables are stored for later use and are never removed because V8 sees that the variables are referenced in another function within the outer function's scope.


Summary + Recap

From this article, let's bundle and condense some of the takeaways:

  • JavaScript's Engine is V8.
  • V8 runs and compiles our code.
  • Functions in JavaScript are lexically scoped.
  • Lexical Scope: the accessibility of variables dependant on where the function is declared; NOT where the function is invoked (or called).
  • Functions in JavaScript are First-Class Citizens.
  • Variables declared in the global context OR within an enclosing scope are stored in the memory heap.
  • Closures allow a function to access variables from an enclosing scope even after it leaves the scope in which the function is declared.
  • Because of closures, the JavaScript Engine ensures that an inner function has access to all of the variables within its enclosing scope.
  • Encapsulation = privacy or reduced accessibility of data.

Still not sure about closures? Here are some resources I used to help me further understand:

Thank you for reading along and coding with me :)
Have any questions, comments or suggestions? Leave them below.

Remember: always code in the way that is best for your learning.

Top comments (1)

Collapse
 
kumarkalyan profile image
Kumar Kalyan • Edited

Nice! This article is easy to understand !