Callbacks & Closures
July 26, 2021
Content |
---|
Closures & callbacks |
Closures & function scope |
Closures & re-usable callback functions |
About closures
Every function in JavaScript is bound with references to its own surrounding state or lexical environment. A closure happens whenever an outer function's local scope is kept alive, eventhough the function has finished executing. As a result, the inner function gains access to the local scope of its outer function.
A closure example
The code below executes function fn1; which in turn calls and executes function fn2. By the time fn1 finished executing, and fn2 begins, you have a closure.
function fn1() {
var x = 1,
y = 2;
function fn2() {
console.log(x + y);
}
fn2();
}
fn1();
>$ 3 //return value
Function fn2 is defined within function fn1, and whenever you see a function, that it's defined within another function, the inner function has access to the scope of the outer function.
Closures in Callbacks
A callback is an example of a closure as it retains its outer function lexical environment after it's finished executing. A callback is invoked or called after something else happens. This is done by passing in a function into another one.
Here introFn
is the callback, and it'll be executed after 2s.
let introFn = function() {
console.log('introFn was called');
}
setTimeout(introFn, 2000);
Normally, you don't define the function before calling it. What you do is pass in an anonymous function instead.
setTimeout(function() {
console.log('function was called back');
})
Closures and function scope
Closures are closely related to function scope, and this determines whether you'll be able to access the local scope of a function after it's finished executing or not.
Here below there are three functions. Two main functions: fn1 and fn3, that share the same global scope. Fn2
is defined inside of fn1 and because it's defined inside of fn1, it has access to the scope of fn1. It has access to x and y.
Fn3
, on the other hand is defined outside of fn1 so it doesn't have access to the scope of fn1, and as a result it can't access
x or y.
let fn1 = function() {
var x = 1,
y = 2;
let fn2 = function() {
console.log(x + y);
}
fn2();
}
let fn3 = function() {
console.log(x + y)
}
fn1();
fn3();
So if you call fn1 and fn3, fn1 returns 5 and fn3 undefined because is trying to access variables that were defined inside of fn1, and fn3 is outside the scope of fn1.
Now, this example above is shown often to explain how a closure works, but the example below makes
more sense to me. It's clear below that fn2
accesses fn1 local scope (x and y) only 1 second after fn1
has finished executing.
let fn1 = function() {
var x = 1,
y = 2;
let fn2 = function() {
console.log(x + y);
}
setTimeout(fn2, 1000);
}
fn1();
Closure in a re-usable callback function
Here the closure is the students's array callback function.
Instead of hard-coding what to do with the students data inside of the process function, I pass in a callback function
for each object contained within
students array.
let students = [
{name: 'joe', score: 88},
{name: 'jess', score: 90},
{name: 'jon', score: 82}
];
let process = function(data, callback) {
data.map(function(itm) {
callback(itm)
});
}
Now the process function can remain the same, while still able to access the data passed into process function, and make changes to it inside of callback function.
process(students, function(obj) {
if(obj.score > 80) {
console.log(obj.name + " " + obj.score);
}
})