What are Closures good for?
Red
Posted on November 13, 2020
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"
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:
- Their local scope(variables defined inside the inner function)
- The scope of their outer functions
- 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;
}
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"
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")
The syntax for Python is similar, using a dictionary, we'd update it with the
fullName
andstateOfOrigin
keys usinggeneral.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
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
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.
Posted on November 13, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.