JavaScript interview questions test closures, promises, the event loop, prototypes, and modern ES features including async/await and destructuring. Most frontend and full-stack JS interviews include at least one question on how JavaScript runs code asynchronously, and many candidates stumble there. This page covers 40 real JavaScript interview questions with answers across four formats: multiple choice, written answers, spoken responses, and live coding challenges, the same mix used in modern technical interviews.
Practice each question in your browser with no signup required. Answers stay hidden until you attempt the question: type, speak, or code your response first, then compare it against the model answer. Questions range from fundamentals for your first developer job to scenario-based questions for senior roles, so you can find the gaps in your knowledge before the real interview.
40 questions with answersLast updated: Oct 19, 2026
Choose a question. Pick any of the 40 questions. They mix multiple choice, written, spoken, and coding, the same mix a real interviewer uses.
Attempt your answer. Select a multiple-choice option, type your written response, record a spoken answer, or write code in the editor.
Reveal the model answer. Once you have attempted the question, the model answer unlocks so you can compare it with yours.
Move to the next question. Track how many questions you have attempted with the progress bar at the top of the page.
Take a mock interview. When you are ready, try a full mock technical interview on Plaicer with AI-driven follow-up questions.
JavaScript Interview Questions and Answers
0 of 40 attempted
1Multiple choice
How would you describe JavaScript and where does it run?
Attempt the question first to reveal the answer.
Answer
JavaScript is a lightweight, dynamically-typed language originally for browsers, now running on servers via Node.js, making it versatile for full-stack development.
2Written answer
Describe JavaScript's characteristics, execution model, and dynamic typing
Attempt the question first to reveal the answer.
Answer
JavaScript is dynamically-typed, meaning types are determined at runtime. Variables can hold any type and change types. It's interpreted, executing line-by-line in the browser or Node.js. JavaScript is single-threaded but handles asynchronous I/O through callbacks, Promises, and async/await, making it responsive without multi-threading. It's prototype-based, allowing flexible object creation and inheritance. Modern JavaScript (ES6+) adds classes, modules, arrow functions, and const/let for better code organization. Its flexibility enables rapid development but requires discipline to avoid type-related bugs.
3Spoken answer
Walk through how you would fetch data from an API and handle errors using async/await
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
I used async/await with try-catch: try { const response = await fetch(url); const data = await response.json(); } catch (error) { console.error(error); }. I checked response.ok before parsing. For multiple requests, I used Promise.all: const [data1, data2] = await Promise.all([fetch1(), fetch2()]); Concurrent requests were faster than sequential.
4Coding
Write an async function that fetches JSON data from an API and displays it on the page
// Fetch and Display Data
// Implement the solution here
Attempt the question first to reveal the answer.
Answer
async function fetchAndDisplay(url) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
const container = document.getElementById('data-container');
container.innerHTML = JSON.stringify(data, null, 2);
} catch (error) {
console.error('Error:', error);
}
}
5Multiple choice
Explain the Document Object Model and its role in web interaction
Attempt the question first to reveal the answer.
Answer
The DOM represents the HTML document as a tree. JavaScript accesses it via document object to read/modify elements, attributes, and content dynamically.
6Written answer
Explain how the DOM represents HTML as a tree and how JavaScript manipulates it
Attempt the question first to reveal the answer.
Answer
The browser parses HTML into a tree where each element is a node. The document object provides access to this tree. JavaScript can query elements: document.getElementById, querySelector. It reads/writes properties: element.textContent, element.style.color. It modifies the tree: appendChild, removeChild, innerHTML. Event listeners attach to elements: element.addEventListener('click', handler). Changes trigger reflows (recalculating layout) and repaints (redrawing). DOM manipulation is reactive: changing properties immediately affects the rendered page. Performance-conscious code batches DOM changes to minimize reflows.
7Spoken answer
Describe a situation where you discovered a this binding issue and how you resolved it
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
A callback lost this context when passed as an event handler. Inside the callback, this was undefined instead of the object. I fixed it by using arrow functions: element.addEventListener('click', () => this.method()); or by binding: element.addEventListener('click', this.method.bind(this)); Arrow functions inherit this from the enclosing scope.
8Coding
Implement event delegation to handle clicks on dynamically created list items
// Event Delegation Handler
// Implement the solution here
Attempt the question first to reveal the answer.
Answer
const list = document.getElementById('list');
list.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
console.log('Clicked item:', event.target.textContent);
event.target.classList.toggle('selected');
}
});
9Multiple choice
What is a closure and how does variable scope create them?
Attempt the question first to reveal the answer.
Answer
When a function is defined inside another, it captures the outer scope's variables. Those variables remain accessible even after the outer function completes.
10Written answer
Explain how functions capture variables from outer scopes and retain them
Attempt the question first to reveal the answer.
Answer
When a function is created, it captures variables from its enclosing scope. Even after the outer function returns, the inner function retains access through the closure. Example: function outer() { const x = 5; return function inner() { return x; } } The returned function remembers x. This enables data privacy: x is inaccessible except through inner(). Closures power callbacks and event handlers. The captured variables persist in memory as long as the closure exists, potentially causing memory leaks if not cleaned up.
11Spoken answer
Explain how you would implement a closure to create private variables in a module
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
I created a module exporting functions that shared private state: const counter = (() => { let count = 0; return { increment: () => ++count, get: () => count }; })(); count is inaccessible outside; only increment() and get() expose controlled access. This pattern protects internal state from external modification.
12Coding
Create a counter function using closures to maintain private state
Explain JavaScript hoisting and how it affects function and variable declarations
Attempt the question first to reveal the answer.
Answer
Hoisting moves declarations to the top during compilation. var and function declarations are fully hoisted; let/const are hoisted but not initialized (temporal dead zone).
14Written answer
Describe how hoisting works differently for var, let, const, functions, and classes
Attempt the question first to reveal the answer.
Answer
During compilation, declarations are moved to the top of their scope. var declarations are hoisted and initialized as undefined, so accessing before the line where it's declared returns undefined (not an error). Function declarations are fully hoisted; they're callable before the declaration. let/const are hoisted but not initialized, creating a temporal dead zone: accessing before declaration throws ReferenceError. Arrow functions are treated like variables (hoisted if const, temporal dead zone). Classes are hoisted but behave like let/const regarding the temporal dead zone. Understanding hoisting prevents unexpected undefined values.
15Spoken answer
Talk about a time you debugged callback hell and refactored to Promises or async/await
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
Legacy code had deeply nested callbacks handling sequential API calls. Refactoring to async/await flattened the structure: const user = await fetchUser(); const posts = await fetchPosts(user.id); Code was immediately easier to follow. Error handling with try-catch replaced nested .catch() handlers.
16Coding
Chain multiple Promises to handle sequential API calls
How do let and const differ from var in scope and reassignment?
Attempt the question first to reveal the answer.
Answer
var has function scope. let/const have block scope (if, loop). const prevents reassignment but allows mutation of objects/arrays.
18Written answer
Contrast var's function scope with let/const's block scope and reassignment rules
Attempt the question first to reveal the answer.
Answer
var is function-scoped: var x = 1; inside an if block is accessible outside the block. let/const are block-scoped: let x = 1; inside {} is inaccessible outside. var can be redeclared and reassigned. let can be reassigned but not redeclared in the same scope. const cannot be reassigned or redeclared. const prevents reassignment but allows mutation: const obj = {}; obj.prop = 1 is valid. Modern code prefers const (immutability) and let (clear scope), avoiding var.
19Spoken answer
Describe how you would set up event listeners on dynamically created DOM elements
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
I queried parent elements, attaching event listeners there instead of on individual items: parent.addEventListener('click', (e) => { if (e.target.matches('.item')) { handleItemClick(e); } }); Event delegation handles dynamically created elements without reattaching listeners for each new item.
20Coding
Select DOM elements and modify their content, attributes, and styles
// DOM Query and Manipulation
// Implement the solution here
What is callback hell and how do Promises and async/await solve it?
Attempt the question first to reveal the answer.
Answer
Deeply nested callbacks become hard to read. Promises chain with .then(); async/await makes asynchronous code look synchronous, improving readability.
22Written answer
Explain how Promises and async/await provide better control flow than callbacks
Attempt the question first to reveal the answer.
Answer
Callbacks passed to functions execute when the operation completes. Nested callbacks (callback hell) become hard to read and error-prone. Promises represent eventual completion/failure, returning a Promise object allowing chaining: promise.then(success).catch(error). async/await builds on Promises, making code look synchronous: const result = await operation(); Code flows top-to-bottom, easier to follow. Error handling uses try-catch, not nested callbacks. async/await handles sequences naturally: result1 = await op1(); result2 = await op2(result1). Readability improves, reducing bugs.
23Spoken answer
Explain how you would use destructuring to improve code readability in a real project
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
Instead of accessing object properties repeatedly in function parameters, I destructured: function displayUser({ name, email, avatar }) { instead of function displayUser(user) { const name = user.name; const email = user.email; ... }. Defaults reduced null checks: { name = 'Guest' } = {}.
24Coding
Use array methods like map, filter, reduce with callback functions
// Array Methods with Callbacks
// Implement the solution here
Explain how the event loop manages asynchronous operations in JavaScript
Attempt the question first to reveal the answer.
Answer
The event loop checks if the call stack is empty, then processes microtasks (Promises) before macrotasks (setTimeout), enabling asynchronous I/O without threads.
26Written answer
Describe the event loop, call stack, task queue, and microtask queue mechanics
Attempt the question first to reveal the answer.
Answer
The call stack executes synchronous code. Asynchronous operations (setTimeout, fetch, event handlers) add tasks to the task queue (macrotasks); Promises add to the microtask queue. The event loop runs: (1) Execute all call stack code, (2) Execute all microtasks, (3) Execute one macrotask, (4) Repeat. Microtasks run before the next macrotask, so Promises resolve before setTimeout callbacks. This single-threaded model with queuing enables handling I/O without threads. Understanding the order prevents timing bugs and performance issues.
27Spoken answer
Walk through how you would implement memoization using closures and objects
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
I implemented memoization for expensive computations: const memoize = (fn) => { const cache = {}; return (arg) => { if (!(arg in cache)) cache[arg] = fn(arg); return cache[arg]; }; }. Calling memoize(expensiveFunction) returned a cached version. Repeated calls with the same argument returned cached results instantly.
28Coding
Implement a debounce function to limit how often a function executes
// Debounce Function
// Implement the solution here
How does prototypal inheritance differ from classical inheritance?
Attempt the question first to reveal the answer.
Answer
Objects inherit from other objects via the prototype chain. ES6 classes are syntactic sugar over prototypes, making inheritance feel more familiar to classical programmers.
30Written answer
Explain how objects inherit from prototypes and how to set up the chain
Attempt the question first to reveal the answer.
Answer
Every object has a prototype, accessed via __proto__ or Object.getPrototypeOf(). When a property is not found on the object, JavaScript searches up the prototype chain. Setting obj.__proto__ = baseObj chains them. Function constructors: function Base() {}; function Child() {}; Child.prototype = Object.create(Base.prototype); sets up inheritance. ES6 classes: class Child extends Base {} is syntactic sugar over prototypes. The new operator creates an object with the constructor's prototype. Understanding the chain enables writing reusable hierarchies.
31Spoken answer
Talk about understanding and preventing memory leaks from retained closures
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
A closure captured a large dataset in a loop, preventing garbage collection. Each closure retained the entire dataset. I fixed it by limiting captured variables or using weak references. I ensured event listeners and closures were cleaned up when no longer needed: element.removeEventListener or assigning null to references.
32Coding
Use destructuring to extract and work with object properties
// Object Destructuring
// Implement the solution here
Attempt the question first to reveal the answer.
Answer
const user = { name: 'Alice', age: 30, email: 'alice@example.com' };
const { name, age, email = 'unknown' } = user;
const [first, ...rest] = [1, 2, 3, 4];
function greet({ name, age }) { console.log(`Hello ${name}, age ${age}`); }
33Multiple choice
What does this refer to and how does its binding work in different contexts?
Attempt the question first to reveal the answer.
Answer
In methods, this is the calling object. In functions, it's the global object (or undefined in strict mode). Arrow functions inherit this from enclosing scope. .call, .apply, .bind can override it.
34Written answer
Discuss how this binding works in different contexts and how to control it
Attempt the question first to reveal the answer.
Answer
In methods, this is the calling object: obj.method() has this as obj. In functions, this is the global object (or undefined in strict mode). Arrow functions inherit this from the enclosing scope, ignoring the call context. .call(obj, args) executes a function with this as obj. .apply(obj, [args]) is like .call but takes an array. .bind(obj) returns a new function with this bound to obj. .bind is useful for event handlers and callbacks where context is lost. Understanding binding prevents the common bug of losing this in callbacks.
35Spoken answer
Describe how you would implement the module pattern to organize related functions
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Attempt the question first to reveal the answer.
Answer
I organized related functions in a module: const userModule = (() => { const users = []; return { add: (u) => users.push(u), getAll: () => [...users] }; })(); The module had private state (users) and public API (add, getAll). This pattern is cleaner than global functions.
36Coding
Implement the module pattern with private and public methods
The event loop: call stack, task queue, microtasks
Destructuring: arrays and objects
Array methods: map, filter, reduce
The DOM: manipulation, event listeners, event delegation, debouncing
Frequently asked questions
How many JavaScript interview questions are on this page?
40 real JavaScript interview questions across four formats: multiple choice (10), written answer (10), spoken answer (10), and coding (10).
Do I need to sign up to practice?
No. All 40 questions and model answers are free with no signup. You attempt each question first, and the model answer unlocks once you have engaged.
Who are these JavaScript interview questions for?
Developers preparing for JavaScript technical interviews at any level, from first job to senior engineer roles. Questions cover fundamentals through real-world scenarios.
What topics do the JavaScript questions cover?
The questions cover closures, scope, hoisting, and the this keyword. They also test prototypes, promises, async/await, and the event loop. Coding tasks include DOM manipulation, event delegation, and debouncing.
Can I practice coding questions in the browser?
Yes. Coding questions include starter code in an in-browser editor, so you can write your solution without installing anything.
How often are the questions updated?
The question bank is reviewed regularly and updated to reflect current JavaScript interview patterns.
Can I take a full mock interview?
Yes. Plaicer's mock technical interview walks you through a realistic 30 to 45 minute interview with AI-driven follow-up questions.
Do the questions cover TypeScript?
No. These questions focus on core JavaScript, since TypeScript interviews still test the same fundamentals: closures, async code, and the event loop.