DEV Community

Cover image for What are Closures good for?
Red
Red

Posted on

What are Closures good for?

What is a Closure?

A Closure is an inner function that makes reference to the environment in it's outer function.
A simple closure looks like this:

function outer(){
   let name = "John Doe";
   function inner(){
      return name;
   }
   return inner();
}  
>> outer() // returns "John Doe"
Enter fullscreen mode Exit fullscreen mode

Closures can be used in JavaScript, Python, Ruby, Swift, C#, Objective C and a few of other languages. I'll stick to using simple JavaScript examples as it's similar to most languages and is easy to understand.
I'll also give Python examples when the JavaScript approach won't work the same way.

outer is a regular function, while inner is a closure since it was defined inside outer and has access to it's variables.

Closures have access to:

  1. Their local scope(variables defined inside the inner function)
  2. The scope of their outer functions
  3. The global scope

Now, let's use a more intuitive example to understand how to use closures:

function setGreeting(greeting){
   function setName(name){
      return `${greeting}, ${name}`;
   }
   return setName;
}
Enter fullscreen mode Exit fullscreen mode

Quite self-explanatory function, the outer function takes a greeting as an argument, and the inner function takes a name as an argument and returns the greeting and name together.
You might notice that unlike the first example, in this case, the outer function returns the inner function without calling it - without the parentheses.
We'll see how this affects the code and how we call the functions.

>> let welcome = setGreeting("Welcome")
>> // This sets greeting="Welcome"
>> typeof(welcome) // returns "function"
>> welcome // prints f setName(name){...}
>> // So welcome is equivalent to setName
>> // We can call it with the "name" argument
>> welcome("John") // prints "Welcome, John"
>> welcome("Joan") // prints "Welcome, Joan"
Enter fullscreen mode Exit fullscreen mode

We can see from this that the first call to setGreeting sets the greeting, and returns the inner function for use. This means we can use welcome just like it were setName. This can be very useful in cases where we need multiple functions that do something similar with different contexts, rather than creating a function for each greeting, we create one function to set the greeting, and an inner function to print the greeting and name.
That might sound like a bit much to take in, so let's look at practical use cases.

Creating functions with Context

You're filling a form for all graduating students in your department. There's a lot of details and most of them seem to be general for everyone, like department, isAdult, certificate, country, race, faculty etc. With only very few unique fields like fullName and stateOfOrigin.
You could easily create a Closure where all the general fields are set by the outer function, and the inner function only takes the unique fields:

function setGeneral(general){
   // general is an object containing all general details
   function setUnique(fullName, stateOfOrigin){
      // We use the spread operator "..." 
      // to add fullName and stateOfOrigin to the object
      fillForm({...general, 
         fullName: fullName, 
         stateOfOrigin: stateOfOrigin
         });
   }
   return setUnique;
}
>> cs = {department:"Computer Science",
>> isAdult: true,
>> certificate: "B.Sc. Comp",
>> country: "Nigeria",
>> race: "Negro",
>> faculty: "ICT"};
>> csStudent = setGeneral(cs);
>> // Now we can use csStudent to easily fill forms
>> csStudent("John Doe", "Lagos")
>> csStudent("Ciroma Chukwuma", "Abuja")
Enter fullscreen mode Exit fullscreen mode

The syntax for Python is similar, using a dictionary, we'd update it with the fullName and stateOfOrigin keys using general.update({"fullName":fullName, "stateOfOrigin": stateOfOrigin})

Creating private attributes

Languages like Java give you the option to make certain attributes(properties) private. That's not the case for JavaScript or Python. But we can enforce that using closures.

function Person(){
   let name = "";
   let age = 0;

   function setName(name){
      name = name;
   }
   function getName(){
      return name;
   }
   function grow(){
      age += 1;
   }
   function getAge(){
      return age;
   }

   accessible = {setName: setName,
                 getName: getName,
                 grow: grow,
                 getAge: getAge};
   return accessible;
}
>> john = Person()
>> john.setName("John")
>> john.grow();
>> john.grow();
>> john.getName() // prints "John"
>> john.getAge() // prints 2
>> john.name // undefined
>> john.age // undefined
Enter fullscreen mode Exit fullscreen mode

This is a simple Object Oriented application of closures, which can be used to imitate private attributes.
The function returns an object(dictionary) of functions to be accessible from outside the Person function, thus making them accessible by dot notation(i.e john.grow()). While keeping the attributes not returned - name and age - inaccessible outside the Person function.

Python dictionary keys can't be accessed using dots, so here's a way you can do the same in Python

from types import SimpleNamespace
def Person():
   #same code, but in Python
   def grow():
      nonlocal age
      #python needs to know age is not a local
      #variable before directly operating on it
      age += 1
   accessible = {}#same thing as well
   return SimpleNamespace(**accessible)
# And that's it, SimpleNamespace will make them 
# accessible using dot notation
Enter fullscreen mode Exit fullscreen mode

Summary

With a better understanding of Closures, we can say closures are functions that keep their namespace(variables and or functions) after execution. Making it possible to setup an initial environment first before usage.
Keep in mind that closures involve multiple functions, making your code a bit slower and more memory consuming. Try to avoid using them if you're not getting the benefits peculiar to Closures.
Otherwise, enjoy.

Latest comments (2)

Collapse
 
iambenkay profile image
B_aaS • Edited

I think you forgot to return the setUnique Function in setGeneral

Collapse
 
redmartian profile image
Red • Edited

Oh, I didn't read that well.
My bad, thanks