Java Script
Java Script
Java Script
In the container the first component is memory component and the 2nd one is code component
Memory component has all the variables and functions in key value pairs. It is also called Variable
environment.
Code component is the place where code is executed one line at a time. It is also called the Thread of
Execution.
1 / 75
lectures.md 20/10/2023, 00:15
Let's consider the below example and its code execution steps:
var n = 2;
function square(num) {
var ans = num * num;
return ans;
}
var square2 = square(n);
var square4 = square(4);
2 / 75
The very first thing which JS does is memory creation phase, so it goes to line one of above code snippet,
lectures.md
and allocates a memory space for variable 'n' and then goes to line two, and allocates a 20/10/2023,
memory 00:15space
for function 'square'. When allocating memory for n it stores 'undefined', a special value for 'n'. For
'square', it stores the whole code of the function inside its memory space. Then, as square2 and
square4 are variables as well, it allocates memory and stores 'undefined' for them, and this is the end of first
phase i.e. memory creation phase.
Now, in 2nd phase i.e. code execution phase, it starts going through the whole code line by line. As it
encounters var n = 2, it assigns 2 to 'n'. Until now, the value of 'n' was undefined. For function, there is
nothing to execute. As these lines were already dealt with in memory creation phase.
Coming to line 6 i.e. var square2 = square(n), here functions are a bit different than any other
language. A new execution context is created altogether. Again in this new execution context, in
memory creation phase, we allocate memory to num and ans the two variables. And undefined is placed in
them. Now, in code execution phase of this execution context, first 2 is assigned to num. Then var ans =
num * num will store 4 in ans. After that, return ans returns the control of program back to where this
function was invoked from.
3 / 75
lectures.md 20/10/2023, 00:15
When return keyword is encountered, It returns the control to the called line and also the function
execution context is deleted. Same thing will be repeated for square4 and then after that is finished, the
global execution context will be destroyed. So the final diagram before deletion would look something like:
Javascript manages code execution context creation and deletion with the the help of Call Stack.
4 / 75
Call Stack is a mechanism to keep track of its place in script that calls multiple function.
Call Stack maintains the order of execution of execution contexts. It is also known as Program Stack,
lectures.md 20/10/2023, 00:15
Control Stack, Runtime stack, Machine Stack, Execution context stack.
5 / 75
It should have been an outright error in many other languages, as it is not possible to even access
lectures.md
something which is not even created (defined) yet But in JS, We know that in memory20/10/2023,
creation 00:15
phase it
assigns undefined and puts the content of function to function's memory. And in execution, it then
executes whatever is asked. Here, as execution goes line by line and not after compiling, it could only
print undefined and nothing else. This phenomenon, is not an error. However, if we remove var x = 7;
then it gives error. Uncaught ReferenceError: x is not defined
Hoisting is a concept which enables us to extract values of variables and functions even before
initialising/assigning value without getting error and this is happening due to the 1st phase (memory
creation phase) of the Execution Context.
So in previous lecture, we learnt that execution context gets created in two phase, so even before code
execution, memory is created so in case of variable, it will be initialized as undefined while in case of
function the whole function code is placed in the memory. Example:
Now let's observe a different example and try to understand the output.
6 / 75
lectures.md 20/10/2023, 00:15
var x = 1;
a();
b(); // we are calling the functions before defining them. This will work properly, as seen
in Hoisting.
console.log(x);
function a() {
var x = 10; // local scope because of separate execution context
console.log(x);
}
function b() {
var x = 100;
console.log(x);
}
Outputs:
7 / 75
10
lectures.md 20/10/2023, 00:15
100
In first phase of GEC (memory phase), variable x:undefined and a and b have their entire function code
as value initialized
In second phase of GEC (execution phase), when the function is called, a new local Execution Context is
created. After x = 1 assigned to GEC x, a() is called. So local EC for a is made inside code part of GEC.
For local EC, a totally different x variable assigned undefined(x inside a()) in phase 1 , and in phase 2 it
is assigned 10 and printed in console log. After printing, no more commands to run, so a() local EC is
removed from both GEC and from Call stack
Call Stack :[GEC, b()] -> GEC (after printing yet another totally different x value as 100 in console log)
Finally GEC is deleted and also removed from call stack. Program ends.
reference:
8 / 75
lectures.md 20/10/2023, 00:15
9 / 75
lectures.md 20/10/2023, 00:15
JS engine creates something known as 'window'. It is an object, which is created in the global space. It
contains lots of functions and variables. These functions and variables can be accessed from anywhere
in the program. JS engine also creates a this keyword, which points to the window object at the
global level. So, in summary, along with GEC, a global object (window) and a this variable are created.
In different engines, the name of global object changes. Window in browsers, but in nodeJS it is called
something else. At global level, this === window
If we create any variable in the global scope, then the variables get attached to the global object.
eg:
var x = 10;
console.log(x); // 10
console.log(this.x); // 10
console.log(window.x); // 10
10 / 75
lectures.md 20/10/2023, 00:15
undefined is when memory is allocated for the variable, but no value is assigned yet.
If an object/variable is not even declared/found in memory allocation phase, and tried to access it then
it is Not defined
When variable is declared but not assigned value, its current value is undefined. But when the variable
itself is not declared but called in code, then it is not defined.
console.log(x); // undefined
var x = 25;
console.log(x); // 25
console.log(a); // Uncaught ReferenceError: a is not defined
11 / 75
JS is a loosely typed / weakly typed language. It doesn't attach variables to any datatype. We can
lectures.md
say var a = 5, and then change the value to boolean a = true or string a = 'hello' later20/10/2023,
on. 00:15
Never assign undefined to a variable manually. Let it happen on it's own accord.
// CASE 1
function a() {
console.log(b); // 10
// Instead of printing undefined it prints 10, So somehow this a function could access
the variable b outside the function scope.
} 12 / 75
var b = 10;
lectures.md
a(); 20/10/2023, 00:15
// CASE 2
function a() {
c();
function c() {
console.log(b); // 10
}
}
var b = 10;
a();
// CASE 3
function a() {
c();
function c() {
var b = 100;
console.log(b); // 100
}
}
var b = 10;
a();
// CASE 4
function a() {
var b = 10;
c();
function c() {
console.log(b); // 10
}
}
a();
console.log(b); // Error, Not Defined
So, Lexical Environment = local memory + lexical env of its parent. Hence, Lexical Environement is
the local memory along with the lexical environment of its parent
Whenever an Execution Context is created, a Lexical environment(LE) is also created and is referenced
in the local Execution Context(in memory space).
14 / 75
The process of going one by one to parent and checking for values is called scope chain or Lexcial
lectures.md 20/10/2023, 00:15
environment chain.
function a() {
function c() {
// logic here
}
c(); // c is lexically inside a
} // a is lexically inside global execution
Lexical or Static scope refers to the accessibility of variables, functions and object based on physical
location in source code.
Global {
Outer {
Inner
}
}
// Inner is surrounded by lexical scope of Outer
TLDR; An inner function can access variables which are in outer functions even if inner function is
nested deep. In any other case, a function can't access variables not in its scope.
15 / 75
lectures.md 20/10/2023, 00:15
Both a and b are actually initialized as undefined in hoisting stage. But var b is inside the storage space
of GLOBAL, and a is in a separate memory object called script, where it can be accessed only after
assigning some value to it first ie. one can access 'a' only if it is assigned. Thus, it throws error.
16 / 75
Temporal Dead Zone : Time since when the let variable was hoisted until it is initialized some value.
lectures.md 20/10/2023, 00:15
So any line till before "let a = 10" is the TDZ for a
Since a is not accessible on global, its not accessible in window/this also. window.b or this.b ->
15; But window.a or this.a ->undefined, just like window.x->undefined (x isn't declared
anywhere)
Reference Error are thrown when variables are in temporal dead zone.
let a = 10;
let a = 100; //this code is rejected upfront as SyntaxError. (duplicate declaration)
------------------
let a = 10;
var a = 100; // this code also rejected upfront as SyntaxError. (can't use same name in
same scope)
Let is a stricter version of var. Now, const is even more stricter than let.
let a;
a = 10;
console.log(a) // 10. Note declaration and assigning of a is in different lines.
------------------
const b;
b = 10;
console.log(b); // SyntaxError: Missing initializer in const declaration. (This type of
declaration won't work with const. const b = 10 only will work)
------------------
const b = 100;
b = 1000; //this gives us TypeError: Assignment to constant variable.
This Error signifies that x has never been in the scope of the program. This literally means
that x was never defined/declared and is being tried to be accesed.
This Error signifies that 'a' cannot be accessed because it is declared as 'let' and since it is
not assigned a value, it is its Temporal Dead Zone. Thus, this error occurs.
This Error signifies that we are redeclaring a variable that is 'let' declared. No execution will
take place.
This Error signifies that we haven't initialized or assigned value to a const declaration.
Block aka compound statement is used to group JS statements together into 1 group. We group them
within {...}
{
18 / 75
var a = 10;
let b = 20;
lectures.md
const c = 30; 20/10/2023, 00:15
// Here let and const are hoisted in Block scope,
// While, var is hoisted in Global scope.
}
{
var a = 10;
let b = 20;
const c = 30;
}
console.log(a); // 10
console.log(b); // Uncaught ReferenceError: b is not defined
* Reason?
* In the BLOCK SCOPE; we get b and c inside it initialized as *undefined* as a part of
hoisting (in a seperate memory space called **block**)
* While, a is stored inside a GLOBAL scope.
* Thus we say, *let* and *const* are BLOCK SCOPED. They are stored in a separate mem
space which is reserved for this block. Also, they can't be accessed outside this block.
But var a can be accessed anywhere as it is in global scope. Thus, we can't access them
outside the Block.
What is Shadowing?
var a = 100;
{
var a = 10; // same name as global var
let b = 20;
const c = 30;
console.log(a); // 10
console.log(b); // 20
console.log(c); // 30
}
console.log(a); // 10, instead of the 100 we were expecting. So block "a" modified val of
global "a" as well. In console, only b and c are in block space. a initially is in global
space(a = 100), and when a = 10 line is run, a is not created in block space, but replaces
100 with 10 in global space itself.
So, If one has same named variable outside the block, the variable inside the block shadows the outside
variable. This happens only for var
Let's observe the behaviour in case of let and const and understand it's reason.
let b = 100;
{ 19 / 75
var a = 10;
lectures.md
let b = 20; 20/10/2023, 00:15
const c = 30;
console.log(b); // 20
}
console.log(b); // 100, Both b's are in separate spaces (one in Block(20) and one in
Script(another arbitrary mem space)(100)). Same is also true for *const* declarations.
const c = 100;
function x() {
const c = 10;
console.log(c); // 10
}
x();
console.log(c); // 100
let a = 20;
{
var a = 20;
}
// Uncaught SyntaxError: Identifier 'a' has already been declared
We cannot shadow let with var. But it is valid to shadow a let using a let. However, we can shadow var
with let.
All scope rules that work in function are same in arrow functions too.
Since var is function scoped, it is not a problem with the code below.
20 / 75
let a = 20;
lectures.md 20/10/2023, 00:15
function x() {
var a = 20;
}
Episode 10 : Closures in JS
Function bundled along with it's lexical scope is closure.
JavaScript has a lexcial scope environment. If a function needs to access a variable, it first goes to its
local memory. When it does not find it there, it goes to the memory of its lexical parent. See Below
code, Over here function y along with its lexical scope i.e. (function x) would be called a closure.
function x() {
var a = 7;
function y() { 21 / 75
console.log(a);
lectures.md
} 20/10/2023, 00:15
return y;
}
var z = x();
console.log(z); // value of z is entire code of function y.
In above code, When y is returned, not only is the function returned but the entire closure (fun y
+ its lexical scope) is returned and put inside z. So when z is used somewhere else in program, it
still remembers var a inside x()
Another Example
function z() {
var b = 900;
function x() {
var a = 7;
function y() {
console.log(a, b);
}
y();
}
x();
}
z(); // 7 900
*A closure is a function that has access to its outer function scope even after the function has
returned. Meaning, A closure can remember and access variables and arguments reference of its
outer function even after the function has returned.*
22 / 75
Advantages of Closure:
lectures.md 20/10/2023, 00:15
Module Design Pattern
Currying
Memoize
Data hiding and encapsulation
setTimeouts etc.
Disadvantages of Closure:
We expect JS to wait 3 sec, print 1 and then go down and print the string. But JS prints string
immediately, waits 3 sec and then prints 1.
The function inside setTimeout forms a closure (remembers reference to i). So wherever function goes
it carries this ref along with it.
setTimeout takes this callback function & attaches timer of 3000ms and stores it. Goes to next line
without waiting and prints string.
After 3000ms runs out, JS takes function, puts it into call stack and runs it.
function x() {
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
console.log("Namaste Javascript");
}
x();
// Output:
// Namaste Javascript
// 6
// 6
// 6
// 6
// 6
Reason?
This happens because of closures. When setTimeout stores the function somewhere and
attaches timer to it, the function remembers its reference to i, not value of i. All 5 copies of
function point to same reference of i. JS stores these 5 functions, prints string and then
comes back to the functions. By then the timer has run fully. And due to looping, the i value
became 6. And when the callback fun runs the variable i = 6. So same 6 is printed in each
log
24 / 75
To avoid this, we can use let instead of var as let has Block scope. For each iteration, the i
lectures.md 20/10/2023, 00:15
is a new variable altogether(new copy of i). Everytime setTimeout is run, the inside function
forms closure with new variable i
function x() {
for (var i = 1; i <= 5; i++) {
function close(i) {
setTimeout(function () {
console.log(i);
}, i * 1000);
// put the setT function inside new function close()
}
close(i); // everytime you call close(i) it creates new copy of i. Only this
time, it is with var itself!
}
console.log("Namaste Javascript");
}
x();
25 / 75
lectures.md 20/10/2023, 00:15
Ans: A function along with reference to its outer environment together forms a closure. Or in other words, A
Closure is a combination of a function and its lexical scope bundled together. eg:
function outer() {
var a = 10;
function inner() {
console.log(a);
} // inner forms a closure with outer
return inner;
}
outer()(); // 10 // over here first `()` will return inner function and then using secong
`()` to call inner function
function outer() {
function inner() {
console.log(a);
}
var a = 10;
return inner;
}
outer()(); // 10
Ans: Yes, because inner function forms a closure with its outer environment so sequence doesn't matter.
function outer() {
let a = 10;
function inner() {
console.log(a);
}
return inner;
}
outer()(); // 10
Q4: Will inner function have the access to outer function argument?
function outer(str) {
26 / 75
let a = 10;
function inner() {
lectures.md
console.log(a, str); 20/10/2023, 00:15
}
return inner;
}
outer("Hello There")(); // 10 "Hello There"
Ans: Inner function will now form closure and will have access to both a and str.
function outest() {
var c = 20;
function outer(str) {
let a = 10;
function inner() {
console.log(a, c, str);
}
return inner;
}
return outer;
}
outest()("Hello There")(); // 10 20 "Hello There"
Ans: Yes, inner will have access to all its outer environment.
function outest() {
var c = 20;
function outer(str) {
let a = 10;
function inner() {
console.log(a, c, str);
}
return inner;
}
return outer;
}
let a = 100;
outest()("Hello There")(); // 10 20 "Hello There"
Ans: Still the same output, the inner function will have reference to inner a, so conflicting name won't matter
here. If it wouldn't have find a inside outer function then it would have went more outer to find a and thus
have printed 100. So, it try to resolve variable in scope chain and if a wouldn't have been found it would have
given reference error.
// without closures
var count = 0;
function increment(){
count++;
}
// in the above code, anyone can access count and change it.
------------------------------------------------------------------
------------------------------------------------------------------
*************************
// Above code is not good and scalable for say, when you plan to implement decrement
counter at a later stage.
// To address this issue, we use *constructors*
Ans: Overconsumption of memory when using closure as everytime as those closed over variables are not
garbage collected till program expires. So when creating many closures, more memory is accumulated and
this can create memory leaks if not handled.
Garbage collector : Program in JS engine or browser that frees up unused memory. In highlevel languages
like C++ or JAVA, garbage collection is left to the programmer, but in JS engine its done implicitly.
function a() {
var x = 0;
return function b() {
console.log(x);
};
}
// Once a() is called, its element x should be garbage collected ideally. But fun b has
closure over var x. So mem of x cannot be freed. Like this if more closures formed, it
becomes an issue. To tacke this, JS engines like v8 and Chrome have smart garbage
collection mechanisms. Say we have var x = 0, z = 10 in above code. When console log
happens, x is printed as 0 but z is removed automatically.
29 / 75
lectures.md 20/10/2023, 00:15
function a() {
console.log("Hello");
}
a(); // Hello
30 / 75
var b = function () {
lectures.md 20/10/2023, 00:15
console.log("Hello");
};
b();
function () {
}// this is going to throw Syntax Error - Function Statement requires function name.
They don't have their own identity. So an anonymous function without code inside it results in an error.
Anonymous functions are used when functions are used as values eg. the code sample for function
expression above.
Same as Function Expression but function has a name instead of being anonymous.
Q: Parameters vs Arguments? 31 / 75
lectures.md 20/10/2023, 00:15
var b = function (param1, param2) {
// labels/identifiers are parameters
console.log("b called");
};
b(arg1, arg2); // arguments - values passed inside function call
We can pass functions inside a function as arguments and /or return a function(HOF). These ability are
altogether known as First class function. It is programming concept available in some other languages too.
32 / 75
lectures.md 20/10/2023, 00:15
Functions are first class citizens ie. take a function A and pass it to another function B. Here, A is a
callback function. So basically I am giving access to function B to call function A. This callback function
gives us the access to whole Asynchronous world in Synchronous world.
setTimeout(function () {
console.log("Timer");
}, 1000); // first argument is callback function and second is timer.
JS is a synchronous and single threaded language. But due to callbacks, we can do async things in JS.
setTimeout(function () {
console.log("timer");
33 / 75
}, 5000);
function x(y) {
lectures.md
console.log("x"); 20/10/2023, 00:15
y();
}
x(function y() {
console.log("y");
});
// x y timer
In the call stack, first x and y are present. After code execution, they go away and stack is empty. Then
after 5 seconds (from beginning) anonymous suddenly appear up in stack ie. setTimeout
All 3 functions are executed through call stack. If any operation blocks the call stack, its called blocking
the main thread.
Say if x() takes 30 sec to run, then JS has to wait for it to finish as it has only 1 call stack/1 main
thread. Never block main thread.
Always use async for functions that take time eg. setTimeout
Event Listener
// index.html
<button id="clickMe">Click Me!</button>;
// in index.js
document.getElementById("clickMe").addEventListener("click", function xyz() {
//when event click occurs, this callback function (xyz) is called into callstack
console.log("Button clicked");
});
let count = 0;
34 / 75
document
.getElementById("clickMe")
lectures.md .addEventListener("click", function xyz() { 20/10/2023, 00:15
console.log("Button clicked", ++count);
});
function attachEventList() {
//creating new function for closure
let count = 0;
document
.getElementById("clickMe")
.addEventListener("click", function xyz() {
console.log("Button clicked", ++count); //now callback function forms
closure with outer scope(count)
});
}
attachEventList();
35 / 75
Event listeners are heavy as they form closures. So even when call stack is empty, EventListener won't
lectures.md
free up memory allocated to count as it doesn't know when it may need count again. 20/10/2023,
So we remove00:15
event listeners when we don't need them (garbage collected) onClick, onHover, onScroll all in a page
can slow it down heavily.
Browser has JS Engine which has Call Stack which has Global execution context, local execution context
etc.
But browser has many other superpowers - Local storage space, Timer, place to enter URL,
Bluetooth access, Geolocation access and so on.
Now JS needs some way to connect the callstack with all these superpowers. This is done using
Web APIs. 36 / 75
lectures.md 20/10/2023, 00:15
WebAPIs
None of the below are part of Javascript! These are extra superpowers that browser has. Browser gives
access to JS callstack to use these powers.
setTimeout(), DOM APIs, fetch(), localstorage, console (yes, even console.log is not JS!!), location and
so many more.
We get all these inside call stack through global object ie. window
console.log("start");
setTimeout(function cb() {
console.log("timer");
}, 5000);
console.log("end");
// start end timer
cb() cannot simply directly go to callstack to be execeuted. It goes through the callback queue when
timer expires.
Event loop keep checking the callback queue, and see if it has any element to puts it into call stack. It is
like a gate keeper.
Once cb() is in callback queue, eventloop pushes it to callstack to run. Console API is used and log
printed 38 / 75
lectures.md 20/10/2023, 00:15
See the below Image and code and try to understand the reason:
Explaination?
console.log("Start");
document.getElementById("btn").addEventListener("click", function cb() {
// cb() registered inside webapi environment and event(click) attached to it. i.e.
REGISTERING CALLBACK AND ATTACHING EVENT TO IT.
console.log("Callback");
});
console.log("End"); // calls console api and logs in console window. After this GEC
get removed from call stack.
// In above code, even after console prints "Start" and "End" and pops GEC out, the
eventListener stays in webapi env(with hope that user may click it some day) until
39 / 75
explicitly removed, or the browser is closed.
lectures.md 20/10/2023, 00:15
Eventloop has just one job to keep checking callback queue and if found something push it to call stack
and delete from callback queue.
Ans: Suppose user clciks button x6 times. So 6 cb() are put inside callback queue. Event loop sees if call
stack is empty/has space and whether callback queue is not empty(6 elements here). Elements of callback
queue popped off, put in callstack, executed and then popped off from call stack.
console.log("Start"); // this calls the console web api (through window) which in turn
actually modifies values in console.
setTimeout(function cbT() {
console.log("CB Timeout");
}, 5000);
fetch("https://api.netflix.com").then(function cbF() {
console.log("CB Netflix");
}); // take 2 seconds to bring response
// millions lines of code
console.log("End");
Code Explaination:
* Same steps for everything before fetch() in above code.
* fetch registers cbF into webapi environment along with existing cbT.
* cbT is waiting for 5000ms to end so that it can be put inside callback queue. cbF is
waiting for data to be returned from Netflix servers gonna take 2 seconds.
* After this millions of lines of code is running, by the time millions line of code will
execute, 5 seconds has finished and now the timer has expired and response from Netflix
server is ready.
* Data back from cbF ready to be executed gets stored into something called a Microtask
Queue.
* Also after expiration of timer, cbT is ready to execute in Callback Queue.
* Microtask Queue is exactly same as Callback Queue, but it has higher priority. Functions
in Microtask Queue are executed earlier than Callback Queue.
* In console, first Start and End are printed in console. First cbF goes in callstack and
"CB Netflix" is printed. cbF popped from callstack. Next cbT is removed from callback
Queue, put in Call Stack, "CB Timeout" is printed, and cbT removed from callstack.
* See below Image for more understanding
40 / 75
lectures.md 20/10/2023, 00:15
All the callback functions that come through promises go in microtask Queue.
Mutation Observer : Keeps on checking whether there is mutation in DOM tree or not, and if there,
then it execeutes some callback function.
Callback functions that come through promises and mutation observer go inside Microtask Queue.
All the rest goes inside Callback Queue aka. Task Queue.
If the task in microtask Queue keeps creating new tasks in the queue, element in callback queue never
gets chance to be run. This is called starvation
2. Are only asynchronous web api callbacks are registered in web api environment? - YES, the
synchronous callback functions like what we pass inside map, filter and reduce aren't registered in the
Web API environment. It's just those async callback functions which go through all this.
3. Does the web API environment stores only the callback function and pushes the same callback
to queue/microtask queue? - Yes, the callback functions are stored, and a reference is scheduled in
the queues. Moreover, in the case of event listeners(for example click handlers), the original callbacks
stay in the web API environment forever, that's why it's adviced to explicitly remove the listeners when
not in use so that the garbage collector does its job.
4. How does it matter if we delay for setTimeout would be 0ms. Then callback will move to queue
without any wait ? - No, there are trust issues with setTimeout() 😅. The callback function needs to
wait until the Call Stack is empty. So the 0 ms callback might have to wait for 100ms also if the stack is
busy.
42 / 75
lectures.md 20/10/2023, 00:15
43 / 75
lectures.md 20/10/2023, 00:15
44 / 75
lectures.md 20/10/2023, 00:15
45 / 75
lectures.md 20/10/2023, 00:15
JRE is like a big container which has everything which are required to run Javascript code.
JRE consists of a JS Engine (❤️of JRE), set of APIs to connect with outside environment, event loop,
Callback queue, Microtask queue etc.
Browser can execute javascript code because it has the Javascript Runtime Environment.
ECMAScript is a governing body of JS. It has set of rules which are followed by all JS engines like
Chakra(Edge), Spidermonkey(Firefox)(first javascript engine created by JS creator himself),
v8(Chrome)
Javascript Engine is not a machine. Its software written in low level languages (eg. C++) that takes in
hi-level code in JS and spits out low level machine code.
GiF Demo
Companies use different JS engines and each try to make theirs the best.
47 / 75
v8 of Google has Interpreter called Ignition, a compiler called Turbo Fan and garbage collector
lectures.md 20/10/2023, 00:15
called Orinoco
v8 architecture:
48 / 75
lectures.md 20/10/2023, 00:15
console.log("Start");
setTimeout(function cb() {
console.log("Callback");
}, 5000);
console.log("End");
// Millions of lines of code to execute
// o/p: Over here setTimeout exactly doesn't guarantee that the callback function will
be called exactly after 5s. Maybe 6,7 or even 10! It all depends on callstack. Why?
Reason?
49 / 75
First GEC is created and pushed in callstack.
Start is printed in console
lectures.md
When setTimeout is seen, callback function is registered into webapi's env. And 20/10/2023, 00:15
timer is attached
to it and started. callback waits for its turn to be execeuted once timer expires. But JS waits for
none. Goes to next line.
End is printed in console.
After "End", we have 1 million lines of code that takes 10 sec(say) to finish execution. So GEC
won't pop out of stack. It runs all the code for 10 sec.
But in the background, the timer runs for 5s. While callstack runs the 1M line of code, this timer
has already expired and callback fun has been pushed to Callback queue and waiting to pushed to
callstack to get executed.
Event loop keeps checking if callstack is empty or not. But here GEC is still in stack so cb can't be
popped from callback Queue and pushed to CallStack. Though setTimeout is only for 5s, it
waits for 10s until callstack is empty before it can execute (When GEC popped after 10sec,
callstack() is pushed into call stack and immediately executed (Whatever is pushed to callstack is
executed instantly).
This is called as the Concurrency model of JS. This is the logic behind setTimeout's trust issues.
The First rule of JavaScript: Do not block the main thread (as JS is a single threaded(only 1 callstack)
language).
In below example, we are blocking the main thread. Observe Questiona and Output.
setTimeout guarantees that it will take at least the given timer to execute the code.
JS is a synchronous single threaded language. With just 1 thread it runs all pieces of code. It becomes
kind of an interpreter language, and runs code very fast inside browser (no need to wait for code to be
compiled) (JIT - Just in time compilation). And there are still ways to do async operations as well.
console.log("Start"); 50 / 75
setTimeout(function cb() {
console.log("Callback");
lectures.md
}, 0); 20/10/2023, 00:15
console.log("End");
// Even though timer = 0s, the cb() has to go through the queue. Registers calback in
webapi's env , moves to callback queue, and execute once callstack is empty.
// O/p - Start End Callback
// This method of putting timer = 0, can be used to defer a less imp function by a
little so the more important function(here printing "End") can take place
Ans: A Higher-order functions are regular functions that take other functions as arguments or return
functions as their results. Eg:
51 / 75
function
lectures.md x() { 20/10/2023, 00:15
console.log("Hi)";
};
function y(x) {
x();
};
y(); // Hi
// y is a higher order function
// x is a callback function
Let's try to understand how we should approach solution in interview. I have an array of radius and I have to
calculate area using these radius and store in an array.
First Approach:
The above solution works perfectly fine but what if we have now requirement to calculate array of
circumference. Code now be like
But over here we are violating some principle like DRY Principle, now lets observe the better approach.
Polyfill of map
// Over here calculate is nothing but polyfill of map function
// console.log(radiusArr.map(area)) == console.log(calculate(radiusArr, area));
***************************************************
Lets convert above calculate function as map function and try to use. So,
Array.prototype.calculate = function(operation) {
const output = [];
for (let i = 0; i < this.length; i++) {
output.push(operation(this[i]));
}
return output;
}
console.log(radiusArr.calculate(area))
53 / 75
lectures.md 20/10/2023, 00:15
Map function
It is basically used to transform a array. The map() method creates a new array with the results of calling a
function for every array element.
const output = arr.map(function) // this function tells map that what transformation I want on each element
of array
So basically map function is mapping each and every value and transforming it based on given condition.
Filter function
Filter function is basically used to filter the value inside an array. The arr.filter() method is used to create a
new array from a given array consisting of only those elements from the given array which satisfy a condition
set by the argument method.
Filter function creates an array and store only those values which evaluates to true.
Reduce function
It is a function which take all the values of array and gives a single output of it. It reduces the array to give a
single output.
55 / 75
const array = [5, 1, 3, 2, 6];
lectures.md 20/10/2023, 00:15
// Calculate sum of elements of array - Non functional programming way
function findSum(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum;
}
console.log(findSum(array)); // 17
// using reduce
const output = arr.reduce((acc, current) => {
if (current > acc ) {
acc = current;
}
return acc;
}, 0);
console.log(output); // 6
// acc is just a label which represent the accumulated value till now,
// so we can also label it as max in this case
const output = arr.reduce((max, current) => {
if (current > max) {
max= current;
}
return max;
}, 0);
console.log(output); // 6 56 / 75
Tricky MAP
lectures.md 20/10/2023, 00:15
const users = [
{ firstName: "Alok", lastName: "Raj", age: 23 },
{ firstName: "Ashish", lastName: "Kumar", age: 29 },
{ firstName: "Ankit", lastName: "Roy", age: 29 },
{ firstName: "Pranav", lastName: "Mukherjee", age: 50 },
];
// Get array of full name : ["Alok Raj", "Ashish Kumar", ...]
const fullNameArr = users.map((user) => user.firstName + " " + user.lastName);
console.log(fullNameArr); // ["Alok Raj", "Ashish Kumar", ...]
----------------------------------------------------------
// Get the count/report of how many unique people with unique age are there
// like: {29 : 2, 75 : 1, 50 : 1}
// We should use reduce, why? we want to deduce some information from the array. Basically
we want to get a single object as output
const report = users.reduce((acc, curr) => {
if(acc[curr.age]) {
acc[curr.age] = ++ acc[curr.age] ;
} else {
acc[curr.age] = 1;
}
return acc; //to every time return update object
}, {})
console.log(report) // {29 : 2, 75 : 1, 50 : 1}
Function Chaining
// function chaining
const output = users
.filter((user) => user.age < 30)
.map((user) => user.firstName);
console.log(output); // ["Alok", "Ashish", "Ankit"]
1. Good Part of callback - Callback are super important while writing asynchronous code in JS
2. Bad Part of Callback - Using callback we can face issue:
Callback Hell
Inversion of control
Understanding of Bad part of callback is super important to learn Promise in next lecture.
💡 JavaScript is synchronous, single threaded language. It can Just do one thing at a time, it has just
one call-stack and it can execute one thing at a time. Whatever code we give to Javascript will be
quickly executed by Javascript engine, it does not wait.
console.log("Namaste");
console.log("JavaScript");
58 / 75
console.log("Season 2");
// Namaste
lectures.md
// JavaScript 20/10/2023, 00:15
// Season 2
// 💡 It is quickly printing because `Time, tide & Javascript waits for none.`
But what if we have to delay execution of any line, we could utilize callback, How?
console.log("Namaste");
setTimeout(function () {
console.log("JavaScript");
}, 5000);
console.log("Season 2");
// Namaste
// Season 2
// JavaScript
Assumption, once order is created then only we can proceed to payment, so there is a dependency. So How
to manage this dependency. Callback can come as rescue, How?
api.createOrder(cart, function () {
api.proceedToPayment();
});
// 💡 Over here `createOrder` api is first creating a order then it is responsible to call
`api.proceedToPayment()` as part of callback approach.
To make it a bit complicated, what if after payment is done, you have to show Order summary by calling
api.showOrderSummary() and now it has dependency on api.proceedToPayment() Now my code should look
something like this:
api.createOrder(cart, function () {
59 / 75
api.proceedToPayment(function () {
api.showOrderSummary();
lectures.md
}); 20/10/2023, 00:15
});
Now what if we have to update the wallet, now this will have a dependency over showOrderSummary
api.createOrder(cart, function () {
api.proceedToPayment(function () {
api.showOrderSummary(function () {
api.updateWallet();
});
});
});
// 💡 Callback Hell
When we have a large codebase and multiple apis and have dependency on each other, then we fall into
callback hell. These codes are tough to maintain. These callback hell structure is also known as Pyramid of
Doom.
Till this point we are comfortable with concept of callback hell but now lets discuss about Inversion of
Control. It is very important to understand in order to get comfortable around the concept of promise.
💡 Inversion of control is like that you lose the control of code when we are using callback.
Let's understand with the help of example code and comments:
api.createOrder(cart, function () {
api.proceedToPayment();
});
// 💡 So over here, we are creating a order and then we are blindly trusting `createOrder`
to call `proceedToPayment`.
60 / 75
lectures.md 20/10/2023, 00:15
We will discuss with code example that how things used to work before Promises and then how it works after
Promises
Now, we will make createOrder function return a promise and we will capture that promise into a variable
Promise is nothing but we can assume it to be empty object with some data value in it, and this data value
will hold whatever this createOrder function will return.
Since createOrder function is an async function and we don't know how much time will it take to finish
execution.
So the moment createOrder will get executed, it will return you a undefined value. Let's say after 5 secs
execution finished so now orderId is ready so, it will fill the undefined value with the orderId.
In short, When createOrder get executed, it immediately returns a promise object with undefined value.
then javascript will continue to execute with other lines of code. After sometime when createOrder has
finished execution and orderId is ready then that will automatically be assigned to our returned promise
which was earlier undefined.
// {data: undefined}
// Initially it will be undefined so below code won't trigger
// After some time, when execution has finished and promiseRef has the data then
automatically the below line will get triggered.
promiseRef.then(function () {
proceedToPayment(orderId);
});
In Earlier solution we used to pass the function and then used to trust the function to execute the callback.
There is difference between these words, passing a function and attaching a function.
Promise guarantee, it will callback the attached function once it has the fulfilled data. And it will call it only
once. Just once.
Earlier we talked about promise are object with empty data but that's not entirely true, Promise are much
more than that.
fetch is a web-api which is utilized to make api call and it returns a promise.
62 / 75
We will be calling public github api to fetch data https://api.github.com/users/alok722
lectures.md 20/10/2023, 00:15
// We will be calling public github api to fetch data
const URL = "https://api.github.com/users/alok722";
const user = fetch(URL);
// User above will be a promise.
console.log(user); // Promise {<Pending>}
/** OBSERVATIONS:
* If we will deep dive and see, this `promise` object has 3 things
* `prototype`, `promiseState` & `promiseResult`
* & this `promiseResult` is the same data which we talked earlier as data
* & initially `promiseResult` is `undefined`
*
* `promiseResult` will store data returned from API call
* `promiseState` will tell in which state the promise is currently, initially it will be
in `pending` state and later it will become `fulfilled`
*/
/**
* When above line is executed, `fetch` makes API call and return a `promise` instantly
which is in `Pending` state and Javascript doesn't wait to get it `fulfilled`
* And in next line it console out the `pending promise`.
* NOTE: chrome browser has some in-consistency, the moment console happens it shows in
pending state but if you will expand that it will show fulfilled because chrome updated the
log when promise get fulfilled.
* Once fulfilled data is there in promiseResult and it is inside body in ReadableStream
format and there is a way to extract data.
*/
Using .then
user.then(function (data) {
console.log(data);
});
// And this is how Promise is used.
// It guarantees that it could be resolved only once, either it could be `success` or
`failure`
/**
A Promise is in one of these states:
We are now done solving one issue of callback i.e. Inversion of Control
// ⚠️ Common PitFall
// We forget to return promise in Promise Chaining
// The idea is promise/data returned from one .then become data for next .then
// So,
createOrder(cart)
.then(function (orderId) {
return proceedToPayment(orderId);
})
.then(function (paymentInf) {
return showOrderSummary(paymentInf);
})
.then(function (balance) {
return updateWalletBalance(balance);
});
// To improve readability you can use arrow function instead of regular function
64 / 75
Watch Live On Youtube below:
lectures.md 20/10/2023, 00:15
promise.then(function (orderId) {
proceedToPayment(orderId);
});
Over above, if your validateCart is returning true, so the above promise will be resolved (success),
promise.then(function (orderId) {
proceedToPayment(orderId);
});
function createOrder(cart) {
const promise = new Promise(function (resolve, reject) {
if (!validateCart(cart)) {
const err = new Error("Cart is not Valid");
reject(err);
}
const orderId = "12345";
if (orderId) {
resolve(orderId);
}
});
return promise;
}
66 / 75
Now let's see if there was some error and we are rejecting the promise, how we could catch that?
lectures.md 20/10/2023, 00:15
-> Using .catch
// Here we are consuming Promise and will try to catch promise error
promise
.then(function (orderId) {
// ✅ success aka resolved promise handling
proceedToPayment(orderId);
})
.catch(function (err) {
// ⚠️ failure aka reject handling
console.log(err);
});
createOrder(cart)
.then(function (orderId) {
// ✅ success aka resolved promise handling
// 💡 we have return data or promise so that we can keep chaining the promises, here we
are returning data
console.log(orderId);
return orderId;
})
.then(function (orderId) {
// Promise chaining
// 💡 we will make sure that `proceedToPayment`
67 / 75 returns a promise too
return proceedToPayment(orderId);
lectures.md
}) 20/10/2023, 00:15
.then(function (paymentInfo) {
// from above, `proceedToPayment` is returning a promise so we can consume using
`.then`
console.log(paymentInfo);
})
.catch(function (err) {
// ⚠️ failure aka reject handling
console.log(err);
});
function proceedToPayment(cart) {
return new Promise(function (resolve, reject) {
// For time being, we are simply `resolving` promise
resolve("Payment Successful");
});
}
Q: What if we want to continue execution even if any of my promise is failing, how to achieve this?
-> By placing the .catch block at some level after which we are not concerned with failure.
-> There could be multiple .catch too. Eg:
createOrder(cart)
.then(function (orderId) {
// ✅ success aka resolved promise handling
// 💡 we have return data or promise so that we can keep chaining the promises, here we
are returning data
console.log(orderId);
return orderId;
})
.catch(function (err) {
// ⚠️ Whatever fails below it, catch wont care
// this block is responsible for code block above it.
console.log(err);
});
.then(function (orderId) {
// Promise chaining
68 / 75
// 💡 we will make sure that `proceedToPayment` returns a promise too
return proceedToPayment(orderId);
lectures.md
}) 20/10/2023, 00:15
.then(function (paymentInfo) {
// from above, `proceedToPayment` is returning a promise so we can consume using
`.then`
console.log(paymentInfo);
})
What is async?
What is await?
How async await works behind the scenes?
Example of using async/await
Error Handling
Interviews
Async await vs Promise.then/.catch 69 / 75
Q: What is async?
lectures.md 20/10/2023, 00:15
A: Async is a keyword that is used before a function to create a async function.
// 💡 async function always returns a promise, even if I return a simple string from below
function, async keyword will wrap it under Promise and then return.
async function getData() {
return "Namaste JavaScript";
}
const dataPromise = getData();
console.log(dataPromise); // Promise {<fulfilled>: 'Namaste JavaScript'}
//❓How to extract data from above promise? One way is using promise .then
dataPromise.then(res => console.log(res)); // Namaste JavaScript
But Question is how we used to handle promises earlier and why we even need async/await?
function getData() {
p.then(res => console.log(res));
}
await function() {} // Syntax error: await is only valid under async function.
// 📌 Promise.then/.catch way
function getData() {
// JS engine will not wait for promise to be resolved
p.then(res => console.log(res));
console.log('Hello There!');
}
getData(); // First `Hello There!` would be printed and then after 3 secs 'Promise resolved
value!!' will be printed.
// Above happened as Javascript wait for none, so it will register this promise and take
this callback function and register separately then js will move on and execute the
following console and later once promise is resolved, following console will be printed.
//❓ Problem: Normally one used to get confused that JS will wait for promise to be
resolved before executing following lines.
// 📌 async-wait way:
async function handlePromise() {
// JS Engine will waiting for promise to resolve.
const val = await p;
console.log('Hello There!');
console.log(val);
}
handlePromise(); // This time `Hello There!` won't be printed immediately instead after 3
secs `Hello There!` will be printed followed by 'Promise resolved value!!'
// 💡 So basically code was waiting at `await` line to get the promise resolve before
moving on to next line.
// Let's create one promise and then resolve two different promise.
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise resolved value by p2!!');
}, 2000);
})
// Now let's reverse the order execution of promise and observe response.
async function handlePromise() {
console.log('Hi');
const val = await p2;
console.log('Hello There!');
console.log(val);
// call stack flow -> handlePromise() is pushed -> It will log `Hi` to console -> Next it
sees we have await where promise is suppose to be resolved -> So will it wait for promise
to resolve and block call stack? No -> thus handlePromise() execution get suspended and
moved out of call stack -> So when JS sees await keyword it suspend the execution of
function till promise is resolved -> So `p` will get resolved after 5 secs so
handlePromise() will be pushed to call-stack again after 5 secs. -> But this time it will
start executing from where it had left. -> Now it will log 'Hello There!' and 'Promise
resolved value!!' -> then it will check whether `p2` is resolved or not -> It will find
since `p2` will take 10 secs to resolve so the same above process will repeat -> execution
will be suspended until promise is resolved.
// Moreover in above scenario what if p1 would be taking 10 secs and p2 5 secs -> even
though p2 got resolved earlier but JS is synchronous single threaded language so it will
first wait for p1 to be resolved and then will immediately execute all.
Error Handling
While we were using normal Promise we were using .catch to handle error, now in async-await we would be
using try-catch block to handle error.
// In above whenever any error will occur the execution will move to catch block. One could
try above with bad url which will result in error.
What one should use? async-await is just a syntactic sugar around promise. Behind the scene async-await is
just promise. So both are same, it's just async-await is new way of writing code. async-await solves few of
the short-coming of Promise like Promise Chaining. async-await also increases the readability. So sort of it is
always advisable to use async-await.
74 / 75
lectures.md 20/10/2023, 00:15
To Be Continued...
75 / 75