Lexical Scope, Closures and Currying in JavaScript
This post describes the Lexical Scope, Closures and Currying Function in JavaScript and their relations with examples.
Lexical Scope
Variables defined using var
keywords are having function scope
which means new scope is created everytime you create a new function.
Lexical scope is associated with function scope. Lexical scope of variables is defined by their position in source code. JavaScript resolves the variable starting at the innermost scope and searches outwards until it finds the variable it was looking for.
var me = "global";
function whoami(){
var me = "local";
function func() {
return me;
}
return func;
}
console.log(me); // global
console.log(whoami()()); // local
The value of the variable me
outside is global
whereas if you execute function whoami()
from outside, value is local
Closures
Closure is very important concept and frequently asked in JavaScript interviews.
A closure is an inner function having access to its outer function scope and all above scopes even when that function is executing outside of its outer function.
When you define an inner function inside outer function, Closure is created at runtime for inner function bundled with outer function’s scope.
Let’s look at the example to understand Closures
var outerFunc = function(c){
var a = 1;
var innerFunc = function(d) {
var b = 2;
var innerMostFunc = function(e) {
return a + b + c + d + e;
}
return innerMostFunc;
}
return innerFunc;
}
console.dir(outerFunc(3)); //1. innerFunc
console.dir(outerFunc(3)(4)); //2. innerMostFunc
console.log(outerFunc(3)(4)(5)); //3. 15
Output
▼ ƒ innerFunc(c)
length: 1
name: "innerFunc"
arguments: null
caller: null
➤ prototype: {constructor: ƒ}
➤ __proto__: ƒ ()
[[FunctionLocation]]:
▼ [[Scopes]]: Scopes[2]
➤ 0: Closure (outerFunc) {c: 3, a: 1}
➤ 1: Global {parent: Window, opener: null, top: Window, length: 1, frames: Window, …}
▼ ƒ innerMostFunc(c)
length: 1
name: "innerMostFunc"
arguments: null
caller: null
➤ prototype: {constructor: ƒ}
➤ __proto__: ƒ ()
[[FunctionLocation]]:
▼ [[Scopes]]: Scopes[3]
➤ 0: Closure (innerFunc) {d: 4, b: 2}
➤ 1: Closure (outerFunc) {c: 3, a: 1}
➤ 2: Global {parent: Window, opener: null, top: Window, length: 1, frames: Window, …}
15
We have three console dir/log. Let’s discuss them one by one:
-
innerFunc
has a closure of variables defined or passed as argument inouterFunc
0: Closure (outerFunc) {c: 3, a: 1}
-
innerMostFunc
has a closure of variables defined or passed as argument in inouterFunc
andinnerFunc
i.e.0: Closure (innerFunc) {d: 4, b: 2} 1: Closure (outerFunc) {c: 3, a: 1}
-
innerMostFunc
returnsa+b+c+d+e=15
where- value of a and c is coming from
Closure (outerFunc)
- value of b and d is coming from
Closure (innerFunc)
- value of e is coming from passed argument
- value of a and c is coming from
They way we called outerFunc(3)(4)(5)
is also known as currying
Currying
One of the use case of Closure
is currying functions.
A currying function is a function where you break down a function that takes multiple argument one at a time instead of taking all argument at once.
f(a, b, c) => Currying => f(a)(b)(c)
Let’s convert this function
var add = function(a, b, c){
return a + b + c;
}
add(1, 2, 3); //6
to currying function
var add = function(a){
return function(b){
return function(c){
return a + b + c;
}
}
}
add(1)(2)(3); //6
One more example:-
var sayWhat = function(a){
return function(b){
return function(c){
console.log(`say ${a} to ${b} using ${c}`);
}
}
}
sayWhat("hello")("friends")("currying function");
Output
say hello to friends using currying function
In currying function, each nested function is keeping track of arguments passed in outer function in their closure,