Preparing for Frontend Interview

Preparing for Frontend Interview

Debounce

It is used to improved performance . Sometime a function is being called continuously . The Debounce technique allow us to "group" multiple sequential calls in a single one. For eg Suppose we have blog editor.We are editing the content of the blog.We have a autosave feature for most of the editor. When ever we add or delete a character, an API call is being made to save the content. Therefore whenever we edit a character it makes a call. This overburden our api server. To solve this problem we can use debounce. If we have multiple events triggered. We can either act on the leading event or the last event. So here in case we editor we will act on the last event ie when we stop writing the characters for particular time. Code:

const debounce = (cb,delay)=>{
    let timer ;
    return (...args)=>{
         clearTimeout(timer);
         timer = setTimeout(()=>{
                cb(...args);
         },delay)
    }
}

Usage:

const [text,setText]=useState("")

<input  type="text" value={text} onClick={this.handleInput}/>

const handleInput=(e)=>{
    setText(e.target.value);
    debounce(saveText,3000)
}

Explanation: debounce returns a function. We have scoped timer variable . The function that we are returning has two things clearTimeout and setTimeout.When debounce is triggered we are removing the old Timeout function and creating a new. Our callback will be triggered after delay time. If debounce is triggered again before delay time trigger the callback, then we are removing the Timeout. So callback will be triggered if no debounce event triggered between delay time.

if you want to dig more deeper learn about leading and trailing

Throttle

By using throttle, we don't allow to our function to execute more than once every X milliseconds.

The main difference between this and debouncing is that throttle guarantees the execution of the function regularly, at least every X milliseconds.

Eg: Suppose we have infinite scroll on our page. When we reach the bottom of the page we have an ajax call to fetch the data. So on scroll event we have are logic which checks whether we have reached the bottom of the page or not.If we don't have throttle on our callback function it will trigger on every scroll event.

window.addEventListener('scroll',()=>{
//Logic for checking the end of page and fetching the data
})

Implementation:

const throttle = (cb,delay)=>{
    let timer=null;
    return (...args)=>{
     if(timer===null)
         {
              cb(...args);
              timer = setTimeout(()=>{timer=null},delay);
         }
    }
}

Usage:

window.addEventListener('scroll',throttle(()=>{
//Logic for checking the end of page and fetching the data
},3000))

Explanation: throttle returns a function. We have scoped timer variable .Our return function checks if there was any setTimeout function already running.If its running the timer variable is not null else its null.On first call timer is null and it triggers the cb function and create a setTimeout function which makes timer variable not null. When throttle is triggered again , timer is not null and hence we wait till setTimeout function completes and makes timer variable null.

if you want to dig more deeper learn about leading and trailing

In summary:

  • debounce: Grouping a sudden burst of events (like keystrokes) into a single one.

  • throttle: Guaranteeing a constant flow of executions every X milliseconds. Like checking every 200ms your scroll position to trigger a CSS animation.

Polyfill( map,reduce,filter)

A polyfill is a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it.

  • map:
Array.prototype.map = (cb)=>{
    let arrayList =[];
    for(let i=0;i<this.length;i++){
     arrayList.push(cb(this[i],i))
    }
    return arrayList;
}
  • reduce

Array.prototype.reduce1= function(callback, initialValue) {
  var accumulator = initialValue === undefined ? undefined : initialValue

  for (var i = 0; i < this.length; i++) {
    if (accumulator !== undefined) {
      accumulator = callback.call(undefined, accumulator, this[i], i, this)
    } else {
      accumulator = this[i]
    }
  }
  return accumulator
} // our polyfill for reduce

let arr = [1,2,3]

let sumOfArr = arr.reduce(function(a, b) {
  return a + b
}, 0) // Initial Value is 0

console.log(sumOfArr)
// 6
  • filter:
Array.prototype.filter = function(callback, context) {
  arr = []
  for (var i = 0; i < this.length; i++) {
    if (callback.call(context, this[i], i, this)) {
      arr.push(this[i])
    }
  }
  return arr
}
let ret = arr.filter(function(data) {
  return data > 2 // providing the context here
})
console.log(ret)

For...in and for...of

let a = [10,9,8]
let b = {one:1, two:2}
for(let i in a) {console.log(i)} => 0 1 2 //iterates over index(key)
for(let i of a) {console.log(i)} => 10 9 8 // iterates over value

for(let i in b) {console.log(i)} => one two // iterates over key
for(let i of b) {console.log(i)} // will not work

bind

bind(thisArg, arg1, arg2, /* …, */ argN)
//thisArg : The value to be passed as the this
//Arguments to prepend to arguments provided to the bound function when invoking func.

A bound function can be further bound by calling boundFn.bind(thisArg, /* more args */), which creates another bound function boundFn2. The newly bound thisArg value is ignored, because the target function of boundFn2, which is boundFn, already has a bound this. When boundFn2 is called, it would call boundFn, which in turn calls fn. The arguments that fn ultimately receives are, in order: the arguments bound by boundFn, arguments bound by boundFn2, and the arguments received by boundFn2.

function log(...args) {
  console.log(this, ...args);
}
const boundLog = log.bind("this value", 1, 2);
const boundLog2 = boundLog.bind("new this value", 3, 4);
boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6

Difference between proto and prototype

__proto__ is available on object and .prototype is available on Class, both refer to same Object which the constructor used to instantiate or create a new object, .prototype is set as the prototype of the new object.

let a = '1222'
a.__proto__.firstItem = function (){return this[0]}
a.firstItem() -> 1
let b ='amit'
b.firstITem -> a // this also work as a.__proto__ === String.prototype

.then Chaining

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

The idea is that the result is passed through the chain of .then handlers.

Here the flow is:

  1. The initial promise resolves in 1 second (*),

  2. Then the .then handler is called (**), which in turn creates a new promise (resolved with 2value).

  3. The next then (***) gets the result of the previous one, processes it (doubles) and passes it to the next handler.

  4. …and so on.

A handler, used in .then(handler) may create and return a promise.

In that case further handlers wait until it settles, and then get its result.

For instance:

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000);

}).then(function(result) {

  alert(result); // 1

  return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) { // (**)

  alert(result); // 2

  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) {

  alert(result); // 4

});

Here the first .then shows 1 and returns new Promise(…) in the line (*). After one second it resolves, and the result (the argument of resolve, here it’s result * 2) is passed on to the handler of the second .then. That handler is in the line (**), it shows 2 and does the same thing.

So the output is the same as in the previous example: 1 → 2 → 4, but now with 1 second delay between alert calls.

Returning promises allows us to build chains of asynchronous actions.

Pure Functions

The definition of a pure function is:

  1. The function always returns the same result if the same arguments are passed in. It does not depend on any state, or data, change during a program’s execution. It must only depend on its input arguments.

  2. The function does not produce any observable side effects such as network requests, input and output devices, or data mutation.

What are Side effects?

Function should not interact with the outside world.That could be anything from changing a variable that exists outside the function, to calling another method from within a function,accessing a variable not in function internal scope.

Note: If a pure function calls a pure function this isn’t a side effect and the calling function is still pure. -pure eg

let P=10,R=20,T=2;
const simpleInterest = function(P,R,T){
    return (P*R*T)/100
}
  • impure eg
let P=10,R=20,T=2;
const simpleInterest = function(){
    return (P*R*T)/100//here we are directly accessing the outside variable
}

Webpack

Webpack takes modules and dependencies to generate static assets.

Concept:

  • Entry: An entry point indicates which module webpack should use to begin building out its internal dependency graph. After entering the entry point, webpack will figure out which other modules and libraries that entry point depends on (directly and indirectly).

  • Output: The output property tells webpack where to store the bundled files ,it defaults to ./dist. Basically, the entire app structure will get compiled into the folder that you specify in the output path.

  • Loaders: Loaders enable webpack to process more than just JavaScript files . They give you the ability to leverage webpack’s bundling capabilities for all kinds of files by converting them to valid modules that webpack can process. eg sass-loader(Loads a Sass/SCSS file and compiles it to CSS),file-loader(for images and files)

  • Plugins: Plugins range from bundle optimization and minification all the way to defining environment-like variables. The plugin interface is extremely powerful and can be used to tackle a wide variety of tasks.

TIP: All React component names must start with a capital letter. If you start a component name with a lowercase letter, it will be treated like a built-in element like a <div> or a <span> . This is because of the way JSX works. In JSX, rendering a component that begins with a lowercase letter compiles down to React.

Few more topics

  1. Singleton class.eg Auth.getInstance()

  2. class inheritance without using class and extends

    1. Difference between defining member function using .prototype.func and this.func in constructor.

    2. Prototype chaining using Object.create