10 JavaScript & React questions you might encounter in your next front-end interview.

10 JavaScript & React questions you might encounter in your next front-end interview.

Table of contents

No heading

No headings in the article.

  1. Closures

    A closure in JavaScript is a combination of a function bundled with its lexical environment.

    Let’s understand with an example.

    The above code snippet has a function named greet and a variable name is declared and initialized with a value Priyanka. welcome is another function defined inside greet & it just logs the value of the variable name. When the function welcome is run, it first looks for the variable name inside its local environment and when it fails to locate the variable, it then looks in its parent’s scope. Since the variable name is present in the parent scope, welcome prints the value associated with it.

    In the above case, the function welcome is being returned to greet. Notice that welcome is not invoked inside greet, but is just returned. Now when greet is invoked, it contains the function definition of welcome and not the value. This returned function definition is then assigned to another variable greetings. If you try to log the value associated with greetings, you might only see the function definition of welcome, but in reality, it also contains the reference to its lexical environment, which in our case, is the value linked to the variable name. So when the function greetings is called, it doesn't throw any reference error but prints Priyanka. If not for JavaScript, you could expect a reference error thrown at you stating that the variable name is not defined.

    Now coming back to the definition of closure, it’s a combination of a function(welcome()) bundled with its lexical environment(variable name).

  2. Scope

    Scope references the current execution context and makes the variables and expressions available for use.

    There are three types of scopes in JavaScript,

    Function scope

    When the variables are declared inside a function, their use is limited only to that function segment and cannot be accessed anywhere outside the function.

    Block Scope

    When variables are declared within a particular block, they are accessible only within that block and not anywhere outside. But var is an exception here. Blocks only scope let and const declarations and var declarations are accessible both inside & outside the block.

    Global Scope

    When a variable or an expression is declared outside of any block or a function segment, they are referred to as Global variables and have global scope. These variables are accessible anywhere inside a program and have no restrictions.

  3. call(), apply() and bind()

    Before going on to the call, apply and bind methods, we first need to understand this keyword in JavaScript.

    this always refers to an object and is used to reference the object attributes within the same object in which it’s being used.

    In the global context, this refers to the window object and when used in strict mode, it refers to undefined.

    call()

    call() is a special method in JavaScript. It's used to invoke methods declared in another object using the parent object as an argument.

    From the above code snippet, we can find two objects, person & greet. Inside greet object, we have a method greetings that logs the person’s details. Notice that the person object doesn’t contain the method greetings. Now if we come across a scenario where in we are expected to use a method from a different object(greet) to perform the operations using the parent object(person), we use the call() method. In case we have additional data to be sent as arguments, we can do so by sending comma-separated values.

    apply()

    apply() method is similar to call(), but instead of passing arguments as comma-separated-values, we send them in an array.

    bind()

    bind() method is used for function borrowing. The bind() method creates a new function in which this refers to the object being sent as an argument.

  4. Regular function vs Arrow function

    Regular functions are always named functions. They are defined using the function keyword.

    Arrow functions on the other hand are anonymous functions and do not include the keyword function. Arrow functions are similar to any other variable in JavaScript. They do not possess any name and instead, they are assigned to a variable. These kinds of functions can be used anywhere in the program similar to how we use the other variables.

    Regular functions always bind this keyword and arrow functions don’t. From the above example of call(), apply(), and bind(), instead of using a normal function, had we used an arrow function as a method, we would be thrown an error in the console as arrow functions don’t bind this.

  5. Rest & Spread operators

    Rest and Spread operators in JavaScript are used to contract and expand the user-specified values into an array and are always prefixed with three dots(). The differentiation between the two lies in their usage. Let’s understand them using examples,

    Rest

    The Rest operator is always added at the end of the parameter list as shown. In the above code snippet, we have a function cars which just logs a few elements from the argument list. When the function is invoked, a & b match “Audi” & “Bentley” respectively, and all the other remaining elements are put into the rest operator. So when we log the data using console.log(rest), we don’t get to see “Audi” & “Bentley”.

    Spread

    Spread is similar to rest but is always added in the beginning of the parameter list. a and b are two different arrays as shown and when used them by prefixing three dots, the values in the array spread and thus get assigned to a new array c.

  6. Currying

    Currying in JavaScript is a way of invoking a function with just a single argument at a time.

    A normal function, function(a,b,c) can be transformed into the function(a)(b)(c) using currying.

    During the initial execution, function(a) is called and it returns another function which then takes b as an argument, and this again returns another function that takes the argument c. Now it’s the responsibility of function c to return valuable information.

    Let’s understand it better using an example,

    You may wonder how is something that is inside a function block being called outside the scope. This is where closures come into the picture. Closures always bind the function and its lexical environment together. So when the first returning function is called using the argument b, it inherently has access to its parent variables, which in our case is a. Hence, instead of throwing a reference error at us, it calculates the sum.

  7. Memoization

    Whenever we build a web application or software, performance optimization holds the top priority. Memoization is one such technique often used in optimizing web applications. In React applications, there might be situations where rendering a complex component might lead to performance issues. Using the Memoization technique, we could boost the application’s performance.

    Memoization is used to cache the values returned by heavy function calls. When we have functions returning the same values using the same arguments, there is no use in calling them each time we render the application. Instead, we can cache the returned values and store them in memory so we could use them when we need them. This technique reduces the time taken by functions to run and hence improves the performance.

    We have 3 ways in which we can implement memorization in a React application.

    Pure components

    Pure components are used in class-based React components and they internally compare the state and props and allow rendering only if there is a change.

    React.memo()

    React.memo() is similar to the Pure component but works only in functional components.

    useMemo()

    useMemo() is a hook provided by React and does the same job as React.memo() or Pure components.

  8. Reconciliation

    Reconciliation is a method used to improve the performance of a React application. It's a process through which the browser updates the DOM. DOM is created by parsing the code and it represents the page in terms of nodes and objects. Whenever a state or a prop changes, React re-renders the component. Re-rendering essentially means the browser destroys the previous DOM tree and builds a new one. Even if a part of the component has suffered a change, the entire DOM is replaced. This process reduces the application's performance and, hence, the user experience.

    To cater to this issue, React offers us a virtual DOM. Virtual DOM is an exact copy of the original DOM but is used in comparing the nodes and this happens using a diffing algorithm. When a state or a prop updates, React needs to check if it's necessary to update the DOM. In the initial rendering of components, React has already created a tree and when something updates in the component, It’s doesn’t directly reflect on the browser. Instead, the changes are first noticed in virtual DOM, and then React compares the DOM trees. In case there is a change, React updates only that part of the DOM tree and leaves the rest as they are, thus reducing the time of re-rendering the components. If root nodes themselves get changed during the course, then React completely destroys the tree and creates a new one.

    In the case of lists and array elements, React keeps track of the key prop associated with the elements. If a new element gets pushed into an array or a new element gets added to a list, React goes through the keys and only updates that part in which the modification is seen and keeps the rest unchanged.

  9. Prop-Drilling

    While working on applications in React, we generally encounter props that help us in extracting & rendering data across the components. We use props to keep ourselves away from the DRY (do not repeat) syndrome. During the course of our development, it so happens that we unknowingly start repeating ourselves so much that we don't even bother to notice the structure of the code. To avoid this situation, we make use of props and at times, we see that even using props turns out to be inefficient.

    It makes sense when we use props to render a set of data in the child component from its immediate parent. But what if there is a scenario wherein there is a need for the parent component to send the data through props to a component that is not its immediate child? Under such cases, we generally go on sending the data from parent to child to grand-child to ..... until we arrive at the component that primarily needs the data. This practice of rendering the data through multiple components is what is referred to as Prop Drilling and is a highly inefficient and tedious process. During prop drilling, we unnecessarily send data to most of the components which are not even in need. This reduces the performance of the application & hence the efficiency.

  10. Ways to optimize a React application

    1. Virtualizing / Windowing

    2. React lazy & React suspense

    3. Dependency optimization

    4. Memoize store

    5. Avoid unnecessary renders

    6. Use production build

    7. Keep state local instead of store

    8. Use CDN

    9. React fragments