JavaScript is partly a functional language.
To learn JavaScript, we got to learn the functional parts of JavaScript.
In this article, we’ll look at how to use closures.
Closures
Closures are inner functions.
An inner function is a function within a function.
For example, it’s something like:
function outer() {
function inner() {}
}
Closures have access to 3 scopes.
They include variables that are declared in its own declaration.
Also, they have access to global variables.
And they have access to an outer function’s variable.
For example, if we have:
function outer() {
function inner() {
let x = 1;
console.log(x);
}
inner();
}
then the console log logs 1 because we have x
inside the inner
function and we access it in the same function in the console log.
The inner
function won’t be visible outside the outer
function.
We can also access global variables within the inner
function.
For example, if we have:
let global = "foo";
function outer() {
function inner() {
let a = 5;
console.log(global)
}
inner()
}
Then 'foo'
is logged since inner
has access to the global
variable.
Another scope that inner
has access to is the scope of the outer
function.
For example, we can write:
function outer() {
let outer = "outer"
function inner() {
let a = 5;
console.log(outer);
}
inner()
}
We have the outer
variable and we access it in the inner
function.
Closure Remembers its Context
A closure remembers its context.
So if we use it anywhere, the variables that are in the function are whatever they are within the original context.
For example, if we have:
const fn = (arg) => {
let outer = "outer"
let innerFn = () => {
console.log(outer)
console.log(arg)
}
return innerFn;
}
Then the outer
and arg
variable values will be the same regardless of where it’s called.
outer
is 'outer'
and arg
is whatever we passed in.
Since we return innerFn
with fn
, we can call fn
and assign the returned function to a variable and call it:
const foo = fn('foo');
foo()
We pass in 'foo'
as the value of arg
.
Therefore, we get:
outer
foo
from the console log.
We can see that the values are the same even if we called it outside the fn
function.
Real-World Examples
We can create our own tap
function to let us log values for debugging.
For example, we can write:
const tap = (value) =>
(fn) => {
typeof(fn) === 'function' && fn(value);
console.log(value);
}
tap("foo")((it) => console.log('value:', it))
to create our tap
function and call it.
We have a function that takes a value
and then returns a function that takes a function fn
and runs it along with the console log.
This way, we can pass in a value and a function.
Then we get:
value: foo
foo
logged.
The first is from the callback we passed in.
And the 2nd is from the function we returned with tap
.
Conclusion
Closures are inner functions.
They have access to the outer function’s scope, global variables, and their own scope.
We can use it or various applications.