FrontendDeveloper.in

JavaScript Interview Questions

  • Question 451

    How to use await outside of async function prior to ES2022?

    Prior to ES2022, if you attempted to use an await outside of an async function resulted in a SyntaxError.

    await Promise.resolve(console.log("Hello await")); // SyntaxError: await is only valid in async function
    

    But you can fix this issue with an alternative IIFE (Immediately Invoked Function Expression) to get access to the feature.

    (async function () {
    await Promise.resolve(console.log("Hello await")); // Hello await
    })();
    

    In ES2022, you can write top-level await without writing any hacks.

    await Promise.resolve(console.log("Hello await")); //Hello await
    
  • Question 452

    What is the purpose of the this keyword in JavaScript?

    The this keyword in JavaScript refers to the object that is executing the current function. Its value is determined by how a function is called, not where it is defined. this is essential for writing object-oriented and event-driven code, as it allows methods to interact with the data of the object they belong to.

    Example 1: this in a Global Context

    console.log(this);
    
    • In a global context, this refers to the global object (e.g., window in a browser).

    Example 2: this in a Function

    function displayThis() {
    console.log(this);
    }
    
    displayThis();
    
    • In a regular function, this refers to the global object(window in browser and global in nodejs) for non-strict mode. In strict mode, it's value is undefined.

    Example 3: this in a Method

    const person = {
    name: "John",
    greet: function () {
    console.log("Hello, " + this.name);
    },
    };
    
    person.greet();
    
    • In a method, this refers to the object that owns the method (person in the case).

    Example 4: this in an Event Handler

    document.getElementById("myButton").addEventListener("click", function () {
    console.log(this);
    });
    
    • In an event handler, this refers to the element that triggered the event (the button in this case).

    Example 5: this with Arrow Functions

    const obj = {
    age: 42,
    regular: function() { console.log(this.age); },
    arrow: () => { console.log(this.age); }
    };
    obj.regular(); // 42 (this refers to obj)
    obj.arrow();   // undefined (this refers to the outer scope, not obj)
    
    • Arrow functions do not have their own this binding; they inherit it from their surrounding (lexical) context.

    Example 6: this in Constructor Functions / Classes

    function Person(name) {
    this.name = name;
    }
    
    const p1 = new Person('Sudheer');
    console.log(p1.name); // Sudheer
    
    • When used with new, this refers to the newly created object.
  • Question 453

    What are the uses of closures?

    Closures are a powerful feature in programming languages like JavaScript. They allow functions to retain access to variables from their containing (enclosing) scope even after the outer function has finished executing. This means that a function defined within another function can access variables from the outer function, even if the outer function has already returned. Here are some common use cases of closures:

    • Data Privacy: Closures can be used to create private variables and methods. By defining variables within a function's scope and returning inner functions that have access to those variables, you can create a form of encapsulation, limiting access to certain data or functionality.

    • Function Factories: Closures are often used to create functions with pre-set parameters. This is useful when you need to create multiple functions with similar behavior but different configurations.

    • Callback Functions: Closures are frequently used in asynchronous programming, such as handling event listeners or AJAX requests. The inner function captures variables from the outer scope and can access them when the callback is invoked.

    • Memoization: Closures can be used for memoization, a technique to optimize performance by caching the results of expensive function calls. The inner function can remember the results of previous calls and return the cached result if the same input is provided again.

    • iterators and Generators: Closures can be used to create iterators and generators, which are essential for working with collections of data in modern JavaScript.

  • Question 454

    What are the phases of execution context?

    The execution context in JavaScript is a data structure that stores the information necessary for executing a piece of code. It includes the code itself, the values of the variables used in the code, and the scope chain. The scope chain is a list of objects that are used to resolve variable names.

    The execution context has two phases:

    • Creation phase: In this phase, the JavaScript engine creates the execution context and sets up the script's environment. This includes creating the variable object and the scope chain.
    • Execution phase: In this phase, the JavaScript engine executes the code in the execution context. This includes evaluating expressions, assigning values to variables, and calling functions.

    The execution context is created when a function is called. The function's code is then executed in the execution context. When the function returns, the execution context is destroyed.

  • Question 455

    What are the possible reasons for memory leaks?

    Memory leaks can lead to poor performance, slow loading times and even crashes in web applications. Some of the common causes of memory leaks are listed below,

    1. The execessive usage of global variables or omitting the var keyword in local scope.
    2. Forgetting to clear the timers set up by setTimeout or setInterval.
    3. Closures retain references to variables from their parent scope, which leads to variables might not garbage collected even they are no longer used.
  • Question 456

    What are the optimization techniques of V8 engine?

    V8 engine uses the below optimization techniques.

    1. Inline expansion: It is a compiler optimization by replacing the function calls with the corresponding function blocks.
    2. Copy elision: This is a compiler optimization method to prevent expensive extra objects from being duplicated or copied.
    3. Inline caching: It is a runtime optimization technique where it caches the execution of older tasks those can be lookup while executing the same task in the future.
  • Question 457

    What are the examples of built-in higher order functions?

    There are several built-in higher order functions exists on arrays, strings, DOM and promise methods in javascript. These higher order functions provides significant level of abstraction. The list of functions on these categories are listed below,

    1. arrays: map, filter, reduce, sort, forEach, some etc.
    2. DOM: The DOM method element.addEventListener(type, handler) also accepts the function handler as a second argument.
    3. Strings: replace() method.
  • Question 459

    How do you create polyfills for map, filter and reduce methods?

    The polyfills for array methods such as map, filter and reduce methods can be created using array prototype.

    1. map:

    The built-in Array.map method syntax will be helpful to write polyfill. The map method takes the callback function as an argument and that callback function can have below three arguments passed into it.

    i. Current value ii. Index of current value(optional) iii. array(optional)

    The syntax would like below,

    let newArray = arr.map(callback(currentValue[, index, arr) {
    // return new array after executing the code
    })
    

    Let's build our map polyfill based on the above syntax,

    Array.prototype.myMap = function (cb) {
    let newArr = [];
    for (let i = 0; i < this.length; i++) {
    newArr.push(cb(this[i], i, this));
    }
    return newArr;
    };
    
    const nums = [1, 2, 3, 4, 5];
    const multiplyByTwo = nums.myMap((x) => x * 2);
    console.log(multiplyByTwo); // [2, 4, 6, 8, 10]
    

    In the above code, custom method name 'myMap' has been used to avoid conflicts with built-in method.

    1. filter: Similar to map method, Array.filter method takes callback function as an argument and the callback function can have three agurguments passed into it.

    i. Current value ii. Index of current value(optional) iii. array(optional)

    The syntax looks like below,

    let newArray = arr.filter(callback(currentValue[, index, arr) {
    // return new array whose elements satisfy the callback conditions
    })
    

    Let's build our filter polyfill based on the above syntax,

    Array.prototype.myFilter = function (cb) {
    let newArr = [];
    for (let i = 0; i < this.length; i++) {
    if (cb(this[i], i, this)) {
    newArr.push(this[i]);
    }
    }
    return newArr;
    };
    
    const nums = [1, 2, 3, 4, 5, 6];
    const evenNums = nums.myFilter((x) => x % 2);
    console.log(evenNums); // [2, 4, 6]
    
    1. reduce:

    The built-in Array.reduce method syntax will be helpful to write our own polyfill. The reduce method takes the callback function as first argument and the initial value as second argument.

    The callback function can have four arguments passed into it. i. Accumulator ii. Current value iii. Index of current value(optional) iv. array(optional)

    The syntax would like below,

    arr.reduce(callback((acc, curr, i, arr) => {}), initValue);
    

    Let's build our reduce polyfill based on the above syntax,

    Array.prototype.myReduce = function(cb, initialValue) {
    let accumulator = initialValue;
    for(let i=0; i< this.length; i++) {
    accumulator = accumulator ? cb(accumulator, this[i], i, this) : this[i];
    }
    return accumulator;
    }
    const nums = [1, 2, 3, 4, 5, 6];
    const sum = nums.myReduce((acc, curr, i, arr) => {
    return acc += curr
    }, 0);
    console.log(sum); // 21
    
  • Question 460

    What is the difference between map and forEach functions?

    Both map and forEach functions are used to iterate over an arrays but there are some differences in their functionality.

    1. Returning values: The map method returns a new array with transformed elements whereas forEach method returns undefined even though both of them are doing the same job.
    const arr = [1, 2, 3, 4, 5];
    arr.map(x => x * x); // [1, 4, 9, 16, 25]
    arr.forEach(x => x * x); //
    
    The `forEach()` method in JavaScript always returns undefined. This is because forEach() is used to iterate over arrays and perform side effects on each element, rather than returning a `new array or transforming the original array`
    
    1. Chaining methods: The map method is chainable. i.e, It can be attached with reduce, filter, sort and other methods as well. Whereas forEach cannot be attached with any other methods because it returns undefined value.
    const arr = [1, 2, 3, 4, 5];
    arr.map((x) => x * x).reduce((total, cur) => total + cur); // 55
    arr.forEach((x) => x * x).reduce((total, cur) => total + cur); //Uncaught TypeError: Cannot read properties of undefine(reading 'reduce')
    
    1. Mutation: The map method doesn't mutate the original array by returning new array. Whereas forEach method also doesn't mutate the original array but it's callback is allowed to mutate the original array.

    Note: Both these methods existed since ES5 onwards.

  • Question 461

    Give an example of statements affected by automatic semicolon insertion?

    The javascript parser will automatically add a semicolon while parsing the source code. For example, the below common statements affected by Automatic Semicolon Insertion(ASI).

    1. An empty statement
    2. var statement
    3. An expression statement
    4. do-while statement
    5. continue statement
    6. break statement
    7. return statement
    8. throw statement
  • Question 462

    What are the event phases of a browser?

    There are 3 phases in the lifecycle of an event propagation in JavaScript,

    1. Capturing phase: This phase goes down gradually from the top of the DOM tree to the target element when a nested element clicked. Before the click event reaching the final destination element, the click event of each parent's element must be triggered.

    2. Target phase: This is the phase where the event originally occurred reached the target element .

    3. Bubbling phase: This is reverse of the capturing phase. In this pase, the event bubbles up from the target element through it's parent element, an ancestor and goes all the way to the global window object.

    The pictorial representation of these 3 event phases in DOM looks like below,

    Screenshot

  • Question 463

    What are the real world use cases of proxy?

    Proxies are not used in regular day to day JavaScript work but they enabled many exciting programming patterns. Some of the real world use cases are listed below,

    1. Vue3 used proxy concept to implement reactive state
    2. SolidJS implemented reactive stores
    3. Immerjs built upon proxy to track updates to immutable updates
    4. ZenStack improved Prisma ORM for access control layer
  • Question 464

    What are hidden classes?

    Since JavaScript is a dynamic programming language, you can add or remove properties and methods from objects on the fly at runtime. This nature of JavaScript increases the dynamic dictionary lookups(because objects implemented as HashTables in memory) for retrieving a property on an object.

    Let's consider the following example to see how the additional properties age and gender added at runtime.

    function Person(name) {
    this.name = name;
    }
    
    var person1 = new Person("John");
    var person2 = new Person("Randy");
    
    person1.age = 40;
    person1.gender = "Male";
    
    person2.gender = "Female";
    person2.age = 50;
    

    As a result, this behavior leads to lower JavaScript performance compared to the contiguous buffer method used in non-dynamic languages. The V8 engine provided a solution named hidden classes to optimize the access time when retrieving a property on an object. This optimization is achieved by sharing hidden classes among objects created in a similar fashion. These hidden classes are attached to each and every object to track its shape.

    When V8 engine sees the constructor function(e.g, Person) is declared, it creates a hidden class (let's say Class01) without any offsets. Once the first property assignment statement (this.name = name) is executed, V8 engine will create a new hidden class (let's say Class02), inheriting all properties from the previous hidden class (Class01), and assign the property to offset 0. This process enables compiler to skip dictionary lookup when you try to retrieve the same property(i.e, name). Instead, V8 will directly point to Class02. The same procedure happens when you add new properties to the object.

    For example, adding age and gender properties to Person constructor leads to transition of hidden classes(Class02 -> Class03 -> Class04). If you create a second object(Person2) based on the same Person object, both Class01 and Class02 hidden classes are going to be shared. However, the hidden classes Class03 and Class04 cannot be shared because second object has been modified with a different order of properties assignment.

    Since both the objects(person1 and person2) do not share the hidden classes, now V8 engine cannot use Inline Caching technique for the faster access of properties.

  • Question 465

    What is inline caching?

    Inline caching is an optimization technique based on the observation that repeated calls to same function tends to occur on same type of objects. The V8 compiler stores a cache of the type of objects that were passed as a parameter in recent method calls. Upon next time when same function is called, compiler can directly search for the type in cache.

    Let's consider an example where the compiler stores the shape type in cache for repeated calls in the loop.

    let shape = { width: 30, height: 20 }; // Compiler store the type in cache as { width: <int>, height: <int>} after repeated calls
    
    function area(obj) {
    //Calculate area
    }
    for (let i = 0; i < 100; i++) {
    area(shape);
    }
    

    After few successful calls of the same area method to its same hidden class, V8 engine omits the hidden class lookup and simply adds the offset of the property to the object pointer itself. As a result, it increases the execution speed.

    There are mainly 3 types of inline caching possible:

    1. Monomorphic: This is a optimized caching technique in which there can be always same type of objects passed.
    2. Polymorphic: This ia slightly optimized caching technique in which limited number of different types of objects can be passed.
    3. Megamorphic: It is an unoptimized caching in which any number of different objects can be passed.
Get LinkedIn Premium at Rs 399