JavaScript Interview Guide
JavaScript Interview Guide
#22 page
Topics to be complete
High
Arrow functions `this` behaviour
Immutability rules and practices
Function purity
Function memoization
Basic monadic operations
Medium
polyfill(map, reduce, forEach, filter, slice, find, flat, call, apply, bind, promises, compose and pipe)
Mutation observer)
Low
Function currying and infinite currying
document.getElementById("idName").addEventListener("click", myFunction,
{capture:true});
Call Stack
Maintains the order of execution of Execution Context.
It keeps track of multiple function calls.It is like a stack in the data structure.
Also known as Execution Context stack, Program Stack, Control Stack, Runtime stack, machine stack.
Hoisting
Hoisting is a finomina by which we can access variables and functions even before its initialization without any
error.
console.log(foo,getName()); // undefined afroz quraishi
var foo = 'bar';
function getName(){console.log(“afroz quraishi”);}
(variable and proper function) Arrow function behaves like a variable so it print undefined in case of hoisting.
Shortest Js Program
1. Shortest Program: Empty file. Still, browsers make global EC and global space along with Window object.
2. Global Space: Anything that is not inside a function, is in the global space.
3. Variables(var only) present in a global space can be accessed by a "window" object. (like window.a)
4. In global space, (this === window) true, object.
Scope
Where you can access a specific variable and function in your code.
The scope is directly dependent on the Lexical Environment(local memory along with the lexical
environment of its parent). A lexical environment is created whenever the execution context is created.
Golden points:
1. let and const are hoisted but its memory is allocated at another place than the window which cannot be
accessed before initialisation.
2. The Temporal Dead Zone exists until a variable is declared and assigned a value.
3. window.variable OR this.variable will not give value of variable defined using let or const.
4. We cannot redeclare the same variable with let/const(even with using var the second time).
5. const variable declaration and initialisation must be done on the same line.
6. There are three types of error:
[1] referenceError {given where variable does not have memory allocation}
[2] typeError {given when we change type that is not supposed to be changed}
[3] syntaxError {when proper syntax(way of writing a statement) is not used}.
7. Use const wherever possible followed by let, Use var as little as possible(only if you have to). It helps
avoid errors.
8. Initialising variables at the top is a good idea, helps shrink TDZ to zero.
Scope:- scope of a variable or a function is the place where these are accessible.
Block scope:- The variables and functions present within the scope of a block section. And block follows the
lexical scope chain pattern while accessing the variable.
Function Scope:- What are the variables and functions we can access Inside a function are called function
scope.
{
var a=10;
const b=20;
let c=23;
}
Snapshot
Block
b: undefined
c: undefined
Global
a: undefined
Shadowing:- Providing the same name to the variable as of those variables which are already present in
outer scope. Let and const are the same as usual.
var a=100;
{
var a=10;
let b=30;
const c=32;
console.log(a,b,c); // 10 30 32
}
console.log(a); // 10
iLLegal Shadowing:- as 'var' declaration goes to 'Global environment' and sets in Memory context, it cannot
be set using 'Block environment' value Hence: Uncaught SyntaxError: Identifier 'a' has already been
declared
INVALID
let a=20;
{
var a=10;
}
VALID
var a=10;
{
let a=322;
}
It will work. var is a function scope and let is a block scope.
It cannot cross the boundary of its scope.
const a=20;
{
const a=80;
{
const a=200;
}
}
console.log(a); //20
Closure
A function along with reference to its outer environment together forms a closure.
Function bundled together with its lexical environment(local memory along with the lexical environment of its
parent) is known as a closure. Whenever a function is returned, even if it vanishes in the execution context, it
still remembers the reference it was pointing to. It's not just that function alone it returns but the entire
closure.
Function bundle/bind together along with its lexical scope(store the reference to the outer function) forms a
closure.
The closure is a function in a function. The inner function has access to the outer function scope and
parameters even after the inner function has returned.
function outer(){
var a=10;
function inner(){
console.log(a)
}
a=100;
return inner;
}
var z=outer();
……… 10 miliion line are here
z(); // still print 100
setTimeout+Closure
setTimeout is not a part of JavaScript it is a part of the web/browsers API's.
When every line of code runs successfully then only it will run even if setTimeout is 0 second and run at the
very last.
Web/browser Api’s: setTimeout, Dom Api, fetch, localStorage, console, location, bluetooth.
Golden points
1. setTimeout stores the function in a different place and attached a timer to it, when the timer is finished it
rejoins the call stack and executes.
2. Without closure the var reference gives the latest value as it does not retain the original value but rather
has the reference so any update in value after timeout will be shown.
3. If we use let/const because they have block scope, every time a new copy of variable is attached, thus this
can be done without closure.
function x(){
var i=1;
setTimeout(function (){
console.log(i);
},3000);
console.log("Hello");
}
x(); // Hello 1
Function expression: Function is first created and assigned to a variable so that it can be called by its
variable name and unless it is defined, it cannot be executed otherwise it throws out "Uncaught TypeError"
var b=function(){console.log("b called");}
The difference between function statement and function expression is hoisting when we call both a b function
in the top then in b we will get typeError: b is not a function
Anonymous function: function where there is no need to define a name for the function, it just can be
assigned to variable
var b=function (){}
Named function: Normal function with its name assigned to a variable, In this case, you cannot call function
by its name in outer scope!! (Scope Chain).
var c=function xyz(){}
FIRST CLASS FUNCTION / first-class citizens: The ability to function is used as values and pass it to the
other function as an argument and return from a function this ability is known as a first-class function.
var x=function(param1){
param1();
return function(){}
}
x(function z(){});
Arrow function:
var x=()=>console.log("arrow function");
Currying function: Currying is the process of taking a function with multiple arguments and turning it into a
sequence of functions each with only a single argument by applying currying, n-ary function turns it into a
unary function.
function calculateVolume(length) {
return function (breadth) {
return function (height) {
return length * breadth * height;
}
}
}
console.log(calculateVolume(4)(5)(6)); // 120
Higher Order Function: A higher-order function is a function that accepts another function as an argument
or returns a function as a return value or both of them.
Map, filter and reduce are some examples of higher-order functions that are already built into JavaScript.
function x(param1){
param1();
return function(){}
}
x(function z(){});
let x="afroz";
x[3]='z'; // here I tried to change the internal state of the immutable type but
unable to change because string is an immutable type.
console.log(x); //output: afroz
let x="afroz";
x="my name is Afroz"; // here it will change because we have changed the whole string
here.
console.log(x);
Deep Copy(Completely new/ call by value): A deep copy means that all of the values of a
variable are copied into a new variable and this new variable is completely disconnected from the
original variable. The changes will not affect the original value.
Ex: number, boolean, string, undefined, null, symbol.
Shallow copy(call by reference): A shallow copy means that certain values are still connected to
the original variable. So the change made to the new variable will have an effect on the original
variable as well. Ex: Object, Array, Set, Function…
function logQuote(quote){
console.log(quote);
}
createQuote("eat your vegetables!", logQuote); // 1
// Result in console:
// Like I always say, eat your vegetables!
Although javascript is a sync and single-threaded language but using callback we can do async things inside
js.
EVENT LOOP
The job of the event loop is to continuously watch the call stack to check whether it is empty. As soon as it
found an empty call stack it popped the microtask callback and pushed it to the call stack for execution. Once
the microtask queue gets empty then it starts a popped call back from the macro task queue aka callback
queue and pushes the call back to the call stack for further execution.
Whenever any async task is found it stores it in the web API and after successful execution, it moves the
callback function into MicroTask / CallBack Queue.
Bind: B for bundle. Bind the function with an object and then call those functions.
const obj = {
num: 5,
};
function add(a,b,c) {
return this.num + a + b + c;
}
console.log(add.call(obj, 1,2,3)); // 11
console.log(add.apply(obj, [1,2,3])); // 11
var bind=add.bind(obj,1);
bind(2,3); //11
OR add.bind(obj)(1,2,3) //11
Callback Hell
In Node.js is the situation in which we have complex nested callbacks. In this, each callback
takes arguments that have been obtained as a result of previous callbacks. This kind of
callback structure leads to lesser code readability and maintainability. We can avoid the
callback hell with the help of Promises/promise chaining.
When using the Promise constructor you must invoke either resolve() or reject() callbacks. The
Promise constructor doesn’t use your return value.
● console.log() always returns undefined which can be handle by .then
● A return is key in order to pass a value to the next .then.
● .catch’s can be used to ignore (or override) errors simply by returning a regular value.
● .then’s pass data sequentially, from return value to the next .then(value => /* handle value */)
Output:
The fails!
Output:
start
middle
1
end
success
Promise combinators(all, race, allSettled, any)
syntax:
Promise.xxx([Promise1,Promise2,Promise3,...]).then(res=>con
sole.log(res));
Promise.all- return the all resolved promise. If one of the promises fails then it fails all
of them.
Promise.allSettled- same as Promise.all but for the failed promise it returns all the
rejected/fulfilled promises.
function action(msz){
return new Promise((resolve,reject)=>{
setTimeout(()=>resolve(msz),3000);
})
}
const result=async()=>{
try{
const msz=await action("resolve it");
console.log(msz);
} catch(error){
console.log("error occurred in try block");
}
}
arr.__proto__. This is the object where the javascript engine is putting all these functions
and methods.
var obj={
name:"afroz",
age:20
}
objectOrArrayOrFunctionName.__proto__.methodName
obj.xxx -> we can access all the methods and functions in that object.
arr.xxx -> here also we can access all the methods and functions of an
array.
arr.__proto__ == Array.prototype
arr.__proto__.__proto__ == Object.prototype
arr.__proto__.__proto__.__proto__ == NULL
object.__proto__ == Object.prototype
object.__proto__.__proto__ == NULL
fun.__proto__ == Function.prototype
fun.__proto__.__proto__ == Object.prototype
fun.__proto__.__proto__.__proto__ == NULL
let object={
name:"Afroz",
city:"Gwalior",
getIntro:function(){
console.log(this.name,"from",this.city);
}
}
let object2={
name:”Alina”
}
//never do this
object2.__proto__=object //inherit all property of object inside
object2;
Function.prototype.mybind == fun.__proto__.mybind
so mybind is now accessible to all the functions if we declare using Function.prototype.XXX.
why __proto__ why not a simple name. It's just because you don’t end up messing up the
prototype by mistake and nobody is going to mess up with __proto__ :)
Event Bubbling, Capturing aka Trickling
Bubbling: When an event happens on an element, it first runs the handlers on it, then on its parent, then all
the way up on other ancestors. Event bubbling is used by default when we don't pass a third argument in the
eventListener. jaha click kiye uske uper ka sb click hota jayega.
Capturing: Event capturing is one of two ways to do event propagation in the HTML DOM. In event
capturing, an event propagates from the outermost element to the target element. It is the opposite of event
bubbling, where events propagate outwards from the target to the outer elements. Capturing happens before
bubbling. jaha click kiye uske reverse se jha click kiye wha tk aayega.
Output:
1. When we click on child selector
Child Clicked!
Parent Clicked!
Grandparent Clicked!
2. When we click on parent selector
Parent Clicked!
Grandparent Clicked!
Capturing Example:-
Note: If there is mixing of bubbling and capturing the first capturing will happen and the
bubbling will happen.
Event Delegation
Benefit:
1. save memory
2. less code
3. Dom Manipulation
Limitation:
1. All the events are not bubbled up (blur, focus, resize window, and scroll).
2. If we use e.stopPropagation() the it will not call further.
Cookies(client/server): expire based on the setting and working per tab and window.
- We can set expiry date.
- Data transfer to the server is exist
- The data is sent with the request from the client to the server
- The data is stored on the browser only.
- The data is not sent with the request from the client to the server
Currently mostly all websites are using localStorage. Instead of making a network call, store the data in
localStorage and use it(more costly). Getting that data from localStorage is pretty fast(O(1)).
Spread and Rest Operator(...)
Syntax is the same but use cases are different.
Spread: It creates Deep copy of an array or object. used to divide array elements or properties on objects
so that array elements can be added/inserted into new arrays.
const students = ['Sami', 'Chhaku', 'Sam'];
const newStudents = [...students, 'Tom'];
console.log(newStudents); // ["Sami", "Chhaku", "Sam", "Tom"]
const student = {
id: 1,
firstName:'Sami'
}
const updatedStudent = {
...student,
lastName: 'Zingare'
}
console.log(updatedStudent);
Output:
Rest: The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
It is always defined as a last argument; we can't declare it between or starting as an argument.
function sum(...inputs){
console.log(inputs)//it will give you in the form of an array
//console.log(...inputs)//it will give you direct values
let total = 0;
for (let i of inputs){
total=total+i;
}
console.log(total);
}sum(1,2,3,4,5,6,7,8,9,10)
MISTAKE TO AVOID
function sum(...inputs, last) // wrong rest always defined as end.
function sum(start, …inputs, last) // wrong
OK
function sum(start,...inputs);
function sum(first,second,third,...inputs)
Limitation:
Rest parameters take all the remaining arguments of a function and package them in an array. That naturally
brings two limitations. You can use them max once in a function, multiple rest parameters are not allowed.
Qus1:
this.a=5;
function getParam(){
console.log(this.a); // this points to the parent object(window);
} getParam(); // 5
Ques2:
const user={
name:"afroz",
age:23,
getUser(){
console.log(this.name); // this point to its parent object(user);
}
}
user.getUser(); // afroz
Ques3:
const user={
name:"afroz",
age:23,
getDetail:{
lname:”Quraishi”,
getUser(){
console.log(this.name,this.lname);
}
}
}
user.getDetail.getUser(); // undefined Quraishi
This in Arrow Function
Arrow Functions: Arrow functions don't have a defined this. Instead, they treat it as a variable and they try
to get the value lexically (inherit from parent scope)
It value comes from its parent normal function object.
const user={
name:"afroz",
age:23,
getUser:()=>{
console.log(this.name)
}
}
user.getUser(); //It print nothing. bcs in => fun `this` point to the window object.
ex;
const user={
name:"afroz",
age:23,
getDetails(){ // Normal Function
const nestedArrow=()=>console.log(this.name); // Arrow Function
nestedArrow();
}
} user.getDetails();
https://www.youtube.com/watch?v=rv7Q11KWmKU&t=92s
https://chhakulizingare.hashnode.dev/spread-operator-and-rest-operator
Q4.
console.log("Hello World");
let check=true;
setTimeout(()=>{
check=false;
console.log("nothing");
},2000);
while(check){
console.log("i am running");
check=false;
}
console.log("end");
Q3. What will be the output?
1.
for(var i=0;i<5;i++){
setTimeout(()=>{
console.log(i);
},1000*i);
}
Soln: print 5 five times, Explain properly
2.
console.log(“start”);
let check=true;
setTimeout(()=>check=false,2000);
while(check){
console.log(“hi”);
}
console.log(“end”);
3.
function a(){
var x=10;
return ()=>console.log(x);
}
a();
// explain the answer
Form closure with outer function a and with variable x;
Ex1.
{
var x=10
let y=112;
const z=23;
}
We can access var variable but let and const are not
console.log(x); //ok
console.log(y); //not defined syntax error
Ex2
var a=10;
var a=12;//ok
let b=12
let b=23; // notOk already declare syntax error
Same with const
Ex3
var a=12
a=23//ok
let b=12
b=23 //ok
const x=23;
x=32;// not ok we can't modified after initialization
arr.forEach((i) => {
if (Array.isArray(i)) {
result.push(...flatten(i))
} else {
result.push(i)
}
})
return result
}
2nd method easily understandable
function flatten(nested){
var ans=[];
for(var i=0;i<nested.length;i++){
if(Array.isArray(nested[i])){
var x=flatten(nested[i]);
x.forEach(element => {
ans.push(element);
});
}else{
ans.push(nested[i]);
}
}
return ans;
}
const nested = [1, 2, 3, [4, 5, [6, 7, [1, 2]], 8, 9]]
flatten(nested)
Op- [1, 2, 3, 4, 5, 6, 7, 1, 2, 8, 9]
Qus. Polyfill for Bind Method
const obj={
name:"Afroz",
age:23
}
function print(clg,loc){
console.log(this.name,this.age,clg,loc);
}
Function.prototype.myBind= function(...args){
let obje=args[0];
let args1=args.slice(1);
return function(...args2){
print.apply(obje,[...args1,...args2])
}
}
print.bind(obj,"IIIT")("Gwalior");
print.myBind(obj,"IIIT")("Gwalior");
let count=0
function getData(){
console.log("Fetching Data....",count++);
}
function doSomeMagic(getDatafn,delay){
let timer;
return function(){
let context=this;
let args=arguments;
clearTimeout(timer);
timer=setTimeout(()=>{
getDatafn.apply(context,args);
},delay);
}
}
const debounce=doSomeMagic(getData,500);
The button is attached to an event listener that calls the debounce function. The debounce function is
provided with 2 parameters – a function and a Number. Declared a variable debounceTimer, which as the
name suggests, is used to actually call the function, received as a parameter after an interval of ‘delay’
milliseconds. If the debounce button is clicked only once, the debounce function gets called after the delay.
However, if the debounce button is clicked once, and again clicked prior to the end of the delay, the initial
delay is cleared and a fresh delay timer is started. The clearTimeout function is being used to achieve it.
The general idea for debouncing is:
1. Start with 0 timeout
2. If the debounced function is called again, reset the timer to the specified delay
3. In case of timeout, call the debounced function Thus every call to a debounce function, resets the timer
and delays the call.
Qus: Throttling in Javascript
Throttling or sometimes is also called throttle function is a practice used in websites. Throttling is used to call
a function after every millisecond or a particular interval of time; only the first click is executed immediately.
//window.addEventListener(‘resize’,betterExpensive());
let count=0;
function expensive(){
console.log("I'm expensive...",count++);
}
function throttle(fn,limit){
let flag=true; // clouser with flag
return function(){
let context=this;
let args=arguments;
if(flag){ // this if statement called after limit when flag become true
fn.apply(context,args);
flag=false;
setTimeout(()=>{
flag=true;
},limit);
}
}
}
const betterExpensive=throttle(expensive,400);
betterExpensive();
Debouncing:
Baby: Mom give me a piece of chocolate cake, . . .Mom give me a piece of chocolate cake
... mom give me ...
Mom: No, You will get a piece of cake only after 1 hour from LAST time you asked for one.
Both are used to optimise the performance of the web app by limiting the rate of function
calls.
Which one is better? It depends on the scenario where we want to use it.
Q: What is CORS(Cross-origin resource sharing)?
Ans: Cors is a mechanism that uses additional HTTP headers to tell the browser whether a
specific web app can share resources with another web app or subdomain.
The Most important thing is that both web apps should have different origins.
const employee={
name:"James",
dept:{
id:21,
name:"afroz",
}
address:{
street:"morena link road",
city:"Gwalior"
}
}
const {dept:{address:{street}}}=employee;
// by doing that we can extract the street from the employee object.
we can also assign a default value to which not present in that object.
default value
const {name, dept, message= `${name} ${dept}`} = employee;
alias
const {dept:departement}= employee;
dynamic
const {[key]:returnValue}=employee;
return returnValue;
function
function getDetails({name,age}){
console.log(`${name} ${age}`}
};
getDetails(employee);
loops
for(let {name,dept} of employee){
console.log(`${name} ${dept});
}
8. Promises
9. Classes
class student{
constructor(newName){
this.name=newName; //called whenever new object is created
}
getName(){
console.log(this.name);
}
}
Q1.
var name="Afroz";
(function(){
console.log(name); // give reference error(name is block scope here)
let name="afroz";
console.log(name);
})()
Note: here scope chain won’t work bcs name variable present in x’s
lexical environment so if name is not inside of x then lexical chain
will work.
Giving error bcs we are accessing the let variable before its
declaration.
Q2.
var name=”afroz”;
function x(){
if(name==”afroz”){
var name=”Placement”;
console.log(name);
}
console.log(name); // undefined;
}
x();
Session 2
HANDLE EVERY SINGLE ERROR
Episode 1
There are 2 Parts of Callback:
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
a.) Callback Hell: When a callback function is kept inside another function, which in turn is
kept inside another function. (in short, a lot of nested callbacks). This causes a pyramid of
doom structure causing our code to grow horizontally, making it tough to manage our code.
b.) Inversion of control: This happens when the control of the program is no longer in our
hands. In nested functions, one API calls the callback function received but we don't know
how the code is written inside that API and how it will affect our code. Will our function be
called or not? What if called twice? We have given control of our code to other codes.
Episode 2
promise().then(data=>data.json()).then(()=>).catch(err=>console.log(err));
Episode 3
Uncaught Error: When a promise is rejected, it immediately looks for a rejection handler. If it finds
one, that means the Promise rejection is handled and will call the function with the error, but
otherwise, it throws a general unhandled promise rejection warning error “uncaught (in promise) …”.
Episode 4
What is async?
async fn always returns a promise by wrapping it to promise. and if we pass Promise then it won’t
wrap it to promise.
async and await combo is
HR Interview Question/Answer
Q1. Tell me about yourself.
Ans: I’m Afroz Quraishi. I did post graduation in Information Technology from IIIT Gwalior. Currently
doing a technical internship in Rakuten India. So far I have worked with js and its related libraries
and frameworks such as reactjs, …. I have also done 3 Internships in Frontend development. I also
solve data structure and algorithm-related problems on various online coding platforms such as
leetcode, codechef, and hackerrank.
% My hobbies are teaching, playing carrom and badminton.
That's it from my side. Thank you!