Mastering JavaScript Functions: Understanding Execution Contexts, First-Class Functions, Higher-Order Functions, and Closures



How the functions work in Javascript
Pre Requisites
Execution Context :
Think of it as an environment where a code block runs, a box with variables, functions and more. It is a context (information) for lines of code to be executed. Execution context can be local and global. There can be multiple local contexts in a global context, just like boxes inside a bigger box. There can also be more execution contexts inside a local context and it could go on like a tree. Example : This code snippet
const x = 99
function one(){
let y = 88
function two(){
let z = 77
}
}
would look like this as execution context
How does this work?
Each execution context have their own memory block, so that they could store all the functions and variables even before they start to execute the code.
Example:
const x = 99
one()
function one(){
let x = 88
console.log('one executed ', x)
}
The above code snippet has one global and one local execution context, So this is how its memory block would look like:
Variable | Value |
---|---|
x | undefined |
one | f(){...} |
After the context is established, code execution will take place.
- First x is assigned 99 as a value
- function
one
gets executed and a new context is created.
The new context (local context for one
) looks like this:
Variable | Value |
---|---|
x | undefined |
After this, execution takes place:
- x is assigned 88 as a value
- A log will be visible on console :
one executed 88
.
Call Stack
Its a stack that runs along with execution context and contains current function calls with the Last In First Out (LIFO) principle. After the context is built for a function, it is pushed into the call stack and executed.
Example:
const x = 99
one()
function one(){
let x = 88
let y = `one executed ${successMessage()}`
function successMessage() {
return 'Successfully!'
}
console.log(y)
}
Call stack for this function will go like this:
- Firstly, global context is set and pushed to the call stack.
- Then,
one
gets called and its memory block gets formed, then its pushed into call stack and gets executed. - Inside
one
, there's another function which is calledsuccessMessage
, has to set up its context first and then jump to the call stack aboveone()
. The reason,one()
isn't popped off yet is because its still is providing its scope tosuccessMessage
. - After finishing execution
successMessage
, pops off. - After this,
one
pops off as well.
This is what the stack looks like :
Ways to use a function
There are three popular ways to declare a function in javascript
Declaration :
# IIFE (Immediately Invoked Functional Expression)
(function sum() {
let a = 6
let b = 8
console.log(a+b)
})()
# Functional Expression
const sum = function(a, b) {
return a+b
}
# Functional Statement
function sum (a, b) {
return a+b
}
# Arrow functions
const sum = (a, b) => {
return a+b
}
functional statement vs functional expressions
If you know how javascript engine works, you should know that the variables are stored first and then the statements get executed line by line.
So, logically the major difference between function expression and function statement is Hoisting.
Functional expressions are hoisted while the expressions are not. This happens because expressions have been declared already in phase 1 of JS code execution where Javascript stores the values of variables in the global scope. So while saving other variables, it also saves the function. Which explains the example below :
a() // Will print the log.
b() // Will throw the log as it is not executed.
const a = () => {
console.log('a is exec')
}
function b() {
console.log('b is exec')
}
More ways to use functions
Anonymous functional Statements
The functions without names (duh). Here is what it looks like:
function(){
}
The above function will throw an error because function statements always require a name. Although they could be used in functional expressions like this:
const sum = function() {
}
Named function Expressions
Just like the anonymous function expression, we can have named ones
const sum = function abc() {
}
But you can't just call them by their names, no, they're not some Harry potter villain but its just their existence isn't known to JS engine yet. So when you call abc()
, it will log you an error. Although you can access the function inside it recursively like this :
const sum = function abc() {
console.log(abc)
}
First Class Functions
Pre Requisite Knowledge : Parameters are local variables inside the function definition and the values that we pass when calling the function, are called arguments.
function Sum(a, b){ //Parameters
return a+b
}
Sum(2, 4) //Arguments
Coming back to the first class functions, If a function has ability to be passed as a variable or inside another function parameter, its called a first class function.
function prefix() {
return "Hello, and welcome to ";
}
function greeting(helloMessage, name) {
console.log(helloMessage() + name);
}
greeting(prefix, "JavaScript!"); // Hello, and welcome to JavaScript!
here, prefix is a first class function.
Higher order functions
Higher order function is a function that takes other smaller functions as an argument, and returns a function as well.
Example: map, filter, etc.
Why do we need it?
When we write multiple functions for multiple operations that have a part of same/similar logic, It causes code repetition and poor readability along with increased execution time. To overcome these problems, we use HOFs.
Example:
<!-- Here is the higher order function to do few operations on circle -->
const radii = [1, 2, 3, 4]
const circleOps = (arr, logic) => {
let newArr = []
for (let i = 0; i < arr.length; i++){
newArr.push(logic(arr[i]))
}
}
const area = (radius) => {
return Math.PI * radius * radius
}
const perimeter = (radius) => {
return Math.PI * 2 * radius
}
<!-- Returns an array of perimeters of given radii -->
console.log(circleOps(arr, perimeter))
As we can see above for all the operations that we do on circles, that needs radius, we have written a higher order function.
In future if we need to get perimeter, we could just pass perimeter logic along with the array of radius. This saved us multiple lines of for loops in each logic that could have ended up with bigger code and heavier js payload.
You might notice, how our function resembles with javascript map function which does the similar job.
Higher Order Function for debounce
function debounce(func, delay) {
let timeoutId;
return function (...args) {
const context = this;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
Closures
A function along with its lexical environment is called closure. A lexical environment is a scope that is around that function, for example:
function a() {
let s = 'Mohit'
function b() {
console.log(s) // Mohit
}
}
For function b, its lexical environment is inside of function a.
Coming back to closures, in previous example, b is a function, and b along with a is a closure.
There are many uses of closure in javascript since it has a unique tendency of remembering its surrounding. It could be used in memoization, currying and more.
Scope Chaining
When a statement can't find a variable in its scope, it tends to go upwards and find it until it reaches to global scope. This tendency of a variable is called scope chaining.
Example :
function a() {
let s = 'Mohit'
let x = 77
return function b() {
let x = 99
return function c(){
console.log(s) // Mohit
console.log(x) // 99
}
}
}
In the first console log, the js engine tries to find the value of s in the execution context of function c, but since there's no such variable, it treads beyond its scope and enters the execution context of function b, same happens and it is forced to move beyond b as well and finally, inside the memory block of the execution context of a, it does find the value of s and logs it.
For the second console log, 99 gets displayed because the order of scope chain is fom close to far, so it finds the closest value of x and logs it.
In the next article we will understand call-apply-bind, currying and more. Stay tuned with flashweb!
GG!