JavaScript, often abbreviated JS, is a programming language that is one of the core technologies of the World Wide Web, alongside HTML and CSS. Many websites use JavaScript on the client side for web page behavior, often incorporating third-party libraries.
"Javascript is a synchronous single-threaded language and everything in it happens inside an execution context"
- This means that it can only move on the next line once the single line operation is complete
- The execution context consists of Memory(Variable Enviornment) and Code(Thread of Execution) as two components, the memory component consists of variable and functions as key-value pairs and the code component consists of the instructions that are executed.
// Code Sample
var n = 2;
function square(num) {
var ans = num * num;
return ans;
}
var square2 = square(n);
console.log(square2);
Output:
4
-
In the first step, javascript will skim through the code and will allocate memory to all the variables and functions.
-
It stores a special value of
undefined
for all the variables and incase of functions it will store the whole function code.
-
In the second step, javascript will again rundown the code line by line
-
It will set
n=2
and since there is nothing to exectue from line 2 to 5 it will move on to varsqaure2
where it will encounter a function invocation. -
When it encouters a function invocation, a brand new execution context is created inside the code section
-
So again the memory will be allocated to the varibales
num
andans
and they will store the valueundefined
-
Now comes the part of code execution, in which we will be executing each line inside the function
-
In
square(n)
the value of n is 2 (argument) is passed tonum
as a parameter -
Now in the code section, the operation num * num is performed and the result is stored in
ans
-
In the end, we have a
return
keyword which means that we are done with the execution of the function and have to return the control back to the execution context where the function was invoked. -
The
return ans
which is present in the code section finds the value of 4 inside the memory section and returns it to the execution context where the function was invoked and hence the value ofsquare2
changes fromundefined
to4
. -
Once the whole code is completed, then all the execution contexts are destroyed and the memory is freed up
JavaScript uses the call stack to keep track of several function calls. It works in data structures like a true stack, allowing data to be pushed and popped and adhering to the Last In First Out (LIFO) principle. We use the call stack to remember which function is currently active.
- Execution Stack:
- Program Stack
- Control Stack
- Runtime Stack
- Machine Stack
Hoisting as a core concept relies on the way how Execution Context is created. In the first phase i.e. the Memory Allocation Phase all the variables and functions are allocated memory, even before any code is executed. All the variables are assigned undefined at this point in time in the local memory.
getName();
console.log(x);
var x = 7;
function getName()
{
console.log("JavaScript");
}
The Output would be:
JavaScript
undefined
This is because javascript first skims through the code and looks for the variables and functions and then assigns the value to the variables which is undefined.
If we remove var x = 7
from the code, then the console.log(x) will give Uncaught error: x is not defined
.
var x = 7;
console.log(getName);
function getName()
{
console.log("JavaScript");
}
The Output would be:
function getName()
{
console.log("JavaScript");
}
console.log(getName);
var x = 7;
var getName = () =>
{
console.log("JavaScript");
}
// function expresssion
var b = function () {
console.log("This declaration makes function not hoisted!");
}
Output would be:
undefined
Not Hoisted
Here, the output will be undefined because javascript will take this var
as a typical variable and not count it as a function.
var x = 1;
a();
b();
console.log(x);
function a(){
var x = 10;
console.log(x);
}
function b(){
var x = 1000;
console.log(x);
}
Output would be:
10
1000
1
- Global Execution context is created and memory is allocated to
var x
,function a and b
. Var x
will store the valueundefined
andfunction a and b
will point to thefunction body
.- When the code will start running, first thing that will take place is that x's undefined will be replaced with the value 1
- After this the
function a
will be invoked and its own execution context will be created in the call stack ,which will store the value of seperatex=10
- Similar thing will happen with
function b
- As soon as these functions are executed, their memory and space is deleted from the call stack.
- In the end the control goes to global execution context which console logs the value of
x = 1
in the end.
- Whenever you create an execution context,
this
is created along with it even for the functional execution of the context and even for this global execution context. - At Global level the
this
points to global object that is the window incase of the browser - Anything that is not inside any function is called as global object.
var x = 1; ---> Global Varibale
function a () = {
var b = 3;
}
console.log(window.a);
console.log(this.a);
console.log(b);
- When we will go to console in browser and type
window
we will getx: 1
anda(): f a()
as a part of thewindow
b
wont be there as a part of window, Thus the output will beundefined
Undefined
in javascript is someundefined
value given to a variable or function before the execution of the program. It is not considered empty as it consumes memory space as wellNot-Defiend
in javascript is somehing which is not allocated space and not declared in the program- JavaScript is
losely typed language
, which means that anything can be allocated to anything i.e.var
can be allocated asstring
,int
,boolean
.
var a;
console.log(a);
var a = 10;
console.log(a);
a = "Hello World"
console.log(a);
Output:
undefined
10
Hello World
foo = 1;
console.log(foo);
var foo;
var x;
console.log(x);
x = 1;
Output:
1
undefined
function a(){
var b = 10;
c();
function c(){
console.log(b);
}
}
a();
Output:
10
- In Javascript the
lexical enviornment
is created when a local memory of every execution context of javascript is nested in one another and accesed by the lowest lexicographic enviornment Scope Chaining
is the method by which the lexical enviornment is created and accessed by the execution context.- In the above example: The javascript first creates the
lexical enviornment
fora
and then forc
and then forb
- Then the
lexical enviornment
is accessed by thec
function and the value ofb
is printed. - For reference do watch the video below. ( Important )
console.log(x);
console.log(a);
console.log(b);
let a = 10;
var b = 100;
Output:
Uncaught ReferecneError: x is not defined
Uncaught ReferenceError: Cannot access 'a' before initialization
undefined
- Hoisting is done for
let
andconst
variables as well, but they are stored in a different meomry space which the users cannot access. (Script) - Any line written above
let
variable for accessing it is defiend asTemporal Dead Zone
for that variable. let
andconst
variables give an output ofundefined
on accessing them through the keywordsthis
andwindow
in global executionLet and Const are block Scoped and Var is function scoped
let a = 10;
let a = 100;
var a = 212;
console.log("abcd");
Output:
Uncaught SyntaxError: Identifier 'a' has already been declared
- This means that you cannot redeclare the same variable in the same scope for
let
andconst
- Incase of
var
it is possible
// Correct Way
const b = 10;
// Incorrect Way - 1
const b;
b = 100;
// Incorrect Way - 2
const b = 8;
b = 88;
Output:
Uncaught SyntaxError: Missing initializer in const declaration
Uncaught TypeError: Assignment to constant variable.
-
This is because
const
is a constant variable and it is not possible to change the value of it. -
That's the reason why all the initializations are kept on the top to minimize the temporal dead zone.
-
Other Examples
//Works fine
{
var y = 10;
}
function inner() {
console.log(y);
}
inner();
// Works fine
let x = 10;
function inner() {
console.log(x);
}
inner();
// Gives Error as let is block scoped
{
let z = 10;
}
function inner() {
console.log(z);
}
inner();
- We group multiple statements in a block so that we can use it where javascript expects one statement
{
//Compound Statement
var a = 10;
let b = 20;
const c = 30;
}
console.log(a);
console.log(b);
console.log(c);
Output:
ReferenceError: c is not defined
- This is because the
var
is hoisted in the global block level,let
andconst
variables are not hoisted to the top of the block instead they are hoisted in Block level.
var a = 100;
console.log(a);
{
//Compound Statement
var a = 10;
let b = 20;
const c = 30;
console.log(c);
}
console.log(a);
Output:
100
30
10
- Here comes the concept of shadowing, as in the above example we can see that when
var a
is declared on the first line and it gives100
as the output for the first time, but later when we declare it in the block level scope and then try to log the value ofa
it gives10
as the output. This happens because of the concept of shadowing. - Shadowing does not take place in the case of
let
andconst
variables.
const c = 100;
function x () {
const c = 10;
console.log(c);
}
x();
console.log(c);
Output:
10
100
- Shadowing works the same in case of functions as well
let a = 10;
// Illegal Shadowing
{
var a = 20;
}
// Legal Shadowing
{
let a = 20;
}
Illegal shadowing
is when you try to declare a variable with the same name as an existing variable in the same scope.
const a = 20;
{
const a = 19;
{
const a = 12;
}
console.log(a);
}
Output:
19
Lexical Scope
is also followed incase ofBlock level functions
i.e each and every block has its own lexical enviornment. It followslexical scope chain
pattern as well.
- Closure is a function bundled along with its lexical scope
function x() {
var a = 7;
function y() {
console.log(a);
}
return y;
}
var z = x();
console.log(z);
z();
Output:
f y(){
console.log(a);
}
7
- In the first case when
console.log(z)
is executed, it will return thefunction y()
itself as it is the return value of thex()
function. - In the second case, 7 is prinited as the function remembers it's lexical scope and remembers the value of
a
which is 7. return y
statement means that a closure is rerturned and it is put inside z.
function x() {
var a = 7;
function y() {
console.log(a);
}
a = 100;
return y;
}
var z = x();
z();
Output:
100
- Here the value of
a
is changed to 100 and the function remembers it's lexical scope and remembers the value ofa
which is 100.
function z() {
let b = 912;
function x() {
var a = 7;
function y() {
console.log(a,b);
}
y();
}
x();
}
z();
Output:
7 912
- Another example how closures helps us to avoid the use of global variable by remembering the value of the variable in the lexical scope.
- Difference between
Function Statement and Function Expression
- The difference lies in the
hoisting
of the function - The
function a
will be hoisted and will giveundefined
as the output - But
function b
will give Error as it it will be treated like any other varibale and will be givenundefined
intially but on final execution it will see that b is a varibale and not a function, thus it will give Error
//Function Statement
a();
function a(){
console.log("a is called");
}
//Function Expression
b();
var b = function (){
console.log("b is called");
}
Function Statement and Function Declearation
are the same in javascript.
//Anonymus Function
function () {
}
Output:
Uncaught SyntaxError: Function statements requires a function name
- The Use of Anonymus function is used in places like function expression where there is no name given to the function
//Named Function Expression
var b = function xy(){
console.log("Hello World");
}
b();
xy();
//Named Function Expression - 2
var b = function xy(){
console.log(xy);
}
b();
Output:
Hello World
Reference error: xy is not defined
function xy(){
console.log(xy);
}
- The use of named function expression is used in places where we want to call the function later on.
- This gives error because xy is not present in the outer scope but it is created a s local variable
- We can access this function in its own scope by using the name of the function.
//Difference Between Parameters and Arguments
function a(x,y){ // -- Paramenters
console.log(x,y);
}
a(1,3); //-- Arguments
Ouptut
1 3
//First Class Function - 1
var b = function(param1){
console.log(param1);
}
b(function xyz(){
});
//First Class Function - 2
var b = function(param1){
console.log(param1);
}
function xyz(){
}
b(xyz);
//First Class Function - 3
var b = function(param1){
return function(){
};
};
console.log(b());
Output:
[Function: xyz]
[Function: xyz]
[Function: anonymous]
- First Class Functions are the ability of the functions to be used as values and passed into as arguments in another function and can be returned as a function
- It is also called
First Class Citizens
function x(){
var i = 1;
setTimeout(function(){
console.log(i);
},3000);
console.log("Hello World");
}
x();
Output:
Hello World
//Waits for 3 seconds and then prints 1
1
- Here the
setTimeout
function is called with a function as an argument. - The function is called after 3 seconds and prints the value of
i
which is 1. setTimeout
takes this call back function and attaches a timer, when the timer expires it calls the function
//Code Example -1
function x() {
for (var i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
console.log("Hello World");
}
x();
//Code Example - 2
function x() {
for (let i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
console.log("Hello World");
}
x();
//Code Example - 3
function x() {
for (var i = 1; i < 5; i++) {
function close(x){
setTimeout(function () {
console.log(x);
}, x * 1000);
}
close(i);
}
}
x();
Output:
//Ouptut of Code Example - 1
Hello World
5
5
5
5
//Ouptut of Code Example - 2
Hello World
1
2
3
4
//Ouptut of Code Example - 3
Hello World
1
2
3
4
In Code Example -1
the value of 'i' is remembered as reference and not as the value- When the loop runs the first time, makes a copy of this function, attaches a timer and also remebers the reference of 'i'
- Javascript does not wait for anything, it will run the loop again and again, it will not wait for the timer to expire.
- And when the timer expires it is too late, because the loop was constantly running, the value of
i
became 5 and when the call back function runs, thevar i
becomes 5 In Code Example -2
, we have usedlet
keyword instead ofvar
keyword, which has a block level scope and each and every time thissetTimeout
is called, the function forms a closure with a new variable itself, this means the value ofi
is new in every iteration.In Code Example -3
we have made a closure namedclose()
, because here everytime we createx
in the above part everytime the loop is executed
- Callback Functions are functions that are passed as arguments to other functions.
setTimeout(function () {
console.log("timer");
}, 2000);
function x(y) {
console.log("Hello");
y();
}
x(function y() {
console.log("World");
});
Output:
Hello
World
timer
- Any function that block the Call Stack is termed as
blocking the main thread
- To sum up, if javascript did not have this first class citizen concept, it would not be able to use callback functions. (like
setTimeout
function is used) or else everything would be kept on getting blocked if any operation took a longer time to complete. - If it wasnt for the first class citizen concept, we would be unable to complete this asyncronus operations
- Event Listeners are functions that are attached to an event.
- In the below example we have a button on the page and when the button is clicked an event listener is already attached to the button, which calls the
function xyz()
(Callback function)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<h1>Event Listeners in JavaScript</h1>
<button id="clickMe">Click Me</button>
<script src="index.js"></script>
</body>
</html>
//Example - 1
document.getElementById("clickMe").addEventListener("click", function xyz() {
alert("Button clicked");
});
- In simple terms, this snippet of code means that it will pick up the "clickMe" element and it will add an event listener (which is "click"), and when the event is triggered it will call the
function xyz()
(Callback function) thus pulling it into the call stack.
//Example - 2: Couting Number of Clicks -- Wrong Approach
let count = 0;
document.getElementById("clickMe").addEventListener("click", function xyz() {
console.log(count++);
});
//Example - 3: Couting Number of Clicks -- Right Approach
function x() {
let count = 0;
document.getElementById("clickMe").addEventListener("click", function xyz() {
console.log(count++);
});
}
x();
-
In the above code snippet we have used the conept of closures, so that the count variable cannot be accessed outside the function.
-
let
is used instead ofvar
so that it block scoped i.e value cannot be modified beyond the function -
Event Listeners are heavy, it takes memory, so whenever you attach an event listener it forms a closure and even when the call stack is empty, still the program will not free up that memory because you never know when someone can click on that button and might need this closure to function
-
So once we remove this event listener, so all the varibales that were held by this closure are garbage collected
FAQs
- When does the event loop actually start? - Event loop, as the name suggests, is a single-thread, loop that is
almost infinite
. It's always running and doing its job. - Are only asynchronous web API callbacks are registered in the 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 that go through all this.
- 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 advised to explicitly remove the listeners when not in use so that the garbage collector does its job.
- 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. It's a very beautiful concept, and I've covered this in detail in the next episode of Namaste JavaScript.
- A
setTimeout()
with a delay of 5000ms does not always exactly wait for 5000ms. - The code snippet given below demonstrates the problem
// Example - 1
console.log("Start");
//The callback function is delayed for 5000ms
setTimeout(function cb(){
console.log("Callback Cb");
}, 5000);
console.log("End");
// This code snippet is for simulating ~1 million lines of code which can take a time of around 10ms to execute. (Blocking the main thread).
// We have a startDate which gets the currentTime in milliseconds and then in the while loop we are adding 10,000ms to it and making endDate equal to the currentTime in milliseconds.
let startDate = new Date().getTime();
let endDate = startDate;
while(endDate < startDate + 10000) {
endDate = new Date().getTime();
}
console.log("While Expires");
Output:
Start
End
While Expires
Callback Cb
- First of all
Start
will be printed on the console. - Next we have a setTimeout(), the function inside it will be registered in the web API environment and the timer will start from 5000ms.
- Now
End
will be printed in the console - Next, the while loop which is a part of the global execution context will start for next 10,000ms, it will block the main thread
- But after 5000ms the callback function will be finished executing but it will stay inside the Callback Queue and will only execute after the Event Loop finds that after 10,000 ms the Call Stack is empty (Since GEC is still present because of Time code) and then only it will shift the callback function to the main call stack.
- Thus,
While Expires
will be printed in the console first and thenCallback Cb
will be printed - This is also known as the Concurrency Model in JavaScript
// Example-2: setTiemout() with a delay of 0ms
console.log("Start");
setTimeout(function cb(){
console.log("Callback Cb");
}, 0);
console.log("End");
Output:
Start
End
Callback Cb
- A misconception about this code is that output would be
Start
,Callback Cb
,End
because the callback function is executed for 0ms. - But that is certainly not the case, even when the timer is for 0ms, this callback function has to go through a queue and wait for the Call Stack to be empty i.e when GEC is also not present.
- Thus, the correct output would be
Start
,End
,Callback Cb
- The usecase of setting setTimeout() to 0ms will be if you want to defer a piece of code i.e execute that piece of code when the whole program is executed. Example: Implementing infinite scrolling in a web application.
Map function
is being used here to convert the array into a new array with modified values.
//Example of Map
const arr = [4,5,6,23,4];
const newArr = arr.map(function(item){
return item*2;
});
console.log(newArr);
//--- Just Another Way to write it
// function double(item)
// {
// return item*2;
// }
// const output = arr.map(double);
// console.log(output);
//---- Short Hand-Fat Arrow Syntax
// const opt = arr.map((x) => x*2);
Output:
[ 8, 10, 12, 46, 8 ]
Filter function
is used to filter the array based on the condition.
const arr = [4,5,6,23,4];
//Filter out Values odd
const output = arr.filter((item) => item % 2 !== 0);
console.log(output);
Output:
[ 5, 23 ]
Reduce Function
- It is used in places where we have to find a particular value, Used for Sum or Max
const arr = [4,5,6,23,4];
//Traditional Way
function findSum(arr){
let sum =0;
for(let i=0;i<arr.length;i++){
sum = sum + arr[i];
}
return sum;
}
console.log(findSum(arr));
//acc- Accumilator and curr- Current
//0 is the is the initial value of accumilator
const output = arr.reduce(function(acc,curr){
return acc + curr;
},0);
console.log(output);
//Find Maximum Number in the Array
const max = arr.reduce(function(acc,curr){
if(curr>acc){
acc = curr;
}
return acc;
},-Infinity);
console.log(max);
Output:
42
42
23
Task
: To get the First And Last Name of the User
const users = [
{firstName: 'John', lastName: 'Doe', age: 20},
{firstName: 'Mark', lastName: 'Holland', age: 24},
{firstName: 'Jeff', lastName: 'Musk', age: 21},
{firstName: 'Tobby', lastName: 'Wayne', age: 20},
]
const getUsers = users.map(function(x){
console.log(x.firstName+" "+x.lastName);
});
//---- Short-Hand Fat Arrow Syntax
// const getUsers = users.map(x => console.log(x.firstName+" "+x.lastName));
Output:
John Doe
Mark Holland
Jeff Musk
Tobby Wayne
Task-2
: Get the output as{ '20': 2, '21': 1, '24': 1 }
const users = [
{firstName: 'John', lastName: 'Doe', age: 20},
{firstName: 'Mark', lastName: 'Holland', age: 24},
{firstName: 'Jeff', lastName: 'Musk', age: 21},
{firstName: 'Tobby', lastName: 'Wayne', age: 20},
]
const output = users.reduce(function(acc,curr){
if(acc[curr.age]){
acc[curr.age]++;
}
else{
acc[curr.age]=1;
}
return acc;
},{});
console.log(output);
Output:
{ '20': 2, '21': 1, '24': 1 }
Task-3
- Find the First and Last name of people with age greater than 21
//Using Chain of Map and Filter
const users = [
{firstName: 'John', lastName: 'Doe', age: 20},
{firstName: 'Mark', lastName: 'Holland', age: 24},
{firstName: 'Jeff', lastName: 'Musk', age: 21},
{firstName: 'Tobby', lastName: 'Wayne', age: 20},
]
const output = users.filter((x)=> x.age > 20).map(function(y){
return y.firstName + ' ' + y.lastName;
});
console.log(output);
Output:
[ 'Mark Holland', 'Jeff Musk' ]
Task 4
: Achieve the above task using Reduce
const users = [
{firstName: 'John', lastName: 'Doe', age: 20},
{firstName: 'Mark', lastName: 'Holland', age: 24},
{firstName: 'Jeff', lastName: 'Musk', age: 21},
{firstName: 'Tobby', lastName: 'Wayne', age: 20},
]
const output = users.reduce((acc, curr)=>{
if(curr.age>21) {
acc.push(curr.firstName + ' ' + curr.lastName);
}
return acc;
}, []);
console.log(output);
Output:
[ 'Mark Holland', 'Jeff Musk' ]
- A function which takes another function as an argument or returns a function as a result is called
Higher Order Function
.
function x(){
console.log("Hello World");
}
function y(x){
y();
}
- DRY principles are followed
- Each function is independent with its own task
- Functions are reusable
//--- Code written without following DRY (Don't Repeat Yourself) Principles
// Radius of circles
const radius = [1,2,3,4];
// Area Function
const calculateArea = function(radius){
const output = [];
for(let i=0;i<radius.length;i++)
{
output.push(Math.PI*radius[i]*radius[i]);
}
return output;
}
console.log(calculateArea(radius));
// Circumference Function
const calculateCircumference = function(radius){
const output = [];
for(let i=0;i<radius.length;i++)
{
output.push(2*Math.PI*radius[i]);
}
return output;
}
console.log(calculateCircumference(radius));
// Diameter Function
const calculateDiameter = function(radius){
const output = [];
for(let i=0;i<radius.length;i++)
{
output.push(2*radius[i]);
}
return output;
};
console.log(calculateDiameter(radius));
Output:
[
3.141592653589793,
12.566370614359172,
28.274333882308138,
50.26548245743669
]
[
6.283185307179586,
12.566370614359172,
18.84955592153876,
25.132741228718345
]
[ 2, 4, 6, 8 ]
//---- Modular or Reusable Code using Higher Order Functions
const radius = [1,2,3,4];
const calculate = function(radius, logic){
const output = [];
for(let i = 0; i < radius.length; i++){
output.push(logic(radius[i]));
}
return output;
};
const area = function(radius){
return Math.PI*radius*radius;
};
const diameter = function(radius){
return radius*2;
};
const circumference = function(radius){
return 2*Math.PI*radius;
};
console.log(calculate(radius,area));
console.log(calculate(radius,diameter));
console.log(calculate(radius,circumference));
Output:
[ 3.141592653589793, 12.566370614359172, 28.274333882308138, 50.26548245743669 ]
[ 2, 4, 6, 8 ]
[ 6.283185307179586, 12.566370614359172, 18.84955592153876, 25.132741228718345 ]
- Brute way is to call the function directly, but we will use call method -- function borrowing, call method is used to call the function with the context of the object
- Apply method is used to call the function with the context of the array
- Bind method is used to call the function with the context of the object and also to store the context of the object in a variable
- The bind() method creates a new function, when invoked, has the this sets to a provided value. The bind() method allows an object to borrow a method from another object without making a copy of that method
let printFullName = function(hometown , state) {
console.log(this.firstname + " " + this.lastname + " " + "is from " + hometown + " " + "and lives in " + state);
}
let name = {
firstname: "John",
lastname: "Doe",
};
let name2 = {
firstname: "Jane",
lastname: "Night",
};
// Call Method
printFullName.call(name, "Manhattan", "New York");
printFullName.call(name2, "Brooklyn", "New York");
// Apply Method
printFullName.apply(name2, ["Brooklyn", "New York"]);
// Bind Method
let printFullName2 = printFullName.bind(name, "Manhattan", "New York");
console.log(printFullName2);
printFullName2();
Output
John Doe is from Manhattan and lives in New York
Jane Night is from Brooklyn and lives in New York
Jane Night is from Brooklyn and lives in New York
[Function: bound printFullName]
John Doe is from Manhattan and lives in New York
let name = {
firstname: "John",
lastname: "Doe",
}
let printName = function( hometown ,state, country){
console.log(this.firstname + " " + this.lastname + " " + hometown + " " + state + " " + country);
};
// Inbuilt Bind Method
let printMyName = printName.bind(name, "New York", "NY");
console.log(printMyName);
printMyName("USA");
// Creating our own Bind method
//1. Put the function in the global scope
//2. Create a new function
//3. Create a new context
//4. Return the new function
Function.prototype.mybind = function (...args) {
let context = this,
params = args.slice(1); //Except the first argument everything is consoidered
return function (...args2){
context.apply(args[0], [...params, ...args2]);
}
};
let printMyName2 = printName.mybind(name, "New York", "NY");
printMyName2("USA");
Output:
[Function: bound printName]
John Doe New York NY USA
John Doe New York NY USA
- Debouncing is a technique to avoid the function to be called too often, hence avoiding UI freezing.
- The below example is of debouncing function, where user searches for a product in search bar, if he/she types more than 1 character, the function will be called again and again until the user types faster than 300 milliseconds, in that case lesser number of function calls will be made.
let counter = 0;
const getData = () => {
console.log("Fetching Data.. ", counter++);
};
//Only debounce and call the getData() method when the difference between two key presses is greater than 300 ms
const debounce = (fn, delay) => {
let timer;
return function () {
let context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, arguments);
}, delay);
};
};
const betterFunction = debounce(getData, 300);
- Both are used to optimize the performance of the web app, it happens by limiting the rate of function calls
Debouncing
happens when the user gives a pause of that specified delay (say 300ms) amount set by the website and then only further results are fetched from the server.Example
: If a user wants to search for 'Adidas Shoes' on an e-commerce webiste, if debouncing is not there in the website then for every word that user will type, API calls will be made to the server which can lag the UI (imagine for millions of users accessing that website). If debouncing is being used in the website at a delay of 300ms, then after typing without a gap of 300ms user can get the results without making lot of function/API calls to the server. Like if he/she types 'Adidas' within 300ms then only 1 call will be made and next call will be made for 'Adidas Shoes' after 300ms.Throttling
is also similar to Debouncing but here there is a time frame specified by the website, in that time frame only one function call will be made.Example
: When a user searches for 'Adidas Shoes', and if the throttling dealy time is 300ms, then no matter how fast the user types, the function call will only be made after 300ms after the user starts searching.- So in-case of a
searching bar of an e-commerce website, debouncing is better than throttling
because if you type at a decent speed, then it will make API calls faster for that keyword. In throttling you might have to wait for a while to get the results, as the website has a specified delay. - In-case we have to
track website resizing
. In debouncing suppose the website is being resized at varying speeds then in that case if the user gets slow while resizing then it will make few API calls. So it will perfect for finding how many times the user is resizing.In throttling, the function call will be made only after certain intervals of time, which will satiate our need for finding the frequency of the user resizing the screen.
- Throttling is similar to Debouncing, but here there is a time frame specified by the website, in that time frame only one function call will be made.
Normal Thorttling Code:
const expensive = () => {
console.log('expensive');
}
const betterExpensive = () => {
throttle(expensive, 5000);
}
window.addEventListener('resize', betterExpensive);
Making your own throttling function:
const throttle = (func, limit) => {
let flag = true;
return function () {
let context = this;
args = arguments;
if (flag) {
func();
flag = false;
setTimeout(() => {
func.apply(context, args);
flag = true;
}, limit);
}
};
};
window.addEventListener('resize', throttle(() => {
console.log('Window resized');
}, 5000));
- In the below example we have made a copy of the subtract method and we can make more methods out of it and pass some arguments in the function. Like 2 is set as
x
in subtract function.
let subtract = function (x, y) {
console.log(x - y);
};
let subtractByTwo = subtract.bind(this, 2);
let subtractByNum = subtract.bind(this, 9, 1);
// How bind function works by creating a copy of the original function.
// let subtractByTwo = function (y){
// x = 2;
// console.log(x * y);
// }
// Passed as the second argument
subtractByTwo(3); //This is Currying
subtractByNum(3); //This will be ignored
Output:
-1
8
- In the below example we are using closure for currying the function.
let multiply = function (x) {
return function (y){
console.log(x * y);
};
};
let multiplyByTwo = multiply(2);
multiplyByTwo(4);
Output:
8
- These are the two ways of event propogating in a DOM Tree
- In Event Bubbling the event is propogated from the selected element and up the hierarchy till the end of the DOM
- In Event Capturing the event goes down the hierarchy till the selected element and propogates the event to the selected element. Oppposite order of Event Bubbling.
- Some examples of Bubbling and Capturing are given below:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
div{
min-width: 100px;
min-height: 100px;
padding: 30px;
border: 1px solid black;
}
</style>
</head>
<body>
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
document.querySelector('#grandparent').addEventListener('click', ()=>{
console.log('Grandparent clicked!');
});
//If no value is passed as the second argument, the default is false which means bubbling of events.
document.querySelector('#parent').addEventListener('click', ()=>{
console.log('Parent clicked!');
});
document.querySelector('#child').addEventListener('click', ()=>{
console.log('Child clicked!');
});
- On Clicking Child
document.querySelector('#grandparent').addEventListener('click', ()=>{
console.log('Grandparent clicked!');
}, true);
document.querySelector('#parent').addEventListener('click', ()=>{
console.log('Parent clicked!');
}, true);
document.querySelector('#child').addEventListener('click', ()=>{
console.log('Child clicked!');
}, true);
- On Clicking Child
- Capturing takes place first and then Bubbling
//Example - 1
document.querySelector('#grandparent').addEventListener('click', ()=>{
console.log('Grandparent clicked!');
}, true); //Capturing
document.querySelector('#parent').addEventListener('click', ()=>{
console.log('Parent clicked!');
}, false); //Bubbling
document.querySelector('#child').addEventListener('click', ()=>{
console.log('Child clicked!');
}, false); //Bubbling
- On Clicking Child
//Example - 2
document.querySelector('#grandparent').addEventListener('click', ()=>{
console.log('Grandparent clicked!');
}, true); //Capturing
document.querySelector('#parent').addEventListener('click', ()=>{
console.log('Parent clicked!');
}, false); //Bubbling
document.querySelector('#child').addEventListener('click', ()=>{
console.log('Child clicked!');
}, true); //Capturing
- On Clicking Child
- It is used to stop the propogation of the event.
document.querySelector('#grandparent').addEventListener('click', ()=>{
console.log('Grandparent clicked!');
}, false);
document.querySelector('#parent').addEventListener('click', (e)=>{
console.log('Parent clicked!');
e.stopPropagation();
}, false);
document.querySelector('#child').addEventListener('click', ()=>{
console.log('Child clicked!');
}, false);
- On first clicking Grandparent, then Parent (Event Propogation stopped it from going to Grandparent), then Child (Same Case with Child as well, unable to propogate after parent)
document.querySelector('#grandparent').addEventListener('click', ()=>{
console.log('Grandparent clicked!');
}, false); //Bubbling
document.querySelector('#parent').addEventListener('click', (e)=>{
console.log('Parent clicked!');
e.stopPropagation();
}, false); //Bubbling
document.querySelector('#child').addEventListener('click', (e)=>{
console.log('Child clicked!');
}, true); //Trickling
- On Clicking Child
- It is a technique of handling evenets on our web page in a better way
- Capturing and bubbling allow us to implement one of the most powerful event handling patterns called event delegation
- In layman terms, Instead of attaching event handlers to each and every child elements, we should rather attach an event handler to parent of these elements
- Example for Event Delegation:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<div>
<ul id="category">
<li id="laptop"> Laptops </li>
<li id="cameras"> Cameras </li>
<li id="phones"> Phones </li>
</ul>
</div>
<script src="index.js"></script>
</body>
</html>
document.querySelector('#category').addEventListener('click', (e) => {
console.log(e.target.id);
if(e.target.tagName === 'LI') {
window.location.href = `/category/${e.target.id}`;
}
})
- Example of Behaviour Pattern using Event Delegation
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<div id="form">
<input type="text" id="name" data-uppercase>
<input type="text" id="surname">
<input type="text" id="address" data-uppercase>
</div>
<script src="index.js"></script>
</body>
</html>
document.querySelector('#form').addEventListener('keyup',(e)=>{
console.log(e);
if(e.target.dataset.uppercase != undefined){
e.target.value = e.target.value.toUpperCase();
}
});
- It saves a lot memory by reducing the number of additional event listeners
- It also mitigates the risk of performance bottleneck
- Writing less code (Rather than attaching to each child, we attach it to parent)
- All the events are not bubbled up like scroll, blur etc.
- If you are using stop propogation anytime, then eventually that would work, to let event delegation work you dont have to use stop propogation and let the events bubble up
- These are boolean attributes that are used along with script tags to load the external scripts efficiently to the web page
- When we are loading a web page then they are two major things happening in your browser, one is the HTML parsing and the second is the loading of the scripts
- The loading of the scripts consists of two parts, one is fetching scripts from the network and the other is executing the scripts
-
Normal Case
: The browser is parsing the HTML line by line and suddenly encounters a script tag, then the browser stops the parsing at that point of time and then sees the script tag and fetches the script from the network and then executes the script. Then after the script work is done, the HTML parsing will continue. -
Async
: In this case, meanwhile the HTML parsing is going on, the scripts are fetched from the network asyncronously, then HTML parsing is stopped, and the scripts are executed. Once these scripts are executed, then the remaining HTML parsing is completed. -
Defer
: In this case, meanwhile the HTML parsing is going on, the scripts are fetched from the network in parallel. Once the HTML parsing completes then the scripts are executed.
- The async attributes does not guarantee the order of execution of the scripts but defer does
- Like if some scripts are dependent on other scripts, then we should use defer attribute, since defer attribute will execute the scripts in the order of their loading
- But suppose we have to load some external scripts like Google Analytics which are quite modular and are independent of our modular code, so it makes sense to use async attribute
- Web Storage API is nothing but key value pair storage, which is used to store data in the browser
- There are two ways to store this data in the browser, one is local storage and the other is session storage
- Lets say a user is visiting a web app, as soon as he visits the web app, session is started and data which is stored in the session storage will only be persistant till the user closes the browser
- But Session Storage is very useful than cookies, session storage data is not being sent to the server while making the network request calls and this session storage has a larger capacity to hold (~5MB)
- Local Storage is also similar to session storage but the difference here is that once the user closes the tab, the data is still persistant in the local storage
- Local Storage and Session Storage are not accessible from the web page which is not same origin policy because of security reasons.
- The Origin comprises of three things:
Protocol
,Host
andPort
- Lets suppose for the website:
http://akshaysaini.in
- We can access this website from the browser by typing in the address bar:
http://akshaysaini.in/data.php
- We cant access this website on:
https://akshaysaini.in
https://blog.akshaysaini.in
https://akshaysaini.in:8080
because of the changes made toprotocol
,host
andport
respectively.
- Whenever we create a JavaScript Object/Function, the JavaScript engine automatically without even letting you know attaches your objects/function to some hidden functions and properties which is called
Prototype
.
let arr = ["Aditya", "Akshay"];
Array.prototype === arr.__proto__
arr.__proto__ === Object.protoype
because the Array is an object and the Object is an objectarr.__proto__.__proto__.__proto__ === null
because the Object's prototype has no prototype
let object = {
name: "John",
city: "New York",
getIntro : function() {
console.log(`Name is ${this.name} and city is ${this.city}`);
}
}
- Prototype chaining proves the statement that everything in JavaScript is an object.
- So when you try to access the property of
object2
which is not there likecity
then it goes to theobject
proto and prints when it finds it.
Function.prototype.mybind= function(){
console.log("abcde");
}
function fun(){
}
- Using callback we can do asynchronous programming in JavaScript
- We can take a piece of code and pass it as an argument to another function and then that function can be called at a later point of time
- Async programming in JavaScript exist because of callback exist
console.log("Namaste");
setTimeout( function() {
console.log("I am inside the setTimeout function");
}, 2000);
- An example of callback hell for a shopping cart application
const cart = ["shoes", "pant", "kurta"];
api.createOrder(cart, function () {
api.proceedToPaymnet(function () {
api.showOrderSummary(function () {
api.updateWallet(function () {
});
});
});
});
- When we have a large codebase and we have so many apis here and there, they are dependent one after the another so we fall into this trap of callback hell
- Our code starts to grow horizontally instead of vertically, this style of code is unreadable and unmaintainable
- The structure is also called
Pyramid of Doom
- We are blindly trusting our createOrder API that it will call the callback function, but what if it doesnt call the callback function, then we are in trouble
- The createOrder API is a risky API, it could have potential bugs that it might not call the callback function
- Promises are objects representing the eventual completion of an asynchronous operation and it's resulting value
- They are immutable, you cannot alter a promise once it's resolved, it comes with a lot of trust
const cart = ["shoes", "hat", "kurta"];
// Will cause callback hell
createOrder(cart, function(orderId){
proceedToPayment(orderId);
});
//Resolved using promises
const promise = createOrder(cart);
// {data: undefined} changes to {data: orderDetails}
promise.then(function(orderId){
proceedToPayment(orderId);
})
- Promise is an object which has a function called
then
which takes a callback function as an argument - It is undefined in the beginning but when the promise is resolved then it gets the data
- createOrder API is an async operation and it returns a promise object
- The promise object will be filled with the value after an async time, meanwhile other lines of code will be executed
- Earlier we were
passing
the function into the createOrder API i.e another function, but now we areattaching
the function to the promise object - Promises give us the guarantee that the function present inside the promise object will be called
- Promises will call the inner function once and only once
const GITHUB_API = "https://api.github.com/users/rishit30g";
const user = fetch(GITHUB_API);
user
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Something went wrong");
}
})
.then((data) => {
console.log(data.id);
})
.catch((error) => {
console.log(error);
});
- The promise object contains three things: [[Prototype]], [[PromiseState]], [[PromiseResult]]
- Prototype will say 'Promise'
- PromiseState will say 'pending' or 'fulfilled' or 'rejected'
- PromiseResult will say 'undefined intially' or 'data that will be fetched' or 'error'
- Promise Result will be filled with the data that we get from the API after execution of the promise object
const cart = ["apple", "orange", "banana"];
// Callback Hell
createOrder(car, function(orderID) {
proceedToPaymeNt(orderID, function(status){
showOrderStatus(status, function(){
updateWalletBalance();
});
});
});
// Equivalent Code using Promise Chaining
const promise = createOrder(cart);
promise.then(function(orderID){
return proceedToPaymeNt(orderID);
}).then(function(status){
return showOrderStatus(status);
}).then(function(){
return updateWalletBalance();
});
- Example of Simple promise with resolve and reject
const cart = ["shoes", "pants", "kurta"];
const promise = createOrder(cart);
promise.then(function(orderID){
console.log("Order ID: ", orderID);
}).catch(function(err){
console.log("Error: ", err.message);
});
function createOrder(cart){
const pr = new Promise(function(resolve, reject){
if(isValidCart(cart))
{
const orderID = "1223";
if(orderID)
{
resolve(orderID);
}
else{
const err = new Error("Invalid Order");
reject(err);
}
}
else{
const err = new Error("Invalid Cart");
reject(err);
}
})
return pr;
}
function isValidCart(cart){
return Array.isArray(cart) && cart.length > 0;
}
- Example of Promise with Chaining
const cart = ["shoes", "pants", "kurta"];
const promise = createOrder(cart);
promise.then(function(orderID){
console.log("Order ID: ", orderID);
return orderID;
}).then(function(orderID){
return proceedToPayment(orderID);
}).then (function(paymentInfo){
console.log("Payment Info: ", paymentInfo);
}).catch(function(err){
console.log("Error: ", err.message);
});
function createOrder(cart){
const pr = new Promise(function(resolve, reject){
if(isValidCart(cart))
{
const orderID = "1223";
if(orderID)
{
resolve(orderID);
}
else{
const err = new Error("Invalid Order");
reject(err);
}
}
else{
const err = new Error("Invalid Cart");
reject(err);
}
})
return pr;
}
function isValidCart(cart){
return Array.isArray(cart) && cart.length > 0;
}
function proceedToPayment(orderID){
const pr = new Promise(function(resolve, reject){
resolve(`Payment Info is ${orderID}`);
});
return pr;
}
- Async is a keyword that is used before a function to make it asyncronous
- Async functions always returns a promise, so even if we add boolean, float, string as a return type in async function, it will get wrapped in a promise and then it will be returned
- Here are few examples of async functions:
async function getData() {
return "Namaste";
}
const dataPromise = getData();
dataPromise.then((res) => console.log(res));
Output:
Namaste
- In this example we are creating a promise and returning it through an async function
const p = new Promise((resolve, reject) => {
resolve("Promise Resolved Value!!");
});
async function getData(){
return p;
}
const dataPromise = getData();
dataPromise.then((res) => console.log(res));
Output:
Promise Resolved Value!!
- Async and Await combination are used to handle promises
- Await is used in front of a promise to wait for the promise to resolve and then it will return the value
- Await can only be used inside an async function
const p = new Promise(resolve => {
resolve("Promise Resolved Value");
});
async function handlePromise() {
const val = await p;
console.log(val);
}
handlePromise();
Output:
Promise Resolved Value
- An example of deplayed promise using async and await vs traditional handling
const p = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value!!")
}, 5000);
});
async function handlePromise() {
// JS Engine will wait for the promise to be resolved
const val = await p;
console.log(val);
console.log("Hello World");
}
handlePromise();
function getData(){
// JS Engine will not wait for the promise to be resolved
p.then((res) => console.log(res));
console.log("Namaste JavaScript");
}
getData();
Output (On only executing handlePromise()
)
## After 5 seconds..
Promise Resolved Value!!
Hello World
Output (On only executing getData()
)
Namaste JavaScript
## After 5 seconds..
Promise Resolved Value!!
- How async await behaves when there is a statement before the await statement
const p = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value!!");
}, 5000);
});
async function handlePromise() {
console.log("Hello World");
const val = await p;
console.log(val);
console.log("Bye World");
}
handlePromise();
Output:
Hello World
## After 5 seconds
Promise Resolved Value!!
Bye World
- Having multiple promises in async await function, with same delay both the promises will be resolved at the same time
const p = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value!!");
}, 5000);
});
async function handlePromise() {
console.log("Hello World");
const val = await p;
console.log(val);
const val2 = await p;
console.log(val2);
console.log("Hello World 2");
}
handlePromise();
Output
Hello World
## After 5 seconds
Promise Resolved Value!!
Promise Resolved Value!!
Hello World 2
- Having multiple promises in async await function, the promise with a higher delay will be resolved first and then the other promise will be resolved in this case first i.e the promise with higher time is resolved and then the promise with lower time is covered in that time
const p1 = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value 1!!");
}, 2000);
});
const p2 = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value 2!!");
}, 4000);
});
async function handlePromise() {
console.log("Hello World!!");
const val2 = await p2;
console.log(val2);
const val = await p1;
console.log(val);
}
handlePromise();
Output:
Hello World!!
## After 4 seconds
Promise Resolved Value 2!!
Promise Resolved Value 1!!
- Having multiple promises in async await function, the promise with a higher delay will be resolved first and then the other promise will be resolved in this case first i.e the promise with higher time is resolved and then the promise with lower time is covered in that time
const p1 = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value 1!!");
}, 2000);
});
const p2 = new Promise(resolve => {
setTimeout(() => {
resolve("Promise Resolved Value 2!!");
}, 4000);
});
async function handlePromise() {
const val = await p1;
console.log(val);
console.log("Hello World!!");
const val2 = await p2;
console.log(val2);
}
handlePromise();
Output:
## After 2 seconds
Promise Resolved Value 1!!
Hello World!!
## After 2 more seconds
Promise Resolved Value 2!!
-
What we ideally start thinking is that JavaScript has started waiting for functions but that not true, the fact that time, tide and JavaScript wait for none is 100% true.
-
What is happening is that in the callstack the handlePromise() is called and then the first promise is resolved, after that the handlePromise() moves out of the callstack.
-
Again the ongoing line by line process of JavaScript goes on and when another promise is encountered the handlePromise() is bought to the callstack again and it will continue from where it left off in the suspended state.
- Fetch is a promise, it gives a response in return, it is a readable stream which needs to converted to JSON which is again a promise that will give a promise in return which can be printed in the console.
const API_URL = "https://api.github.com/users/Rishit30G";
async function handlePromise() {
const data = await fetch(API_URL);
const jsonValue = await data.json();
console.log(jsonValue);
}
handlePromise();
- It is handled using try and catch block
const API_URL = "https://api.github.com/users/Rishit30G";
async function handlePromise() {
try {
const data = await fetch(API_URL);
const jsonValue = await data.json();
console.log(jsonValue);
} catch (err) {
console.log(err);
}
}
handlePromise();
- Another traditional method is by adding a catch statement to the handlePromise() function which is indeed a promise
const API_URL = "https://api.github.com/users/Rishit30G";
async function handlePromise() {
const data = await fetch(API_URL);
const jsonValue = await data.json();
console.log(jsonValue);
}
handlePromise().catch((err) => console.log(err));
- Equivalent promise code for better understanding
const API_URL = "https://api.github.com/users/Rishit30G";
function handlePromise() {
return fetch(API_URL)
.then(data => data.json())
.then(jsonValue => {
console.log(jsonValue);
return jsonValue;
});
}
handlePromise().catch((err) => console.log(err));
- Async Await is just a syntactical sugar over promises, it is just a different way of writing promises
- It is just a mordern way of writing promises, we don't have to deal with callbacks and promise chaining
Promise.all()
is used to run multiple promises in parallel, it takes an array of promises as an argument and returns a promise- It is used to run multiple promises in parallel and wait for all of them to complete and then do something with the result
- If one of them fails, then the entire promise.all() fails
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 1!!");
}, 1000);
});
const p2 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 2!!");
}, 10000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Promise Rejected Value 3!!");
}, 2000);
});
async function handlePromise() {
const result = await Promise.all([p1, p2, p3]);
console.log(result);
}
handlePromise();
Output:
Will get error after 2 seconds, all promises will fail
Promise.allSettled()
returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 1!!");
}, 1000);
});
const p2 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 2!!");
}, 10000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Promise Rejected Value 3!!");
}, 2000);
});
async function handlePromise() {
const result = await Promise.allSettled([p1, p2, p3]);
console.log(result);
}
handlePromise();
Output:
[
{ status: 'fulfilled', value: 'Promise Resolved Value 1!!' },
{ status: 'fulfilled', value: 'Promise Resolved Value 2!!' },
{ status: 'rejected', reason: 'Promise Rejected Value 3!!' }
]
Promise.race()
returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 1!!");
}, 1000);
});
const p2 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 2!!");
}, 10000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Promise Rejected Value 3!!");
}, 2000);
});
async function handlePromise() {
const result = await Promise.race([p1, p2, p3]);
console.log(result);
}
handlePromise();
Output:
Promise Resolved Value 1
Promise.any()
takes an iterable of Promise objects and, as soon as one of the promises in the iterable fulfills, returns a single promise that resolves with the value from that promise. If no promises in the iterable fulfill (if all of the given promises are rejected), then the returned promise is rejected with an AggregateError, a new subclass of Error that groups together individual errors.
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 1!!");
}, 1000);
});
const p2 = new Promise((resolve) => {
setTimeout(() => {
resolve("Promise Resolved Value 2!!");
}, 10000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Promise Rejected Value 3!!");
}, 2000);
});
async function handlePromise() {
const result = await Promise.any([p1, p2, p3]);
console.log(result);
}
handlePromise();
Output:
Promise Resolved Value 1!!
// Whichever promise gets fulfilled first, that value will be returned
// Incase all the promises are rejected then it will throw an AggregateError
-
The
this
keyword in the global space will represent the global object, it is 'window' in case of browser, and incase of NodeJS it will represent the 'global' -
this
keyword inside a function with strict mode
"use strict";
function x(){
// Depends on strict / non-strict mode
console.log(this);
}
x();
Output:
// Strict
undefined
// Non Strict
Window Object
-
If the value of this keyword is undefined or null, then
this
keyowrd will be replaced with the gloablObject only in case of non strict mode, but in case of strict mode it will remain undefined or null, this is calledthis substitution
-
this
keyword value depends on how the function is called
x();
window.x();
Output:
//Strict
undefined
windowObject
//Non Strict
windowObject
windowObject
this
keyword in object method
"use strict"
const obj = {
name: "James",
x: function(){
console.log(this);
}
}
obj.x();
Output:
//Strict
{name: "James", x: ƒ}
//Non Strict
{name: "James", x: ƒ}
this
keyword in call, apply and bind
"use strict"
const student = {
name: "James",
printName: function(){
console.log(this.name);
}
}
student.printName();
const student2 = {
name: "John"
}
student.printName.call(student2); // Call method takes the object as an argument and then it will call the function with the object as the value of `this` keyword
Output:
James
John
this
keyword in arrow function behaves differently than normal function, it is lexically scoped, it will take the value from the parent scope
// var a = 10;
const obj = {
a: 10,
x: () => {
console.log(this.a);
},
}
obj.x();
Output:
// If var a = 10 is not commented
10
// If var a = 10 is commented
windowObject
const obj2 = {
a: 10,
x: function (){
const y = () =>{
console.log(this);
}
y();
}
}
obj2.x();
Output:
{a: 10, x: f}
this
inside DOM elements: It will give reference to the HTMLelement
<button onClick="alert(this)"> Click Me </button>
Output:
[object HTMLButtonElement]