52 questions
JavaScript interview questions and answers
Junior questions
1. What is the difference between
var,letandconst?Answer
varis function scoped, whileletandconstare block scoped. Avarcan be redeclared,letandconstcannot be redeclared in the same scope. Aconstcannot be reassigned, but the contents of an object or array stored in it can still change. Modern code usesconstby default andletonly when the value changes.Follow-up question: What is the temporal dead zone (TDZ)?
- Course: JavaScript Essentials
2. What is hoisting in JavaScript?
Answer
When a scope is created, the engine registers declarations before any code runs. Function declarations are hoisted completely, so you can call them before the line where they are declared. A
varis hoisted with the valueundefined.letandconstare registered too, but until their declaration line they sit in the TDZ, and accessing them throws aReferenceError.Follow-up question: Is a function expression assigned to a
consthoisted?- Course: JavaScript Essentials
3. What is the difference between
==and===?Answer
===compares without type coercion: the values must have the same type and be equal.==first coerces the operands to a common type, so0 == ""andnull == undefinedare bothtrue. Because the coercion rules are surprising,===is used almost everywhere. One exception sometimes kept on purpose:x == nullchecks for bothnullandundefinedat once.Follow-up question: Why is
NaN === NaNfalse, and how do you check a value forNaN?- Course: JavaScript Essentials
4. What is a closure? Give an example of how it is used.
Answer
A closure is a function together with the lexical environment it was created in. It can access the outer function's variables even after that function has returned. This is how you build private state, for example a counter that returns an
incrementfunction. Callbacks, function factories and memoization also rely on closures.Follow-up question: What does
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i))print, and how do you fix it?- Course: JavaScript Essentials
5. How does a shallow copy of an object differ from a deep copy?
Answer
A shallow copy creates a new top level object, but nested objects remain shared references. You make one with spread
{ ...obj }orObject.assign. A deep copy recursively copies every level, so changes to nested data do not affect the original. The built instructuredClonedoes this, whileJSON.parse(JSON.stringify(obj))loses functions,undefinedand dates.Follow-up question: What can
structuredClonenot copy?- Course: JavaScript Essentials
6. How does an object differ from an array in JavaScript?
Answer
An object is a set of named fields, for example
user.name. An array is an ordered list where you get an element by its number, and the first one has number zero. On the backend you most often see an array of objects, such as a list of orders. Technically an array is also an object, but with special behavior for indexes and thelengthproperty.Follow-up question: How do you get the
totalfield of the second element of theordersarray?- Course: JavaScript Essentials
- Knowledge base: JavaScript
- Source: developer.mozilla.org
7. Given
const a = { n: 1 }; const b = a; b.n = 2;, what doesconsole.log(a.n)print and why?Answer
It prints 2. The line
const b = adoes not copy the object, it gives one more reference to the same object. So a change throughbis visible througha. Primitives (numbers, strings, booleans) are copied by value, while objects and arrays are passed by reference. To get an independent copy you need spread orstructuredClone.Follow-up question: How do you make an independent copy of a nested object?
- Course: JavaScript Essentials
- Knowledge base: JavaScript
8. What is a callback in simple words? Give an example.
Answer
A callback is a function I pass to another function so that it calls it itself at the right moment. For example,
onClickcalls my function on a click, andmapcalls it for each array element. Asynchronous APIs work the same way: I do not wait for the result, I give a function that will run when it is ready.Follow-up question: How many times will
mapcall the callback for an array of three elements?- Course: JavaScript Essentials
- Knowledge base: JavaScript
- Source: developer.mozilla.org
9. The code prints
A, then schedulessetTimeout(() => console.log('B'), 0), then printsC. In what order do the letters appear and why?Answer
It prints A, then C, then B. First all the synchronous code of the current call runs. The
setTimeoutcallback goes to a queue and runs only when the stack is empty, even if the delay is zero. So zero insetTimeoutmeans 'as soon as possible after the current code', not 'immediately'.Follow-up question: Does
setTimeoutwith zero guarantee that the function runs exactly instantly?- Course: JavaScript Essentials
- Knowledge base: JavaScript
10. An async function
getUserreturns the string'Olya'. What doesconsole.log(getUser())print in Node.js and how do you get the string itself?Answer
It prints
Promise { 'Olya' }, because an async function always returns a promise, even if there is a plainreturninside. To get the string you writeawait getUser()inside an async function or use.then. A forgottenawaitis one of the most common mistakes: the code compiles but then works with a promise instead of data.Follow-up question: What does the word
awaitdo to the rest of the program: does it stop it or not?- Course: JavaScript Essentials
- Knowledge base: JavaScript
- Source: developer.mozilla.org
11. What do
typeof nullandtypeof NaNreturn? How doisNaNandNumber.isNaNdiffer, and how do you filter out an invalid number from a query parameter?Answer
typeof nullgives'object': it is a historical mistake of the first implementation that cannot be fixed, so an object is checked asvalue !== null && typeof value === 'object'.typeof NaNgives'number'. The globalisNaN('abc')first converts the argument to a number and returnstrue, whileNumber.isNaNdoes no conversion and is true only forNaNitself. For a query parameter preferNumber.isFinite(Number(x)), remembering thatNumber('')gives 0 andparseInt('42px')gives 42.Follow-up question: Why does
[NaN].includes(NaN)givetruewhileindexOfreturns -1?- Course: JavaScript Essentials
- Knowledge base: JavaScript
- Source: developer.mozilla.org
Middle questions
1. How does the event loop work? How do microtasks differ from macrotasks?
Answer
JavaScript runs code on a single thread through the call stack, while asynchronous callbacks wait in queues. After each macrotask (a script,
setTimeout, an event) the event loop drains the whole microtask queue, and only then takes the next macrotask. Microtasks arePromise.thencallbacks,awaitcontinuations andqueueMicrotask. That is whyPromise.resolve().then(...)runs beforesetTimeout(..., 0).Follow-up question: What happens if a microtask keeps scheduling another microtask?
- Course: JavaScript Essentials
2. What determines the value of
thisinside a function?Answer
In a regular function
thisdepends on how it is called: forobj.method()it isobj, for a plain call it isundefinedin strict mode or the global object without it.call,applyandbindsetthisexplicitly, andnewbinds it to the newly created object. An arrow function has nothisof its own and takes it from the enclosing scope. That is why a method passed as a callback often loses its context.Follow-up question: How do you keep
thiswhen passing a class method toaddEventListener?- Course: JavaScript Essentials
3. How do promises relate to
async/await? How do you handle errors and parallel requests?Answer
async/awaitis syntax on top of promises: anasyncfunction always returns a promise, andawaitwaits for a result without nestedthencalls. Errors are caught withtry/catcharoundawaitor with.catchin a chain. Consecutiveawaitcalls run one after another, so independent requests are started together withPromise.all. If you need the results even when some fail, usePromise.allSettled.Follow-up question: How does
Promise.racediffer fromPromise.any?- Course: JavaScript Essentials
4. A function has
console.log(x)and below itlet x = 'inner', while the outerxequals'outer'. What does the call print and why is the outer value not used?Answer
You get
ReferenceError: Cannot access 'x' before initialization. The innerlet xis registered in the creation phase of the function context and shadows the outerxfor the whole block, not only after its line. From the start of the block to the initialization line the variable is in the temporal dead zone, and any access throws. That is whytypeofof an undeclared variable is safe, whiletypeofof a variable in the TDZ also throws.Follow-up question: Do default parameters have a TDZ? What happens with
function f(a = b, b = 1) {}when you callf()?- Knowledge base: JavaScript
- Source: developer.mozilla.org
5. A
for (var i = 0; i < 3; i++)loop schedules asetTimeoutthat printsi. What does it print, and what does a similar loop withletin the header print? Why does aletdeclared outside the loop behave likevar?Answer
With
varyou get 3, 3, 3, because there is one variable for the whole function and the callbacks fire after the loop has finished. Withletin the header you get 0, 1, 2: the specification creates a new binding for each iteration. Iflet kis declared outsidefor, there is one binding again and the result is likevar. A closure captures the variable, not its value at the moment of creation.Follow-up question: How do you fix the
varversion withoutlet?- Knowledge base: JavaScript
- Source: tc39.es
6. Two arrow functions are created in one function: one returns
x, thenx = 2, the other also returnsx. What does each return? And where is a name looked up: where the function is written or where it is called?Answer
Both return 2, because they close over the same variable
x, not its value at creation time. A closure holds a reference to the lexical environment, not a snapshot of values. Names are resolved lexically: the environment where the function was created is stored in an internal slot, and calling it from elsewhere does not change that. So a variable of the calling function is not visible to the inner function, because JavaScript has no dynamic scope.Follow-up question: How do
evalandwithbreak lexical scope, and why does the engine give up optimizations because of them?- Course: JavaScript Essentials
- Knowledge base: JavaScript
- Source: developer.mozilla.org
7. A class has a method
key(id)that readsthis.prefix. Why doesids.map(service.key)fail with a TypeError, whileids.map((id) => service.key(id))works?Answer
thisis determined by how a function is called, not where it is declared. The expressionservice.key(id)is a call through an object, sothisequalsservice. Butservice.keywithout parentheses just reads a function that remembers nothing aboutservice, andmapcalls it as a plain function. A class body is always strict, sothisthere isundefined, and readingprefixthrows a TypeError. Fixes:bind, an arrow wrapper, or the secondthisArgargument ofmap.Follow-up question: Why does autobind in the constructor solve this for the whole class, and what does it cost?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
8. How does an arrow function differ from a regular one with respect to
this? What happens tothisin a regular function insidesetTimeoutin Node.js, and in a method destructured from an object?Answer
An arrow function has no
thisof its own, it takes it from the surrounding scope where it is written. In CommonJS at the top level that ismodule.exports, in ESM it isundefined. A method pulled out by destructuring is called without an object: in sloppy modethisisglobalThis, in strict modeundefined. Node calls asetTimeoutcallback withthisequal to theTimeoutobject, in a browser it would bewindow. An arrow inside a method takes thethisof that method.Follow-up question: Why can an arrow not be used as a constructor, and what happens with
newon an arrow?- Knowledge base: JavaScript
- Source: developer.mozilla.org
10. How does
prototypediffer from__proto__? Which two prototype chains doesclass Dog extends Animalbuild?Answer
Every object has an internal slot
[[Prototype]].__proto__is a deprecated accessor onObject.prototypethat reads it, the modern way isObject.getPrototypeOf.prototypeis a regular property of a constructor function: the object thatnewwill put into[[Prototype]]of a new instance.extendsbuilds two chains: the instance chain (d,Dog.prototype,Animal.prototype,Object.prototype) and the constructor chain (Dog,Animal), so that static methods work. Class methods are not own properties of the instance:insees them,Object.hasOwndoes not.Follow-up question: What does
Object.setPrototypeOfdo to an existing object, and why does MDN advise avoiding it?- Knowledge base: JavaScript
- Source: developer.mozilla.org
11. They say
classis syntactic sugar over constructor functions. What differences between them still exist?Answer
The mechanics are similar: both a class and a function have a
prototypewhere methods go. But class methods are non-enumerable, whileFn.prototype.m = ...creates an enumerable property, sofor...inover an old-style instance also lists inherited methods. A function can be called withoutnew, andthisbecomesglobalThisin sloppy mode, while a class withoutnewthrows a TypeError. A class body is always strict, and a class is not hoisted like a function declaration (it sits in the TDZ).Follow-up question: Why did inheriting from
Errorthrough functions breakinstanceofandstackbefore ES2015?- Knowledge base: JavaScript
- Source: developer.mozilla.org
12. An async function prints
a1, doesawait null, printsa2. Right after calling it there isPromise.resolve().then(p1).then(p2)and a synchronousconsole.log('sync'). In what order does everything print, and how many microtask steps doesawaitcost?Answer
Order: a1, sync, a2, p1, p2 (the continuation of the async function is queued before
p1). The code of an async function up to the firstawaitruns synchronously, soa1is first. Eachawaitof a plain value puts the continuation into the microtask queue and costs one step. The followingthenhandlers are registered only after the previous ones run, so the async continuation and thethenchain interleave.Follow-up question: How does the order change if you write
await new Promise((r) => r())instead ofawait null?- Knowledge base: JavaScript
- Source: v8.dev
13. In Node.js there are two
setTimeoutcalls with zero delay. The first schedulesPromise.thenandqueueMicrotask, the secondPromise.thenandprocess.nextTick. Do the microtasks of the first timer run before the second timer? What goes first:nextTickor a promise?Answer
Yes, the microtasks of the first timer run before the second timer. Since Node 11, after each individual timer callback Node drains the
process.nextTickqueue first, then the microtask queue, like a browser after each task.queueMicrotaskandPromise.thenshare one FIFO queue. ThenextTickqueue is separate and is processed before promise microtasks, so in the second timernextTickruns beforethen, even though the promise was created first.Follow-up question: What happens to timers if a microtask recursively schedules itself through
queueMicrotask?- Knowledge base: JavaScript, Node.js
- Source: nodejs.org
14. A chain: a
thenwithoutreturn, then athenthat throws, then anotherthen, then acatchthat returns 42, then athenand afinally. What happens at each step?Answer
Every
thenreturns a new promise, and its state is set by the callback. Withoutreturnthe next step getsundefined: this is the most common mistake in chains. Athrowin a callback makes the next promise rejected, and athenwithoutonRejectedpasses it on, so the intermediatethendoes not run. Acatchthat returns a value restores the chain to fulfilled, and the nextthengets 42.finallyreceives no value and does not change it unless it throws itself.Follow-up question: What changes if
catchis placed first in the chain?- Knowledge base: JavaScript
- Source: developer.mozilla.org
15. How do
Promise.all,allSettled,raceandanydiffer? What backend tasks do you use each for, and what happens to the remaining operations afterallrejects?Answer
allrejects on the first error,raceon the first settlement of any kind,anywaits for the first success (if all fail, it rejects withAggregateError), andallSettledwaits for all and never rejects. Typical use:allfor dependent requests,allSettledwhen partial results are needed,anyfor mirrors,racefor timeouts. Importantly, after rejection the remaining operations are not cancelled: requests and SQL continue and hold connections. Cancellation is done separately throughAbortController.Follow-up question: How do you write a request timeout so that the request really stops and the connection returns to the pool?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
16. An endpoint enriches 2000 orders with data from an external service. The version with
awaitin aforloop takes 40 seconds, the version withPromise.allgets 429 and a DB pool timeout. What is wrong with both, and how do you do it right?Answer
The sequential version waits for each step: 2000 times 20 ms is 40 seconds.
Promise.allstarts all 2000 operations at once: the service rate limits requests, and a pool of 10 connections is clogged by a queue. You need bounded concurrency: a worker pool,p-limitor batches of 10-20. Also reduce the number of calls: unique ids, a batch endpoint, oneINSERTper batch. Partial errors are collected withallSettledinstead of failing the whole request.Follow-up question: How do you write
mapLimit(items, limit, fn)without libraries?- Knowledge base: JavaScript, Node.js
- Source: developer.mozilla.org
17. When does the body of a generator start running? What happens if a
for...ofloop over a generator is interrupted withbreak?Answer
Calling a generator function does not run the body, it starts on the first
next(). After that the generator runs only up to the nextyield, so the computation is lazy and alternates with the consumer.breakinfor...ofcalls the iterator'sreturn(): the generator is closed and yields nothing more, and code after the currentyielddoes not run. Onlyfinallyblocks run, so cleanup of resources in a generator is written there.Follow-up question: What changes if the loop in the generator is wrapped in
try/finallywith acleanuplog?- Knowledge base: JavaScript
- Source: developer.mozilla.org
18. How does an iterable differ from an iterator? Why can your own
rangecollection be spread twice while a generator can be used only once?Answer
An iterable has a
Symbol.iteratormethod that returns an iterator. An iterator has anext()method that returns{ value, done }. Arangecollection creates a new iterator on every call ofSymbol.iterator, so it can be traversed many times. A generator returns itself fromSymbol.iterator, so it is single use. Consequence: into a function that may walk the data twice you pass an iterable (or an array), not an already created generator.Follow-up question: How do you make a
Paginatedclass so thatfor await...ofover it loads the next API pages?- Knowledge base: JavaScript
- Source: developer.mozilla.org
19. Why is
0.1 + 0.2not equal to0.3, and why does1.005round to 1? How would you handle money in a payment service?Answer
Numbers in JavaScript are IEEE 754 doubles, and 0.1 has no exact binary representation, so the sum gives 0.30000000000000004. Fractions are compared through a difference with a tolerance, and
Number.EPSILONonly suits values near one.1.005 * 100gives 100.49999999999999, soMath.roundgives 1 andtoFixeddoes not help. Money is kept in whole minor units (cents), inBigIntor in a decimal library, and in Postgres you usenumeric.Follow-up question: The driver returns
numericas a string. How do you carry the amount to JSON with no float at any step?- Knowledge base: JavaScript
- Source: developer.mozilla.org
20. A frontend receives ids from a Postgres
bigintin JSON. What bugs can this cause and how do you avoid them?Answer
JSON.parseturns a number into a double: 9007199254740993 silently becomes 9007199254740992, with no error. The limit of safe integers isNumber.MAX_SAFE_INTEGER(9007199254740991), above it neighboring integers are indistinguishable.BigIntis exact, butJSON.stringifycannot handle it and throws a TypeError. So bigint ids are sent in JSON as strings; thepgdriver returnsint8as a string by default, and you should not blindly parse it into a number.Follow-up question: How do you verify at the API contract level that no client receives a bigint as a JSON number?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
21. Why does
[] == falsegivetrueandnull == 0givefalse, althoughnull >= 0givestrue? How does the==algorithm work?Answer
If one operand is an object and the other a primitive,
ToPrimitiveis called first:[]becomes the string''. A boolean always becomes a number first, sofalseis 0 and''also becomes 0, hencetrue.null == xis true only fornullandundefined, while the>=operator convertsnullto 0, sonull >= 0is true.NaNis not equal to anything, even itself.Object.isdistinguishesNaNand-0, unlike===.Follow-up question: Why does
[NaN].includes(NaN)givetruewhileindexOfreturns -1?- Knowledge base: JavaScript
- Source: tc39.es
22. What does a copy via
JSON.parse(JSON.stringify(x))lose, what canstructuredClonenot do, and what would you use to copy a DTO before mutation?Answer
A JSON copy turns
Dateinto a string,Mapinto{}, dropsundefined, and turnsNaNintonull.structuredClonekeepsDate,Map,Set,NaN,undefinedand circular references. But it does not copy functions (it throwsDataCloneError) and does not preserve class prototypes, getters and private fields. Spread is only a shallow copy: nested objects stay shared. For a plain data DTOstructuredCloneis usually the best choice.Follow-up question: What happens to an instance of class
Moneywith a private field#centsafterstructuredClone?- Knowledge base: JavaScript
- Source: developer.mozilla.org
23. A default config is frozen with
Object.freeze, a service makes a copy with spread and changes the nestedtimeout. Why did another service suddenly get a changed timeout?Answer
Spread copies only the first level, so the nested object in the copy and in the original is the same object.
Object.freezeis also shallow: the nested object is not frozen, so the change goes through. In strict mode writing to a frozen property throws a TypeError, in sloppy mode it is silently ignored. Fixes: deep freeze, a factory that returns a new object each time, orstructuredCloneat creation.Follow-up question: How does
Object.freezediffer fromObject.sealandObject.preventExtensions?- Knowledge base: JavaScript
- Source: developer.mozilla.org
24. A client complains that in a JSON response the key
'2026'comes before'total', although the server order was different. In what order does JavaScript enumerate object keys?Answer
First come keys that are array indexes (integers from 0 to 2^32 minus 2), in ascending order. Then string keys in insertion order, and
'-1'or'01'are not indexes, so they are ordinary strings. Symbols come last, andObject.keysandJSON.stringifydo not show them. If order matters for arbitrary keys, use aMap: it keeps insertion order for any keys, including numbers.Follow-up question: How does V8 store indexed properties separately from named ones, and why is an array with holes slower?
- Knowledge base: JavaScript
- Source: tc39.es
25. How does
debouncework, and why does an implementation that does not keep the timer in a closure fail? How does debounce differ from throttle?Answer
Debounce delays a call until an interval has passed with no new calls. A correct implementation keeps
timerin a closure and callsclearTimeout(timer)before the newsetTimeout. Without cancelling, every call sets its own timer, and saving runs for every keystroke. Throttle instead calls the function no more than once per interval. In production you also passthisand the arguments of the last call, and addcancel,flushand leading or trailing modes.Follow-up question: How do you implement throttle so that the last call of a series is not lost (trailing)?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
26. In a config
retries: 0means 'do not retry', but the service makes 3 attempts. How does??differ from||, and where does?.help and where does it hide an error?Answer
||substitutes the default for any falsy value: 0,'',false,NaN.??substitutes only fornullandundefined, soretries ?? 3keeps 0.?.short-circuits the whole chain if the left side isnullorundefinedand returnsundefinedwithout a TypeError.obj.method?.()will not call the method if it is missing, but throws a TypeError if it is not a function. Excessive?.hides broken invariants: it is better to fail early than to carryundefinedfurther.Follow-up question: How does
??=differ from||=in the example of lazy cache initialization?- Knowledge base: JavaScript
- Source: developer.mozilla.org
27. A client sends
{ offset: null, sort: '' }, and the destructuring has default values. Why did the defaults not apply and the database query fail?Answer
A default in destructuring and function parameters applies only to
undefined, whilenulland''pass through as they are.= {}for the whole parameter protects against a call without arguments, but not againstnull: destructuringnullthrows a TypeError. JSON has noundefined: a missing key givesundefined, whilenullarrives asnull. So defaults and normalization are done at the system boundary: with a validation schema or an explicit??.Follow-up question: Why does
@IsOptional()in class-validator letnullthrough, and how does that affect PATCH wherenullmeans 'clear the field'?- Knowledge base: JavaScript
- Source: developer.mozilla.org
28. How do you correctly wrap errors without losing the root cause? Why does a custom error class show
nameequal toError, and what will a log show afterJSON.stringify(error)?Answer
new Error(message, { cause })keeps the root cause, and the chain is not lost when wrapping. A subclass withoutthis.name = ...inheritsname = 'Error'fromError.prototype, althoughinstanceofworks.message,stackandcauseare not enumerable, soJSON.stringifyyields only own enumerable fields, most often{}. A logger (pino, console) must serialize the error explicitly, recursively together withcause.Follow-up question: How do you tell an expected domain error from a bug in an error filter so the stack is not sent to the client?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
29. A reducer returns a new object via spread, but changes 'leak' into the old state. Where does the mutation usually hide?
Answer
sort()sorts the array in place and returns the same array, so the old state is changed. Also,sortwithout a comparator compares strings:[10, 9, 1]becomes[1, 10, 9]. Spreading the outer object does not protect the nested object: a write tos.user.nameis visible in the old state too. Fixes:toSorted,toReversed,with, copying every level that changes, or Immer.Follow-up question: What is structural sharing and why is copy-on-write cheaper than a deep copy on every change?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
Senior questions
1. What happens in the engine when a function is called? What is in an execution context, how does the creation phase differ from the execution phase, and how is this related to the call stack?
Answer
A call creates an execution context and puts it on top of the context stack (the call stack). The context contains
LexicalEnvironment,VariableEnvironment, thethisbinding, the function itself and the realm. In the creation phase the parameter bindings appear,varwith the valueundefined, function declarations in full, andlet,constandclassin the TDZ. In the execution phase the code runs line by line, and afterreturnthe context is popped off the stack. A function's environment has anouterreference to the environment where the function was written: this is the basis of closures.Follow-up question: How does
LexicalEnvironmentdiffer fromVariableEnvironmentin the example of a block withletinside a function?- Knowledge base: JavaScript
- Source: tc39.es
2. The code holds only one
theThing, butheapUsedgrows to hundreds of megabytes, although the helper functionunusedis never called. Why does this happen with closures, and how do you find it in a heap snapshot?Answer
V8 creates one
Contextobject per scope, shared by all closures that capture something from it. Themethodclosure captures the sameContextasunused, and it holdsprev(the previoustheThing) andbig. A chain forms: every newtheThingholds the old one throughprev, and that one holds its ownbig. In a heap snapshot you see this in Retainers:contextandprev. Fix: null outprevbefore leaving the scope or moveunusedinto a separate scope.Follow-up question: Why would
WeakReforWeakMapnot save this case, and what exactly should be a weak reference here?- Knowledge base: JavaScript, Node.js
- Source: v8.dev
3. Rank the
thisbinding rules by priority. What happens withnewon a bound function and with repeatedbindorcall? How does this change in strict mode?Answer
Priority from highest:
new, then explicit binding (call,apply,bind), then a call through an object, then default binding. Repeatedbindandcalldo not changethisof an already bound function: the firstbindwins.newhas priority even overbind: a new object is created. In sloppy mode a primitivethisis wrapped into an object, andnullandundefinedare replaced withglobalThis. In strict modethisis passed as is.Follow-up question: What is
new.targetfor in base classes, and how does it behave with a bound function?- Knowledge base: JavaScript
- Source: tc39.es
4. An endpoint merges the request body into a settings object with a recursive function, and after one request all users got admin rights. How does prototype pollution work and how do you close it?
Answer
JSON.parsecreates an own property with the key__proto__, andfor...inwalks it. Inmerge, the expressiontarget['__proto__']returnsObject.prototypethrough the accessor, so the function writes into the global prototype. After that all objects inherit, for example,isAdmin: true. Defense: skip the keys__proto__,constructor,prototype, useObject.create(null)orMap, validate the body with a schema, and if needed disable__proto__at the Node process level.Follow-up question: Why is changing
Object.prototypedangerous for libraries that read options from a plain empty object?- Knowledge base: JavaScript, Node.js
- Source: nodejs.org
5. An async function does
return Promise.resolve('r'), while otherthenchains run alongside. Why isrprinted later than expected, although the promise is already fulfilled?Answer
Returning a plain value from an async function fulfills its promise immediately. Returning a promise calls
resolvewith a thenable: the specification queuesNewPromiseResolveThenableJob, which callsthenon the inner promise. This costs two extra microtask queue steps, so the result arrives later than in neighboring chains. The same applies toresolve(promise)innew Promiseand to returning a promise fromthen.Follow-up question: If you replace
return Promise.resolve('r')withreturn await Promise.resolve('r'), where willrbe and why?- Knowledge base: JavaScript
- Source: tc39.es
6. A colleague says an import loop does not block the server because it has
await. But during the import the health check times out. Why, and how do you fix it?Answer
awaitof an already ready value continues through a microtask, and the microtask queue is drained before any macrotask. So such a loop never yields control to the event loop: timers and I/O wait until the whole loop finishes. The event loop does not reach the timers and poll phases, so the health check and other requests stand still. Fix: periodically yield throughsetImmediateorscheduler.yield, split the work into batches or move it to a worker.process.nextTickwould not help here.Follow-up question: Why would
process.nextTickinstead ofsetImmediatechange nothing?- Knowledge base: JavaScript, Node.js
- Source: nodejs.org
7. How does the browser event loop differ from the Node.js event loop? What does the language define and what does the environment define?
Answer
ECMAScript defines only jobs (microtasks) for promises, while the loop itself is set by the environment: the HTML specification for the browser and libuv for Node. In a browser it is one task, then a microtask checkpoint, then a possible render with
requestAnimationFrame. Node has the phases timers, pending, poll, check, close, and microtasks after each callback.process.nextTickandsetImmediateexist only in Node, andnextTickruns ahead of promise microtasks. Consequence: endless microtasks block both rendering and I/O.Follow-up question: How do you measure in Node that the event loop is lagging, and what lag value is already alarming?
- Knowledge base: JavaScript, Node.js
- Source: developer.mozilla.org
8. Two async functions both have
try/catch, one doesreturn fail(), the otherreturn await fail(). Why does only the second catch the error, and when isreturn awaitmandatory?Answer
return fail()returns a promise, andtryfinishes before the promise rejects, socatchdoes not see the error.return awaitwaits for the result insidetry, andcatchfires. The rule:return awaitis mandatory insidetry/catchandtry/finally, and it also keeps the function in the async stack trace. Returning a promise withoutawaitalso costs extra queue steps.Follow-up question: What does the same mistake look like with
finallythat callsclient.release()for a connection pool?- Knowledge base: JavaScript
- Source: developer.mozilla.org
9. Why use a
WeakMapin a metadata cache instead of aMap? Why can a string not be a key, and when areWeakRefandFinalizationRegistryappropriate?Answer
A
WeakMapkey is compared by object identity and does not keep the object alive: when the object is no longer reachable anywhere else, the entry disappears with it. Keys can only be objects and non-registered symbols, a primitive throws a TypeError. AWeakMapis not iterable and has nosize.WeakRefandFinalizationRegistryare non-deterministic: the callback may never be called at all, so an explicitcloseis needed to free resources. A cache will not help if the value refers back to the key: then the object stays reachable.Follow-up question: Why would a
WeakMapcache not help if the cache value refers back to the same object?- Knowledge base: JavaScript
- Source: developer.mozilla.org
10. An index by id from 20,000 rows is built with
rows.reduce((acc, r) => ({ ...acc, [r.id]: r }), {})and takes hundreds of milliseconds, while a simple loop is almost instant. Why, and how do you fix it without giving up purity?Answer
Each iteration copies all accumulated keys, so the total is O(n²): for 20,000 rows that is about 200 million property copies. In addition thousands of intermediate objects appear for the GC. The event loop is blocked for the whole time, and the p99 of all other requests of the process suffers. Mutating a local accumulator inside a function does not break purity, because there is no outside effect. So prefer a loop, a
MaporObject.fromEntries.Follow-up question: How do you find such a hot
reducein production: which Node tools would you use to profile the CPU?- Knowledge base: JavaScript, Node.js
- Source: developer.mozilla.org
11. A memoize built on
Mapdoes not hit for identical object arguments and returns a stale value after the object is mutated. How do you make memoize for object arguments correctly?Answer
Mapcompares objects by reference, so two literals{ qty: 2 }are two different keys. And mutating a key object does not change the cache entry, so the function returns the old result. The key is made from a stable primitive (an id, a hash), canonical JSON with sorted keys, or you use aWeakMapby identity for immutable objects. The cache must be bounded (LRU, TTL) and the function pure, otherwise memoize causes a memory leak and wrong data.Follow-up question: How do you cache a promise so that an error does not get stuck in the cache and concurrent requests merge into one?
- Knowledge base: JavaScript
- Source: developer.mozilla.org
12. An async generator takes a connection from the pool and yields cursor rows, and under load the pool is exhausted. Where does the connection leak and how do you fix it?
Answer
finallyin a generator runs only when it finishes: ondone,throwor a call toreturn(). If the consumer takes values with a manualnext()and abandons the iterator, the generator stays suspended forever and the connection is never returned. Fix: consume withfor await, and with manual calls always callreturn()in the consumer'sfinally, or useawait usingwithSymbol.asyncDispose. As a safety net you need a pool idle timeout and a metric of busy connections.Follow-up question: How do you write a wrapper
take(asyncIterable, n)that is guaranteed to close the source?- Knowledge base: JavaScript, Node.js
- Source: tc39.es
Train these questions
Study the cards, answer by text or voice and get an AI grade. Needs a signed in account.
Answer out loud and get graded
Here you get the questions with model answers. In the ShortLesson app you answer yourself, by text or voice, and AI grades the answer against the rubric and asks a follow-up, like a real interviewer.
The app is coming to the stores soon.
In the knowledge base
Reference pages for the technologies on this topic: who maintains them, when to use them and what interviews ask.
Other topics
- TypeScript interview questions and answers
- React interview questions and answers
- Frontend performance interview questions and answers
- Node.js interview questions and answers
- HTTP interview questions and answers
- SQL interview questions and answers
- Databases interview questions and answers
- Git interview questions and answers
- Algorithms interview questions and answers
- System design interview questions and answers
- DevOps interview questions and answers
- Web security interview questions and answers
- Testing interview questions and answers