This repository contains interview questions for javascript
Practice questions for Javascript Interview:
Limits the exceution of a function call , and waits for a certain amount of time before calling it again.
For example : Search Box - in application - here the number of calls to the api are limited by applying a delay so we can reduce the overhead on the server, and limit the number of api calls to the server.
For below code to work use npm install lodash
Debouncing.jsx
import { useState } from "react";
import debounce from "lodash/debounce";
function Debouncing() {
const [pressedCount, setPressedCount] = useState(0);
const [triggerCount, setTriggerCount] = useState(0);
const onButtonClick = () => {
setPressedCount(pressedCount + 1);
debouncedCount();
};
const debouncedCount = debounce(() => {
setTriggerCount(triggerCount + 1);
}, 800);
return (
<>
<button onClick={onButtonClick}>Increment</button>
<p>
Button Pressed <span>{pressedCount}</span> times
</p>
<p>
Triggered <span>{triggerCount}</span> times
</p>
</>
);
}
export default Debouncing;
App.jsx
import "./App.css";
import Debouncing from "./components/Debouncing";
function App() {
return (
<>
<Debouncing />
</>
);
}
export default App;
Debouncing Polyfill
const myDebounce = (cb, wait) => {
let timer;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
cb(...args);
}, wait);
};
};
const debouncedCount = myDebounce(() => {
setTriggerCount(triggerCount + 1);
}, 800);
Throlling is a mechanism to limit the execution of a even handler function when events are triggered continuously due to user action- like click of a button
For example : Say on scroll we are triggering an event which calls an Api( so it is an expensive function) - we can limit the calls of the event by using throllling function.
Throttling Pollyfill
const myThrottle = (cb, delay) => {
let last = 0;
return (...args) => {
let now = new Date().getTime(); // we get current time
if (now - last < delay) return; // if the current time - last time we triggeres < delay we return
last = now; // if it is greater set the last to current time , triggered time
return cb(...args); // execute the callback we return
};
};
const throttledCount = myThrottle(() => {
setTriggerCount(triggerCount + 1);
}, 1000);
- Currying is a concept in javascript wherein there is function that takes in one input at a time and returns a new function expecting the next input.
- It is a convertion of funtions from callable as f(a,b) -> to f(a)(b)
-Basically Currying doesn’t call a function. It just transforms a function. They are constructed by chaining closures by immediately returning their inner functions simultaneously.
- Also see Folder for Examples
function f(a, b) {
console.log(a, b);
}
function f(a) {
return function (b) {
console.log(a, b);
};
}
f(4)(5);
- To avoid passing the same variable again & again
- to create higher order func
- to make your function pure and less prone to error.
- It is a checking method that checks if you have all the things before you proceed.
- It divides one function into multiple functions so that one handles one set of responsibility.
Lexical scope means a variable defined outside a function can be accessible within a function but opposite is not true, variables declared inside function caanot be accessed outside
- A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
- In other words, a closure gives you access to an outer function's scope from an inner function.
- In JavaScript, closures are created every time a function is created, at function creation time.
For example :
function makeFunc() {
const name = "Sushmita";
function displayName() {
console.log(name);
}
return displayName;
}
//const myFunc = makeFunc();
//myFunc();
makeFunc()();
In the above example we are able to access the name which is defined in outer scope even after the function display name has been returned.
- Closures make it possible for JS to have private variables.
let count = 0(function printCount() {
if (count === 0) {
let count = 1; // shadowing
console.log(count); // 1
}
console.log(count); // 0
})();
Output 1 0
var addSix = createBase(6);
addSix(10); // 16
addSix(21); //27
function createBase(x) {
return function (y) {
console.log(x + y);
};
}
function find(index) {
let a = [];
for (let i = 0; i < 1000000; i++) {
a[i] = i * i;
}
console.log(a[index]);
}
console.time("6");
find(6);
console.timeEnd("6");
console.time("12");
find(6);
console.timeEnd("12");
The above function is time consuming- We can use closures to optimize the time taken by them - The issue is that the looping is happening every time we are calling the function- as there is new reference to the a variable created everytime.
function find() {
let a = [];
for (let i = 0; i < 100000000; i++) {
a[i] = i * i;
}
return function (index) {
console.log(a[index]);
};
}
const closure = find();
console.time("6");
closure(6);
console.timeEnd("6");
console.time("50");
closure(50);
console.timeEnd("50");
We are not doing the heaving operation - hence the time reduced is very very high as can be seen below - it is now in ms
for (var i = 0; i < 3; i++) {
function inner(i) {
setTimeout(() => {
console.log(i);
}, i * 1000);
}
inner(i);
}
*The above code will create a new refernce to i each time the function inner is called.
function counter() {
var _counter = 0; // Here counter is a private variable
function add(increment) {
_counter += increment;
}
function retrieve() {
return "Counter = " + _counter;
}
return {
add,
retrieve,
};
}
const c = counter(); // created a private counter
c.add(5);
c.add(10);
console.log(c.retrieve());
$ node script.js
Counter = 15
var Module = (function () {
function privateMethod() {
// do something which you do not want the user to access directly.
// suppose we have an API call which we do not want to access to the iser, we can keep it here
}
return {
publicMethod: function () {
// can call the private Method
},
};
})();
Module.publicMethod(); // runs successfully
Module.privateMethod(); // give us an error
let view;
function likeTheVideo() {
view = "Sushmita Ghosh";
console.log("Subscribe to " + view);
}
likeTheVideo();
*We can use closures to do the same - we can create a local variable "called" which will keep an count of how many time the function has run.
let view;
function likeTheVideo() {
let called = 0;
return function () {
if (called > 0) {
console.log("Already Subcribed to Sushmita's Channel");
} else {
view = "Sushmita Ghosh";
console.log("Subscribe to " + view);
called++;
}
};
}
let isSubscribed = likeTheVideo();
isSubscribed();
isSubscribed();
$ node script.js
Subscribe to Sushmita Ghosh
Already Subcribed to Sushmita's Channel
*Everytime we call the isSubscribed function we are referencing the same called variable.
- We can write a more generic function for the above function.
-
Whenever we create a function within another function, then the inner function is the closure. This closure is usually returned so that it can use the outer function's variable at a later time. ( Global, outer & local scopes)
-
A scope in js defined what variable you have access to - Local & global ( 2 kinds)
- Depends on the context that we are refering to - based on which it's value changes.
- For normal function this is taken - from the context to which it is referening to.
- For arrow functions this refers to the outer normal function
- In object method, this refers to object
- alone, this refers to the global object
- In a function, in strict mode, this is undefined
- Array functions - map, filter, reduce - can access this.
For example :
let user = {
name: "Sews",
age: 24,
getDetails() {
console.log(this.name + " is " + this.age + " years old ");
},
};
user.getDetails();
$ node this.js
Sews is 24 years old
let user = {
name: "Sews",
age: 24,
childObj: {
newName: "Suzuna",
getDetails() {
console.log(this.newName + " Nd " + this.name);
},
},
};
user.childObj.getDetails();
- In the aboove funtion, this refers to "childObj response
$ node this.js
Suzuna is undefined years old
- Replacing with Arrow Functions:
- line by line
- js is a single-threaded language - it can't execute two tasks (like setTimeout) in parallel - what it does is it first runs all of our synchronous code and then setTimeout code will be executed
- setTimeout is part of API - so even if the time given to setTimeout is 0ms, the order remains the same.
- The above concept is of the event loop
- Since JS is single-threaded, for any task where we need to wait for some time (asynchronous task) we do need callbacks
- callbacks are functions which can be executed at a later point of time.
setTimeout(() => {
console.log("Hello World");
}, 5000);
-
In a real-world application we might run into situations where we have a dependency of tasks on one another - say for example APIs trying to create order, payment, order summary, etc all these different APIs are dependent on one another but if we write the code - it gets ugly and difficult to understand. - which is called callback hell.
-
Callback nested within one another is callback hell, resulting in code growing horizontally.
-
This kind of structure is also known as the pyramid of doom.
Inversion of control is losing control over our code when using callbacks. Two issues while using callbacks
1 - Callback hell When a function is passed as an argument to another function, it becomes a callback function. This process continues and there are many callbacks inside another's Callback function. This grows the code horizontally instead of vertically. That mechanism is known as callback hell.
2 - Inversion of control The callback function is passed to another callback, this way we lose control of our code. We don't know what is happening behind the scenes and the program becomes challenging to maintain. That process is called inversion of control.
-
Before promise we used to depend on callback functions which would result in 1.) Callback Hell (Pyramid of doom) | 2.) Inversion of control
-
Inversion of control is overcome by using promise. 2.1.1) A promise is an object that represents the eventual completion/failure of an asynchronous operation. 2.1.2) They are just syntactical sugar to the ugly code that callbacks bring into the picture. But under the hood all the working remains same. 2.2) A promise has 3 states: pending | fulfilled | rejected. 2.3) As soon as the promise is fulfilled/rejected => It updates the empty object which is assigned undefined in the pending state. 2.4) A promise resolves only once and it is immutable. 2.5) Using .then() we can control when we call the cb(callback) function. 2.6) A promise object has two things - PromiseState(pending/fulfilled) & PromiseData(data) 2.7) fetch returns a promise
-
To avoid callback hell (Pyramid of doom) => We use promise chaining. This way our code expands vertically instead of horizontally. Chaining is done using '.then()'
-
A very common mistake that developers do is not returning a value during chaining of promises. Always remember to return a value. This returned value will be used by the next .then()
-
A Promise is an object that represents the eventual completion or failure of an asynchronous operations.
-
Importance of promise is that we do not loose the control of the program, a promise object is immutable and can be send anywhere without worrying about changes, also it resolves only once either to success or failure.
-
How we initialise promise?
let p = new Promise(function (resolve) {}); console.log(p);
Promise { <pending> }
function myAsyncFn() {
let p = new Promise(function (resolve) {
resolve("hi there");
});
return p;
}
const value = myAsyncFn();
value.then(function (data) {
console.log(value);
console.log(data);
});
Promise { 'hi there' }
hi there
- A html file as soon as it gets the script tag it starts to download and execute the js file. This will interrupt the html parsing causing an initial delay in html rendering and eventually time to load.
Async
tag will start to download the js file parallely to html rendering but it blocks rendering after it is downloaded so blocking time is just the time it requires for executing and not downloading.
- Make parÂalÂlel requests to fetch the files.
- ConÂtinue parsÂing the docÂuÂment as if it was never interrupted.
- ExeÂcute the indiÂvidÂual scripts the moment the files are downloaded.
Defer
tag will parallely download the js file and will execute it once the html rendering is done. So no delay in html rendering.
- Make parÂalÂlel requests to fetch the indiÂvidÂual files.
- ConÂtinue parsÂing the docÂuÂment as if it was never interrupted.
- FinÂish parsÂing the docÂuÂment even if the script files have downloaded.
- ExeÂcute each script in the order they were encounÂtered in the document.
Defer attribute
:It will guarantee all the scripts will execute after the HTML parsing.The defer attribute is useful when the script is used for DOM manipulations.
Async attribute
: It does not guarantee that all the scripts will execute after the HTML parsing. The async attribute is useful when the script is not used for DOM manipulation (google ads link)
The Async tag will always be independent so adding to the head also won't make it much more efficient.
Conventionally all tags are kept in body but keeping the script tag in head with defer will help a lot as during rendering, the js file will be downloaded parallelly and on completion of rendering js execution will start immediately ( we dont have to wait for js download again when rendering ends). This is kind of prefetching assets.
Even though JS is single-threaded ( just like the human brain). JS can do things parallelly by delegating the tasks & also context switch between tasks if need be( the net time to do both things would still be the same).
- JS does this by using async functions.
- They are functions that can be executed parallelly with other sets of tasks probably after a certain amount of time. - eg setTimeout, fs.readFile( reads file from your system), fetch ( to fetch data from API endpoint)
function findSum(n) {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
}
function findSumTill100(n) {
let sumTill100 = findSum(100);
console.log(sumTill100);
}
setTimeout(findSumTill100, 1000);
console.log("hello world");
OUTPUT
hello world
4950
readFile
const fs = require("fs");
fs.readFile("a.txt", "utf-8", (err, data) => {
console.log(data);
});
console.log("Will this be printed first?");
OUTPUT
Will this be printed first?
Hi There ! Hello from a.txt
- 4 major things contribute to the async activity of JS: VISUALISE
- Call Stack - responsible for running each line of code
- Web Apis - provided by browser - have added functionality - not necessarily part of Javascript - In case of setTImeout - webapis help to execute the time frame - ie (number of miliseconds)
- Callback Queue - once the wait is completed , it is pushed to the callback queue and it waits.
- Event Loop - checks when the main thread is idle and it is pushed on to the callback stack to execute. It's job is to check if there is something in the callback queue - if yes puts to the call stack.
For synch code - without callbacks also we can suffice , but with asyn functions we need them
Async Await is just syntactical sugar - also uses Promises and callbacks behind the hood.
function myAsyncFunction() {
let p = new Promise(function (resolve) {
// do some async logic here
setTimeout(function () {
resolve("hi there!");
}, 3000);
});
return p;
}
async function main() {
const p = myAsyncFunction();
console.log(p);
const value = await myAsyncFunction();
console.log(value);
}
main();
console.log("main");
Promise { <pending> }
main
hi there!
- The .then part is how we consume promises, we can create our own promise as well.
- promises can be created by calling the
Promise
constructor - the
Promise
constructor takes in two parameters - resolve & reject - as the name suggests we can resolve the promise when a condition is met and send the value - which can be accessible when we do a .then.
- we can reject a promise and pass in a error to it.
- Suppose we have a promise which creates an order ( which will have return an orderId) if it it gets resolved correctly or will be rejected if the cart does not meet the validations.
// Consuming the promise - generally we worry about this step.
const cart = ["shoes", "pants", "kurta"];
const promise = createOrder(cart); //orderID
promise.then(function (orderId) {
console.log(orderId);
// proceedToPayment(orderId);
});
// Creating the promise
function createOrder(cart) {
const pr = new Promise(function (resolve, reject) {
// we will check if our cart is validated
// if not we will reject the promise
if (!validateCart(cart)) {
const err = new Error("Cart is not valid");
reject(err);
}
// once the cart is validated we can create an order and resolve with orderID
const orderId = "12345";
if (orderId) {
resolve(orderId);
}
});
return pr;
}
function validateCart(cart) {
if (cart) {
return true; // here we can check if cart is empty
} else return false;
}
- Till the time our promise is resolved - it will be in pending state
- We can handle error using the .catch method , and we can pass a failure callback to it as well.
promise
.then(function (orderId) {
console.log(orderId);
})
.catch(function (err) {
console.log(err);
});
- As the name suggests we can keep chaining our promises with the .then callback.
- Each time we chain we can access the value of resolve of the previous promise passed onto the next.
- One common mistake is - whatever we need to pass onto the chain we need to return it. - we can either return a value or the promise itself - which will be resolved.
const cart = ["apple", "banana"];
const promise = createOrder(cart); //orderID
promise
.then(function (orderId) {
console.log(orderId);
return orderId;
})
.then(function (orderId) {
return proceedToPayment(orderId);
})
.then(function (paymentInfo) {
console.log(paymentInfo);
})
.catch(function (err) {
console.log(err.message);
});
function proceedToPayment(orderId) {
return new Promise(function (resolve, reject) {
resolve("Payment done successfully");
});
}
- Map is used to iterate over the array and apply the callback function to each one and return a new array.
const nums = [1, 2, 3, 4];
const multiplyThree = nums.map((num, i, arr) => {
return num * 3;
});
- Applies the function as a conditional to the array - based on true from the condition the element is pushed onto the array.
const nums = [1, 2, 3, 4];
const moreThanTwo = nums.filter((num, i, arr) => {
return num > 2;
});
The reduce method reduces the array of values to just one value. It will execute the callback on each element of the array.
const nums = [1, 2, 3, 4];
const moreThanTwo = nums.reduce((acc, curr, i, arr) => {
return acc + curr;
}, 0);
- Also see FOLDER
-
Es6 or ECMAScript 2015 is the 6th and major edition of the ECMAScript language specification standard. I defined the standarad for the implementation of JavaScript and it has become much more popular than the previous edition ES%
-
Es6 comes with significant changes to Javascript Language. It brought several new features like let and const keywords, spread and rest operators, template literals, classes , modules and many other enhancements to male JavaScript programming easier and more fun
- Let and const keywords
- Arrow Functions
- Multi Line Strings
- Default Parameters
- Template Literals
- Destructing Assignment
- Enhanced Object Literals
- Promises
- Classes
- Modules
The delete operator removes a property from an object. If the property's value is an object and there are no more refereances to the object, the object held by that property is eventually released automatically. ### Code :
* Essentially all the avialable common methods for clonong an object or cloning aan array all create a shallow copy, they are work on refernces when it comes to nested arrays or nested objects. So if you have a nested obj / array , they will not work. Shallow copy is changes in instance of an object modifies the original object ( so if a = b change in b will bring change in a).
- That's no good! To resolve the issue we need to write our own function of deep clone
const deepClone = (obj) => {
if (typeof obj !== "object" || obj === null) return obj;
// create an array / object to hold the values
const newObject = Array.isArray(obj) ? [] : {};
// loop through the object
for (let key in obj) {
const value = obj[key];
// recursively (deep) copy for nested objects, including arrays
newObject[key] = deepClone(value);
}
return newObject;
};
const oldScoreArray = [1, 2, 3, 4, 5];
const newScoreArray = deepClone(oldScoreArray);
newScoreArray[0] = 0;
console.log(newScoreArray);
console.log(oldScoreArray);
OUTPUT:
[ 0, 2, 3, 4, 5 ]
[ 1, 2, 3, 4, 5 ]
- Go to TABLE OF CONTENTS