Scope and Closures / Callback Hell

 1) Scope:


1) Scope in JavaScript actually determines the accessibility of variables and functions at various parts in one’s own code or program.

2) Scope will help us to determine a given part of a code or a program, what variables or functions one could access and what variables or functions one cannot access.

3) Within a scope itself, a variable or a function, or a method could be accessed. Outside the specified scope of a variable or function, the data cannot be accessed.

4) There are three types of scopes available in JavaScript: Global Scope, Local / Function Scope, and Block Scope. 


//global scope
let global = "Swati";


function func1() {
return global;
}


function func2() {
return func1();
}
console.log(func2());







//local scope
function func1() {
let a = 2;
let multiply = function () {
console.log(a * 5);
}
multiply();
}


func1();







//Block scope
{
let a = 5;
}
console.log(a);















2) Scope Nesting:


//Block scope nesting
{
let name = "Swati";
{
console.log(name);
let nextName = "Roshni";
{
console.log(nextName);
console.log(name);
}
}
//console.log(nextName);
}








//function scope nesting
function func1() {
let name = "Swati";
function func2() {
console.log(name);
}
func2();
}
func1();










3) Closures:


pre-requisites:


1) Nested Function: In JavaScript, a function can also contain another function. This is called a nested.


2) Returning a function: In JavaScript, you can also return a function within a function. 



function func() {
let local = 3;
function nested_func() {
console.log(local);
}
return nested_func;
}
let funcCall = func();
console.log(funcCall);
funcCall();












function func() {
let local = 3;
function nested_func(x) {
console.log(local + x);
}
return nested_func;
}


let funcCall = func();
/*
func()();*/
funcCall(2);
funcCall(3);









Closures: A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). 


In JavaScript, closure provides access to the outer scope of a function from inside the inner function, even after the outer function has closed. 


Or in other words any function that has access to the scope of the parent function even when the parent function is closed is called closure.


function greet() {
let name = 'Swati';
function displayName() {
return 'Hi' + ' ' + name;
}
return displayName;
}
let g1 = greet();
console.log(g1);
console.log(g1());








function calculate(x) {
function multiply(y) {
return x * y;
}
return multiply;
}
let multiply1 = calculate(3);
let multiply2 = calculate(4);
//console.log(multiply1);
//console.log(multiply2);
// console.log(multiply1());//Nan
// console.log(multiply1(6));//18
console.log(multiply2(2));//8


Here multiply1 and multiply2 are closures.





let a = 0;
function sum() {
function incSum() {
return a = a + 1;
}
return incSum;
}
let x = sum();
console.log(x());//1
console.log(x());//2
console.log(x());//3
a = a + 1;
console.log(a);//4











Value of “a” is increasing outside the function also with a = a + 1 in the second last statement.


If you want to increase the value of “a” only inside the function, you can use a closure like below:

//let a = 0;
function sum() {
let a = 0;
function incSum() {
return a = a + 1;
}
return incSum;
}
let x = sum();
let a = 5;
console.log(x()); //1
console.log(x()); //2
console.log(x()); //3
console.log(a);//5














4) Closures in For Loop:


Problem:


for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}













Solution:


//Closures in For Loop
for (var i = 0; i < 5; i++) {
function callDelay(i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
callDelay(i);
}










5) Scope Chain:


1) JavaScript engine uses scopes to find out the exact location or accessibility of variables and that particular process is known as Scope Chain.

2) Scope Chain means that one variable has a scope (it may be global or local/function or block scope) is used by another variable or function having another scope (may be global or local/function or block scope).

3) This complete chain formation goes on and stops when the user wishes to stop it according to the requirement.


let value = 12;
function func() {
//let value = 13;
let nested_func1 = function () {
//let value = 14;
return function () {
//let value = 15;
console.log(value);
}
}
return nested_func1();
}
let fn = func();
fn();







let value = 12;
function func() {
let value = 13;
let nested_func1 = function () {
let value = 14;
return function () {
let value = 15;
console.log(value);
}
}
return nested_func1();
}
let fn = func();
fn();








6) Callback Hell:

The phenomenon which happens when we nest multiple callbacks within a function is called a callback hell. The shape of the resulting code structure resembles a pyramid and hence callback hell is also called the “pyramid of the doom”. 


It makes the code very difficult to understand and maintain. 


TaskA() --> 2 sec

TaskB() --> 1 sec


TaskA(TaskB)

{

    //TaskA work

    TaskB()

}



7) CallBack Hell or Pyramid of Doom:


function apiCall() {
setTimeout(() => {
console.log("Data from API 1");
setTimeout(() => {
console.log("Data from API 2");
setTimeout(() => {
console.log("Data from API 3");
setTimeout(() => {
console.log("Data from API 4");
}, 5000);
}, 4000);
}, 3000);
}, 2000);
}


apiCall();












Difficult error handling in nested callbacks:


function apiCall() {
setTimeout(() => {
console.log("Data from API 1");
setTimeout(() => {
let error = false;
if (error) {
}
else {
console.log("Data from API 2");
}
setTimeout(() => {
let error = false;
if (error) {
}
else {
console.log("Data from API 3");
}
setTimeout(() => {
console.log("Data from API 4");
}, 5000);
}, 4000);
}, 3000);
}, 2000);
}


apiCall();












8) Shopping Cart Exercise:


<style>
div {
margin-bottom: 10px;
}


table {
border-collapse: collapse;
text-align: center;
margin: 30px auto;
}


th,
td {
border: 1px solid black;
padding: 10px;
}


th {
min-width: 150px;
}


td[data-ns-test="grandTotal"],
#total {
font-weight: bolder;
}


#container {
margin: 50px auto;
text-align: center;
}
</style>
<div id="container">
<h2>Shopping cart</h2>
<div>
<label for="item-name-input"><i><b>Item Name:</b></i></label>
<input type="text" id="item-name-input" name="name">
</div>
<div>
<label for="item-price-input "><i><b>Item Price:</b></i></label>
<input type="number" id="item-price-input" name="price">
</div>
<div>
<button id="btn">Add</button>
</div>
<table id="table">
<thead>
<tr>
<th data-ns-test="item-name"><i><b>Item Name</b></i></th>
<th data-ns-test="item-price"><i><b>Item Price</b></i></th>
</tr>
</thead>
<tbody>
<tr>
<td data-ns-test="grandTotal">grandTotal</td>
<td id="total">0</td>
</tr>
</tbody>
</table>
</div>
<script>
let row = 1;
let button = document.getElementById("btn");
button.addEventListener("click", Add);
button.addEventListener("click", EmptyField);
function Add() {
let name = document.getElementById("item-name-input").value;
let price = document.getElementById("item-price-input").value;
price = Number(price);
let grandTotal = document.getElementById("total");
let total = grandTotal.innerText;
total = Number(total);
if (!name || !price) {
alert("Enter all required fields.");
return;
}
let table = document.getElementById("table");
let newRow = table.insertRow(row++);
let nameCell = newRow.insertCell(0);
let priceCell = newRow.insertCell(1);
nameCell.innerHTML = name;
priceCell.innerHTML = price;
grandTotal.innerHTML = total + price;
}
function EmptyField() {
document.getElementById("item-name-input").value = "";
document.getElementById("item-price-input").value = "";
}
</script>









Comments

Popular posts from this blog

Object.getOwnPropertyDescriptor & Object.defineProperty Methods.

DOM - Document Object Model (document.getElementById etc.)