JavaScript Interview Questions and Answers

ECMAScript fundamentals, closures, prototypes, event loop, async patterns, DOM manipulation and modern ES6+ features.

Practise 10 random 106 peer-reviewed questions
JavaScript Interview Syllabus & Preparation Strategy

Whether you are preparing for entry-level JavaScript interview questions for freshers or senior software engineer interview questions addressing concurrency, scalability, and system architecture, this track provides peer-reviewed model answers with syntax walkthroughs, edge cases, and practical interview tips.

1 What is a prototype chain? Medium

The prototype chain is a core concept in JavaScript’s inheritance model. It allows objects to inherit properties and methods from other objects. When you try to access a property or method on an object, JavaScript first looks for it on that object itself. If it’s not found, the engine looks up the object's internal [[Prototype]] reference (accessible via Object.getPrototypeOf(obj) or the deprecated __proto__ property) and continues searching up the chain until it finds the property or reaches the end (usually null).

For objects created via constructor functions, the prototype chain starts with the instance, then refers to the constructor’s .prototype object, and continues from there. For example:

    function Person() {}
    const person1 = new Person();

    console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
    

This mechanism allows for property and method sharing among objects, enabling code reuse and a form of inheritance.

Summary:

  • The prototype chain enables inheritance in JavaScript.
  • If a property isn’t found on an object, JavaScript looks up its prototype chain.
  • The prototype of an object instance can be accessed with Object.getPrototypeOf(obj) or __proto__.
  • The prototype of a constructor function is available via Constructor.prototype.
  • The chain ends when the prototype is null.

The prototype chain among objects appears as below,

![Screenshot](images/prototype_chain.png)

2 What is the Temporal Dead Zone? Medium

The Temporal Dead Zone (TDZ) refers to the period between the start of a block and the point where a variable declared with let or const is initialized. During this time, the variable exists in scope but cannot be accessed, and attempting to do so results in a ReferenceError.

This behavior is part of JavaScript's ES6 (ECMAScript 2015) specification and applies only to variables declared with let and const, not var. Variables declared with var are hoisted and initialized with undefined, so accessing them before the declaration does not throw an error, though it can lead to unexpected results.

#### Example

    function someMethod() {
        console.log(counter1); // Output: undefined (due to var hoisting)
        console.log(counter2); // Throws ReferenceError (TDZ for let)
    
        var counter1 = 1;
        let counter2 = 2;
    }
    
3 What is Hoisting? Medium

Hoisting is JavaScript's default behavior where variable and function declarations are moved to the top of their scope before code execution. This means you can access certain variables and functions even before they are defined in the code.

Example of variable hoisting:

console.log(message); // Output: undefined
var message = "The variable has been hoisted";
var message;
console.log(message); // undefined
message = "The variable has been hoisted";

Example of function hoisting:

message("Good morning"); // Output: Good morning

function message(name) {
  console.log(name);
}

Because of hoisting, functions can be used before they are declared.

4 What are closures? Medium

A closure is the combination of a function bundled(enclosed) together with its lexical environment within which that function was declared. i.e, It is an inner function that has access to the outer or enclosing function’s variables, functions and other data even after the outer function has finished its execution. The closure has three scope chains.

  1. Own scope where variables defined between its curly brackets
  2. Outer function's variables
  3. Global variables

Let's take an example of closure concept,

    function Welcome(name) {
      var greetingInfo = function (message) {
        console.log(message + " " + name);
      };
      return greetingInfo;
    }
    var myFunction = Welcome("John");
    myFunction("Welcome "); //Output: Welcome John
    myFunction("Hello Mr."); //output: Hello Mr. John
    

As per the above code, the inner function(i.e, greetingInfo) has access to the variables in the outer function scope(i.e, Welcome) even after the outer function has returned.

5 What are modules? Medium

Modules refer to small units of independent, reusable code and also act as the foundation of many JavaScript design patterns. Most of the JavaScript modules export an object literal, a function, or a constructor

6 Why do you need modules? Medium

Before ECMAScript 2015 (ES6) introduced native modules (import/export), JavaScript ran in a single global scope (window), leading to variable name collisions, security leaks, and fragile script tag ordering in HTML.

### Core Architectural Benefits of Modules:

  1. Encapsulation & Scope Isolation:

Variables, functions, and classes defined within a module are scoped locally to that module by default. Only identifiers explicitly marked with export are accessible from external files, preventing accidental global namespace pollution.

  1. Reusability and Composability:

Self-contained modules can be reused across multiple pages, microfrontends, or backend Node.js services without code duplication.

  1. Explicit Dependency Trees:

Every file clearly declares what it needs via import statements. Tools like Webpack, Vite, and Rollup analyze these imports to bundle assets efficiently and eliminate dead code (Tree Shaking).

  1. Maintainability and Testability:

Small, single-responsibility files are significantly easier to read, document, refactor, and unit test in isolation using mock dependencies.

  1. Asynchronous & Deferred Loading:

ES modules load deferred by default (<script type="module">) and support dynamic imports (import('./widget.js')) for code-splitting and performance optimization.

7 What is a promise? Medium

A Promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It acts as a placeholder for a value that may not be available yet but will be resolved in the future.

A Promise can be in one of three states:

  • pending: Initial state, neither fulfilled nor rejected.
  • fulfilled: The operation completed successfully.
  • rejected: The operation failed (e.g., due to a network error).

#### Promise Syntax

    const promise = new Promise(function (resolve, reject) {
      // Perform async operation
    });
    

#### Example: Creating and Using a Promise

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("I'm a Promise!");
      }, 5000);
    });

    promise
      .then((value) => console.log(value)) // Logs after 5 seconds: "I'm a Promise!"
      .catch((error) => console.error(error))  // Handles any rejection
      .finally(() => console.log("Done"));     // Runs regardless of success or failure
    

In the above example:

  • A Promise is created to handle an asynchronous operation with resolve and reject callbacks.
  • The setTimeout resolves the promise with a value after 5 seconds.
  • .then(), .catch(), and .finally() are used to handle success, errors, and cleanup respectively.

The action flow of a promise will be as below,

![Screenshot](images/promises.png)

8 Why do you need a promise? Medium

Promises are used to handle asynchronous operations, especially in languages like JavaScript, which often work with non-blocking operations such as network requests, file I/O, and timers. When an operation is asynchronous, it doesn't immediately return a result; instead, it works in the background and provides the result later. Handling this in a clean, organized way can be difficult without a structured approach.

Promises are used to:

  1. Handle asynchronous operations.
  2. Provide a cleaner alternative to callbacks.
  3. Avoid callback hell.
  4. Make code more readable and maintainable.
9 Explain the three states of promise? Medium

Promises have three states:

  1. Pending: This is an initial state of the Promise before an operation begins
  2. Fulfilled: This state indicates that the specified operation was completed.
  3. Rejected: This state indicates that the operation did not complete. In this case an error value will be thrown.
10 What are the main rules of promise? Medium

A promise must follow a specific set of rules:

  1. A promise is an object that supplies a standard-compliant .then() method
  2. A pending promise may transition into either fulfilled or rejected state
  3. A fulfilled or rejected promise is settled and it must not transition into any other state.
  4. Once a promise is settled, the value must not change.
11 What is promise chaining? Medium

The process of executing a sequence of asynchronous tasks one after another using promises is known as Promise chaining. Let's take an example of promise chaining for calculating the final result,

    new Promise(function (resolve, reject) {
      setTimeout(() => resolve(1), 1000);
    })
      .then(function (result) {
        console.log(result); // 1
        return result * 2;
      })
      .then(function (result) {
        console.log(result); // 2
        return result * 3;
      })
      .then(function (result) {
        console.log(result); // 6
        return result * 4;
      });
    

In the above handlers, the result is passed to the chain of .then() handlers with the below work flow,

  1. The initial promise resolves in 1 second,
  2. After that .then handler is called by logging the result(1) and then return a promise with the value of result \* 2.
  3. After that the value passed to the next .then handler by logging the result(2) and return a promise with result \* 3.
  4. Finally the value passed to the last .then handler by logging the result(6) and return a promise with result \* 4.
12 What is promise.all? Medium

Promise.all() is a built-in JavaScript method used to handle multiple asynchronous operations together. It accepts an iterable of promises (usually an array) and returns a single promise that resolves only when all the input promises have successfully resolved.

If any one of the promises rejects, the whole Promise.all() call rejects immediately and the error is passed to the .catch() block. The result array contains values in the same order as the input promises, even if they finish at different times.

    const fetchUser = () => Promise.resolve({ id: 1, name: "John" });
    const fetchOrders = () =>
      new Promise((resolve) => setTimeout(() => resolve([101, 102]), 200));
    const fetchProfile = () => Promise.resolve("active");

    Promise.all([fetchUser(), fetchOrders(), fetchProfile()])
      .then(([user, orders, status]) => {
        console.log(user); // { id: 1, name: 'John' }
        console.log(orders); // [101, 102]
        console.log(status); // 'active'
      })
      .catch((error) => {
        console.log("One of the requests failed:", error);
      });
    

Let's consider a case where one promise rejects:

    Promise.all([
      Promise.resolve("A"),
      Promise.reject(new Error("Request failed")),
      Promise.resolve("C"),
    ])
      .then((values) => console.log(values))
      .catch((error) => console.log(error.message)); // Request failed
    

Key points:

  1. It waits for all promises to resolve.
  2. It rejects as soon as any promise fails.
  3. The output order matches the input order, not the completion order.
  4. Non-promise values are treated as resolved values automatically.

Promise.all() is useful when you need to run multiple independent async tasks concurrently and continue only after all of them are done, such as fetching multiple API endpoints together.

13 Does Promise.all() cancel the other Promises? Medium

No. Promise.all() does not cancel the other promises. It only waits for all of them to finish, and if any one rejects, the overall Promise.all() call rejects immediately.

A JavaScript promise does not have a built-in cancel API. Once a promise starts, it keeps running unless the underlying async operation supports cancellation on its own. For example, fetch() supports cancellation using AbortController.

    const controller = new AbortController();
    const signal = controller.signal;

    const requestA = fetch("/api/a", { signal });
    const requestB = Promise.reject(new Error("Server error"));
    const requestC = fetch("/api/c", { signal });

    Promise.all([requestA, requestB, requestC])
      .then((results) => console.log(results))
      .catch((error) => {
        console.log("Promise.all rejected:", error.message);
        controller.abort(); // Cancels the fetch requests still in progress
      });
    

In the example above, if requestB rejects, Promise.all() rejects, but requestA and requestC are not automatically canceled. They continue unless you explicitly abort them using the underlying API or custom logic.

How to cancel async work:

  1. Use an API that supports cancellation, such as fetch() with AbortController.
  2. For custom promises, you can add a cancel() method or a flag to stop work internally.
  3. If the work is not cancelable, the promise cannot be truly canceled from outside.

Note: Promise.all() is about aggregation, not cancellation. It waits for all promises to settle, but it does not stop the other async operations by itself.

### What is the difference between return and return await in async functions

In an async function, return value simply returns the value or promise. If the value is a rejected promise, the rejection is not caught by a surrounding try/catch unless you await it first.

return await value pauses the function until the promise settles, so a try/catch around it can handle errors properly.

    async function withoutAwait() {
      try {
        return Promise.reject(new Error("Something failed"));
      } catch (error) {
        console.log("This will not run");
        return "fallback";
      }
    }

    async function withAwait() {
      try {
        return await Promise.reject(new Error("Something failed"));
      } catch (error) {
        console.log("Caught inside the function:", error.message);
        return "fallback";
      }
    }

    withoutAwait().catch((err) => console.log("outside catch:", err.message));
    withAwait().then((value) => console.log("withAwait result:", value));
    

Output:

    outside catch: Something failed
    Caught inside the function: Something failed
    withAwait result: fallback
    

In short:

  • return promise returns the promise without waiting for it.
  • return await promise waits for the promise and allows the surrounding try/catch to catch errors.

return await is especially useful when you want to clean up or handle the error inside the same async function before it exits.

14 What is the purpose of the race method in promise? Medium

Promise.race() method will return the promise instance which is firstly resolved or rejected. Let's take an example of race() method where promise2 is resolved first

    var promise1 = new Promise(function (resolve, reject) {
      setTimeout(resolve, 500, "one");
    });
    var promise2 = new Promise(function (resolve, reject) {
      setTimeout(resolve, 100, "two");
    });

    Promise.race([promise1, promise2]).then(function (value) {
      console.log(value); // "two" // Both promises will resolve, but promise2 is faster
    });
    
15 What is event bubbling? Medium

Event bubbling is a type of event propagation in which an event first triggers on the innermost target element (the one the user interacted with), and then bubbles up through its ancestors in the DOM hierarchy — eventually reaching the outermost elements, like the document or window.

By default, event listeners in JavaScript are triggered during the bubbling phase, unless specified otherwise.

    <div>
      <button class="child">Hello</button>
    </div>

    <script>
      const parent = document.querySelector("div");
      const child = document.querySelector(".child");

      // Bubbling phase (default)
      parent.addEventListener("click", function () {
        console.log("Parent");
      });

      child.addEventListener("click", function () {
        console.log("Child");
      });
    </script>
    //Child
    //Parent
    

Here, at first, the event triggers on the child button. Thereafter it bubbles up and triggers the parent div's event handler.

16 What are the pros and cons of promises over callbacks? Medium

Below are the list of pros and cons of promises over callbacks,

Pros:

  1. It avoids callback hell which is unreadable
  2. Easy to write sequential asynchronous code with .then()
  3. Easy to write parallel asynchronous code with Promise.all()
  4. Solves some of the common problems of callbacks(call the callback too late, too early, many times and swallow errors/exceptions)

Cons:

  1. It makes little complex code
  2. You need to load a polyfill if ES6 is not supported
17 How do you make asynchronous HTTP request? Medium

Browsers provide an XMLHttpRequest object which can be used to make asynchronous HTTP requests from JavaScript by passing the 3rd parameter as true.

      function httpGetAsync(theUrl, callback) {
        var xmlHttpReq = new XMLHttpRequest();
        xmlHttpReq.onreadystatechange = function () {
          if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200)
            callback(xmlHttpReq.responseText);
        };
        xmlHttpReq.open("GET", theUrl, true); // true for asynchronous
        xmlHttpReq.send(null);
      }
      

Today this is considered deprecated, because an async fetch call (in browsers later than 2016) is simpler and more robust.

18 What is the difference between proto and prototype? Medium

The __proto__ object is the actual object that is used in the lookup chain to resolve methods, etc. Whereas prototype is the object that is used to build __proto__ when you create an object with the new operator (a special variant of a function call).

      new Employee().__proto__ === Employee.prototype;
      new Employee().prototype === undefined;
      

There are few more differences,

| feature | Prototype | proto |
| ---------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
| Access | All function constructors have prototype properties. | All objects have \_\_proto\_\_ property |
| Purpose | Used to reduce memory wastage with a single copy of function | Used in lookup chain to resolve methods, constructors etc. |
| ECMAScript | Introduced in ES6 | Introduced in ES5 |
| Usage | Frequently used | Rarely used |

19 How do you create an object with a prototype? Medium

The Object.create() method is used to create a new object with the specified prototype object and properties. i.e, It uses an existing object as the prototype of the newly created object. It returns a new object with the specified prototype object and properties.

      const user = {
        name: "John",
        printInfo: function () {
          console.log(`My name is ${this.name}.`);
        },
      };

      const admin = Object.create(user);

      admin.name = "Nick"; // Remember that "name" is a property set on "admin" but not on "user" object

      admin.printInfo(); // My name is Nick
      
20 How do you get the prototype of an object? Medium

You can use the Object.getPrototypeOf(obj) method to return the prototype of the specified object. i.e. The value of the internal prototype property. If there are no inherited properties then null value is returned.

      const newPrototype = {};
      const newObject = Object.create(newPrototype);

      console.log(Object.getPrototypeOf(newObject) === newPrototype); // true
      
Showing 20 of 106 questions

Frequently Asked Questions About JavaScript Interviews

What do hiring managers evaluate in JavaScript technical rounds?

Technical interviewers look for foundational fluency, idiomatic syntax, clarity when communicating complex logic, and awareness of performance trade-offs (e.g. memory footprint, render performance, and network latency) in production environments.

What are the best interview tips for practicing JavaScript questions?

Use active recall: summarize each answer in your own words before revealing the model solution. Focus on explaining why a certain approach is chosen rather than just memorizing code syntax.