This content originally appeared on DEV Community and was authored by Bibi
TL/DR:
Closures are a synthetic inner delimiter that inherits its parent's scope, even after the parent function has finished executing.
Not clear? I didn't get it either. Read along partner!
đź”’ Closures đź”’
Ah, closures. Sounds pretty straight forward... whatever's inside the curly braces right?! Well, yes and no. This topic is quite simple but there's a lot to explore in it, you'll wonder whether you accidentally stumbled into a parallel universe after reading, I guarantee. But fear not, my fellow wizards, for today we shall unravel the enigma that is closures!
First, let's start with the textbook definition because I love a good MDN reference:
a closure is a function that has access to variables in its outer (enclosing) lexical scope, even after the outer function has returned. Kinda like having the key to a treasure chest of variables, even when you've left the pirate ship!
Okay that wasn't MDN verbatim, but you get the idea. So, how does this sorcery work?? Well, when you create a function within another, the inner one has access to the variables and parameters of the outer function. This is because the inner function forms a closure, maintaining access to the environment in which it was created. It's like the inner function remembers its surroundings!
Here's a classic example to illustrate closures:
In this example, outerFunction
takes a parameter x
and has a local variable y
. It defines an innerFunction
that accesses both x
and y
, and then returns the innerFunction
. When we assign the result of calling outerFunction(5)
to the variable closure, we are essentially capturing the innerFunction
along with its environment (where x
is 5 and y
is 10). Even though outerFunction
has finished executing, the closure still remembers the values of x
and y
, allowing us to invoke it later and get the expected output of 15.
This was just a simple example, but closures actually have various practical applications, such as data privacy, factories, and memoization. They allow you to create functions with private state and encapsulate behaviour, leading to more modular and reusable code. Let's do a deeper dive into these specific applications:
1. Private Variables and Encapsulation đź”’
Closures can be used to create private variables and achieve encapsulation. Consider the following example:
In this example, the createCounter
function returns an object with an increment
method. The count variable is defined within the createCounter
function and is not accessible from the outside. The increment
method, being a closure, has access to the count
variable and can modify it. Each time counter.increment()
is called, the count
is incremented, but it remains private and cannot be accessed directly from the outside.
2. Function Factories đźŹ
Closures can be used to create function factories, which are functions that generate other functions with customized behaviour. Here's an example:
In this case, the multiplyBy
function takes a factor
parameter and returns a new function that multiplies a given number
by the factor
. We create two separate functions, double
and triple
, by calling multiplyBy
with different factors. Each returned function forms a closure, capturing its own factor
value, allowing us to multiply numbers by the respective factors.
3. Memoization ⏰
Closures can be used to implement memoization, which is a technique to cache the results of expensive function calls and return the cached result when the same inputs occur again. Here's an example of memoizing a factorial function:
In this example, the memoizedFactorial
function returns a closure that serves as the actual factorial
function. The closure maintains a cache
object to store previously computed results. Whenever the factorial
function is called with a number n
, it first checks if the result is already in the cache. If it is, it returns the cached result. Otherwise, it calculates the factorial recursively and stores the result in the cache before returning it. Subsequent calls with the same n
will retrieve the result from the cache, avoiding redundant calculations.
These examples are obviously simple but the applications of closures in JavaScript can be much more complex. They can provide a powerful mechanism for data privacy, code organization, and optimization, when done right!
However, closures can also lead to some gotchas if not used carefully. One common pitfall is creating closures in loops, where the closure captures the last value of the loop variable. But that's a story for another day!
Alright, I think that's it for me ┗(・ω・;)┛
Here are some key points to take home:
- A closure is a function that remembers the environment in which it was created. It has access to variables and parameters of the outer function.
- It allows a function to access variables from its outer (enclosing) scope, even after the outer function has finished executing.
- Closures can access and manipulate variables from the outer scope, even after the outer function has returned.
Hope you learned something with me today! (´◡`)
Bibi
This content originally appeared on DEV Community and was authored by Bibi
Bibi | Sciencx (2024-06-26T03:38:46+00:00) Closures in JS đź”’. Retrieved from https://www.scien.cx/2024/06/26/closures-in-js-%f0%9f%94%92/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.