JavaScript
JavaScript is the language of the browser and, via Node, a large share of backend work too. Interviews lean on its distinctive runtime semantics — dynamic types and coercion, closures, prototypes, the single-threaded event loop, and module execution — because those are where day-to-day bugs actually come from.
on this pageshowhide
guide
overview
~2 minJavaScript interviews test whether you understand the runtime underneath the syntax. Rounds usually open on scope: which binding a name resolves to, why a declaration can be visible above its own line, and what a closure keeps alive after its function returns. From there the conversation moves to the object model, where `class` is a thin layer over objects delegating to other objects, and to the scheduler, where a short snippet and the question "what order does this log in?" separates people who have debugged asynchronous code from people who have only written it. Coercion questions check whether you can predict a conversion from the rules rather than recite a list of surprises. Practical rounds test fluency with collections, errors and the everyday built-ins, and senior rounds drift toward modules: how a module graph is loaded, what a bundler can safely remove, and what breaks when ES modules and CommonJS meet. The hub follows those seams. The language core is [Types and Coercion](/topics/lang-javascript-types-coercion), [Scope and Closures](/topics/lang-javascript-scope-closures) and [Prototypes and Inheritance](/topics/lang-javascript-prototypes). Everyday code lives in [Functions, Iterables and Collections](/topics/lang-javascript-functions-collections) and [Errors and Built-in Objects](/topics/lang-javascript-errors-builtins). Asynchrony is split in two: [Event Loop and Concurrency](/topics/lang-javascript-event-loop) is the scheduler, and [Promises and Async/Await](/topics/lang-javascript-promises-async) is the API you program it with. [Modules and Bundling](/topics/lang-javascript-modules) covers how separate files become one program, and [DOM and Browser APIs](/topics/lang-javascript-dom-browser) is the narrow edge where the language meets the page. Learn it roughly in that order. Types and scope come first, because nearly every later answer assumes you can say what a value is and which variable a name points at. Functions and collections next, since they are most of the code you write in a live exercise, then prototypes, which explain what a method call does and what `class` actually builds. Take the event loop before promises: the promise questions that feel tricky are usually scheduling questions in disguise. Modules and the DOM weigh most when the role names them: front-end work leans on the browser and bundling, Node work on module loading and interop.
primer
### A value has a type; a variable does not Any binding can hold any primitive or object, and the engine converts between kinds whenever an operator needs a different one — not at random, but by a fixed algorithm for each kind it asks for. An interviewer who shows `[] + {}` is checking whether you can run that algorithm in your head, and whether you know when strict comparison or an explicit conversion is the better tool. Numbers add a second layer: an ordinary number is a binary double, with the precision and integer-range limits that implies. ### Names resolve by where code is written; `this` by how it is called Scope is lexical: the nesting of functions and blocks in the source decides which binding a name refers to, and a scope's bindings are created when it is entered, before any of its lines run. That one fact explains hoisting and the temporal dead zone. `this` follows a different rule: a normal function gets it at each call, from how the call is made. An arrow function has none of its own and looks outward for it like any other name. ### A closure keeps variables, not snapshots A function holds a reference to the scope it was created in, so it can read and change those variables long after the outer call has returned. Because it keeps the variable itself, two closures made in the same scope share state, and callbacks created in a loop that reuses one binding all see its latest value. Private state, memoization and partial application all rest on this. ### Objects delegate to other objects Every object has a link to a prototype, and a property lookup that misses on the object continues along that chain. Methods live once on a shared prototype; `new` and `class` are ways of creating objects wired to the right one. Reads and writes are not symmetric, since lookup follows the chain and assignment normally does not. Descriptors, getters and setters, and `Proxy` are the tools for changing what a property access does. ### One thread, one stack, and queues Within a page, a worker or a Node process, your code runs one callback at a time, each to completion, on a single call stack. Everything asynchronous — timers, I/O, user events, promise reactions — is a callback waiting in a queue until the stack is empty. There are two tiers: tasks, taken one per turn of the loop, and microtasks, emptied completely after each task. Ordering puzzles, a frozen page and a late timer are all consequences of that design. ### A promise is a value for a result that is not ready yet Promises turn callbacks into values you can return, chain and combine, and `async`/`await` is syntax for writing that chain as straight-line code. The interview weight is on failure and timing: each step of a chain yields a fresh promise, a rejection travels down the chain until something handles it, and a forgotten `return` or `await` quietly detaches work from the code that should wait for it. ### Modules are linked before they run ES modules declare imports and exports statically, so the whole graph can be found, loaded and wired together before any module body executes. That static shape lets bundlers drop unused exports, and it is why an import stays connected to the exporting module's variable instead of freezing its value. CommonJS works the other way: `require` is an ordinary function call that executes a file and returns an object. Most interop and circular-dependency questions come from the collision of those two models. ### The language is not the platform ECMAScript defines the syntax and the built-ins: objects, arrays, promises, `JSON`, `Date`, regular expressions. The DOM, timers, `fetch` and Node's file system come from the host. Knowing which layer owns an API tells you where it exists and why code that works in one environment crashes in another. The built-ins have sharp edges worth knowing by name: lossy JSON, stateful regexes, months counted from zero.
- Primitive
- A value that is not an object: string, number, bigint, boolean, symbol, null or undefined. Primitives are immutable; calling a method on one uses a temporary wrapper object.
- Type coercion
- The engine's implicit conversion of a value to another type when an operator or context needs one, following fixed abstract operations such as ToPrimitive, ToNumber and ToString.
- Truthy and falsy
- Whether a value converts to true or false in a boolean context; a short fixed list of values is falsy, and everything else, empty arrays and objects included, is truthy.
- Hoisting
- The effect of creating a scope's bindings when the scope is entered, so declarations are known before their line runs; how usable they are before then depends on the declaration kind.
- Temporal dead zone
- The stretch between entering a scope and reaching a let, const or class declaration, during which the binding exists but any access to it throws a ReferenceError.
- Closure
- A function together with the scope it was created in, which keeps that scope's variables alive and shared for as long as the function itself is reachable.
- this binding
- The receiver a normal function sees, decided at each call by how it is invoked: as a method, with new, through call, apply or bind, or as a plain call.
- Arrow function
- A compact function form with no this, arguments or prototype of its own; it reads this from the enclosing scope and cannot be used as a constructor.
- Prototype chain
- The linked sequence of objects a property lookup walks when the property is not found on the object itself, ending at null.
- Property descriptor
- The record behind a property: its value or its getter and setter, plus the writable, enumerable and configurable flags that control how it can be changed and seen.
- Proxy
- A wrapper object whose handler traps intercept fundamental operations such as property reads, writes and deletes; Reflect supplies the default behaviour a trap can forward to.
- Microtask
- A job queued by a promise reaction, an await resumption or queueMicrotask; the whole queue is emptied after the current task, the macrotask, and before the next one starts.
- Promise
- An object standing for the eventual result of an asynchronous operation; it settles once, either fulfilled with a value or rejected with a reason.
- ES module
- A file loaded through import and export declarations; it runs in strict mode, has its own top-level scope, and is linked with its dependencies before it executes.
- Live binding
- An ES module import that shows the exporter's variable as it currently is, read-only on the importing side, rather than a copy taken at import time.
- CommonJS
- Node's original module format, in which require synchronously runs a file and returns its module.exports object; still common in published packages.
- Tree shaking
- A bundler's removal of code nothing imports, which depends on static import structure and on modules not doing needed work as a side effect of loading.
- Iterable
- Any object with a Symbol.iterator method that returns an iterator; the protocol behind for...of, spread, array destructuring and Array.from.
- Generator
- A function declared with function* that returns an iterator and pauses at each yield, resuming when the consumer asks for the next value.
- Event delegation
- Handling events for many child elements with one listener on a shared ancestor, relying on event bubbling and on the event's target to identify the source.
The sections stack. Types and scope define what a value is and which variable a name points at. Functions and collections sit directly on them: every array method takes a callback, and every callback is a closure over the code that wrote it. Prototypes supply the other half of a method call — where the method is found — while [The this Binding](/topics/lang-javascript-scope-closures-this-binding) decides which object it runs against. [The Prototype Chain](/topics/lang-javascript-prototypes-chain) and `this` are the two lookups inside every `obj.method()`, which is why interviewers often ask about them back to back. Asynchrony reuses all of it. A promise callback is a closure; `await` suspends a function and later resumes it with its scope intact; the order in which things resume is set by the [Microtask Queue](/topics/lang-javascript-event-loop-microtasks) and the task queue behind it. That is why [Output Ordering Puzzles](/topics/lang-javascript-event-loop-ordering-puzzles) sit under the event loop rather than under promises: the syntax is async, but the question is about the scheduler. Modules and the host form the outer ring. [ES Module Semantics](/topics/lang-javascript-modules-esm-semantics) decide when each file's top-level code runs and what its imports see; the [Browser vs Node Global Environment](/topics/lang-javascript-dom-browser-runtime-environments) decides which globals exist when it does. Errors cut across every layer: a rejected promise, a thrown `TypeError` and a failed `JSON.parse` are one error model reached from different places. A small example where several of these meet, run as an ES module so strict mode applies: ```javascript function makeCounter(label) { let count = 0; return { label, async tick() { count += 1; await null; return `${this.label}: ${count}`; }, }; } const counter = makeCounter('a'); const { tick } = counter; counter.tick().then(console.log); // "a: 2" tick().catch((err) => console.log(err.name)); // "TypeError" ``` Both calls share one `count`, because both closures hold the same binding, not a snapshot of it. Each async body executes without pausing until it reaches `await`, so both increments happen before either call resumes, and the first result reads 2. The destructured call has no receiver, so `this` is `undefined` in strict code; the `TypeError` that follows is not thrown at the call site but rejects that call's promise, and the handler receives it as a value.
- Types and Coercion →
Primitives, equality and conversion rules are the vocabulary the other sections' answers are written in; learn to predict a conversion first.
- Scope and Closures →
Hoisting, closures and this are where most rounds open, and where a wrong mental model shows up fastest.
- Functions, Iterables and Collections →
Callbacks, array methods, Map and Set, and iterators are the material of nearly every live coding exercise.
- Prototypes and Inheritance →
Explains what a method call and class syntax actually do; read it once closures and this feel routine.
- Event Loop and Concurrency →
Learn the call stack and the two queues before promises, so ordering questions become mechanical rather than memorised.
- Promises and Async/Await →
With the scheduler understood, chains, combinators and async/await become questions about error flow and concurrency.
Guessing the result of
==from intuition: loose equality follows a conversion algorithm, and Equality and Conversion questions are built to punish guesses.Assuming
constmakes data immutable: it fixes the binding only, so aconstobject or array can still be changed by any code holding a reference.Expecting a method to remember its object: passed as a callback it loses its receiver, and written as an arrow function it never had one.
Using
for...inover an array: it yields string keys and includes inherited enumerable properties;for...oforObject.keysusually says what you meant.Treating
setTimeout(fn, 0)as immediate: the callback waits for the running code and all pending microtasks, and a busy thread delays it further.Awaiting inside a loop when the calls are independent: each iteration waits for the previous one, turning concurrent work into summed latency.
Starting a promise without returning or awaiting it: a surrounding
try/catchcannot see its rejection, which surfaces later as unhandled.Using a
JSON.stringifyandJSON.parseround trip as a deep copy: dates,undefined, functions,Mapand cycles do not come back intact.Reading
windowordocumentat a module's top level: the file crashes as soon as server-side code imports it, before any function is called.
This guide assumes an engine implementing ES2022 or later, which current evergreen browsers and maintained Node releases all do. Editions are named by year since ES2015, and interviewers still ask which release brought a feature, mostly to learn whether you can read older code: - **ES5 (2009)** — strict mode, `JSON`, property descriptors and the array iteration methods such as `map` and `filter`. - **ES2015** — the large release: `let` and `const`, arrow functions, classes, promises, modules, template literals, destructuring, `Map` and `Set`, symbols, iterators and generators, `Proxy` and `Reflect`. - **ES2017** — `async` and `await`. - **ES2018** — async iteration, and rest and spread for object properties. - **ES2020** — optional chaining, nullish coalescing, `BigInt`, `globalThis`, dynamic `import()` and `Promise.allSettled`. - **ES2021** — logical assignment operators and `Promise.any`. - **ES2022** — top-level `await`, class fields and `#private` members, `Object.hasOwn`, `Array.prototype.at` and the `cause` option on errors. - **ES2023** — array methods that return a copy instead of mutating, such as `toSorted` and `toReversed`, plus `findLast`. `structuredClone` is missing from that list on purpose: it comes from the web platform and is provided by browsers and by Node rather than by the language, a handy example of the language/host split.
JavaScript is standardised as ECMAScript by Ecma International's TC39 committee and implemented by engines such as V8, SpiderMonkey and JavaScriptCore. Interviewers expect you to place it in a few settings. In the **browser** it is the language with direct access to the DOM; WebAssembly complements it for compute-heavy code rather than replacing it. On the **server**, Node is the established runtime, with Deno and Bun as newer alternatives that also run TypeScript directly. There it competes with Python, Go and Java: the event loop suits I/O-heavy services better than CPU-bound work, and the draw is one language on client and server. **TypeScript** is the most common companion: a typed superset whose types are checked at build time and then erased, so every runtime question in this hub applies unchanged to TypeScript code. Bundlers and build tools such as webpack, Rollup, esbuild and Vite resolve modules, transpile newer syntax and drop unused code, which is why module semantics come up in front-end interviews. Frameworks such as React, Vue and Angular sit on top, and a framework question often turns out to be a closure, reference-equality or event-loop question underneath.
explore
- Scope and Closures56 questions
- Declarations and Hoisting12 questions
- Execution Model8 questions
- Closures18 questions
- The this Binding18 questions
- Prototypes and Inheritance75 questions
- The Prototype Chain14 questions
- Constructor Functions and new13 questions
- Class Syntax20 questions
- Property Mechanics13 questions
- Proxy and Reflect15 questions
- Event Loop and Concurrency31 questions
- Call Stack and Run-to-Completion4 questions
- Macrotask Queue and Timers13 questions
- Microtask Queue9 questions
- Output Ordering Puzzles5 questions
- Promises and Async/Await71 questions
- Promise Fundamentals13 questions
- Promise Combinators12 questions
- Async/Await18 questions
- Async Iteration9 questions
- Async Patterns and Pitfalls19 questions
- Modules and Bundling49 questions
- ES Module Semantics17 questions
- Dynamic import() and Code Splitting5 questions
- Top-Level Await5 questions
- ESM and CommonJS Interop12 questions
- Circular Dependencies4 questions
- Side Effects and Tree Shaking6 questions
- DOM and Browser APIs14 questions
- DOM Selection and Manipulation5 questions
- Event Handling and Delegation4 questions
- Browser vs Node Global Environment5 questions
- Types and Coercion61 questions
- Primitives and Objects13 questions
- Equality and Conversion18 questions
- Numbers and Numeric Types15 questions
- Strings and Symbols15 questions
- Functions, Iterables and Collections75 questions
- Functions as Values17 questions
- Arrays and Array Methods26 questions
- Keyed Collections13 questions
- Iterators and Generators19 questions
- Errors and Built-in Objects69 questions
- Error Handling16 questions
- JSON10 questions
- Regular Expressions15 questions
- Dates and Internationalization18 questions
- globalThis and structuredClone5 questions
questions
501 · 9 sectionsGiven `function makeCounter() { let n = 0; return () => ++n; }` and `const a = makeCounter(), b = makeCounter();`, what does `a(); a(); console.log(a(), b());` print, and why do the two counters not interfere?
basics
~10 sIt prints 3 1. Every call to makeCounter creates a new environment with its own n, so the two returned functions close over two different bindings and count independently.
In JavaScript, `for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }` logs 3, 3, 3. Explain why, and give the smallest change that makes it log 0, 1, 2.
basics
~20 svar creates one function-scoped i shared by the whole loop, so all three deferred callbacks read the same binding, which already holds 3 by the time the timers fire. Declaring the counter with let gives each iteration its own i, logging 0, 1, 2.
In JavaScript, why is a function's local variable not freed when that function returns, if an inner function it returned still refers to the variable?
basics
~20 sA function object stores a hidden pointer to the scope it was created in, so returning it keeps that scope reachable. Reachable memory is never collected, so the variable lives as long as the returned function does.
In JavaScript, why does `function () { console.log('hi'); }();` throw a SyntaxError while `(function () { console.log('hi'); })();` runs, and what other forms make an immediately invoked function expression work?
basics
~20 sA statement that begins with the keyword function is parsed as a function declaration, which needs a name and cannot be invoked in place. Wrapping it in parentheses forces the parser to read it as an expression, and expressions are callable.
In JavaScript, why can you call a function written as `function greet() {}` from a line above its definition, while calling a function stored by `var sayHi = function () {}` above that assignment throws a TypeError?
basics
~20 sA function declaration is created whole, name and body together, when the scope is entered, so it is callable anywhere in that scope. A function expression only becomes a function when its assignment line runs; until then the variable holds undefined.
In JavaScript, how do `'toString' in obj` and `Object.hasOwn(obj, 'toString')` differ for a plain object literal, and when does that difference matter?
basics
~20 sThe in operator searches the whole prototype chain, so it reports true for inherited names such as toString. Object.hasOwn checks only the object's own properties and reports false. Use hasOwn whenever you mean data the object itself carries.
In JavaScript, what is the difference between a function's `.prototype` property and an object's `__proto__`?
basics
~20 sA function's .prototype is an ordinary property holding the object that instances created from that function will delegate to. An object's proto is that object's own delegation link, the object it actually inherits from right now.
In a JavaScript class, what is the difference between `super(...)` and `super.someMethod()`, and where is each one allowed to appear?
basics
~20 ssuper(...) invokes the parent constructor and is legal only inside the constructor of a class written with extends. super.someMethod() calls the parent's version of a method with the current instance as this, and is legal in any method or accessor.
In a JavaScript class body, what does declaring a field as #count = 0 give you that the older convention of naming it _count does not?
basics
~20 sA #-prefixed field is privacy enforced by the engine: it can only be named inside the class body that declares it, it is invisible to Object.keys and JSON.stringify, and touching it elsewhere is an error. An underscore name is only a hint.
In a JavaScript class, what does the `static` keyword do — which object does the member live on, and can you reach it from an instance?
basics
~20 sstatic installs a method or field on the class constructor itself instead of on instances. You call it as ClassName.member; reading it from an instance gives undefined, because the constructor is not on the instance's prototype chain.
JavaScript engines run your code on a single call stack. What gets pushed onto and popped off that stack, and why does that mean two of your functions can never be executing at the same instant?
basics
~20 sThe call stack holds one frame per function call that has started but not returned — its arguments, its locals, and the point to resume at. Calls push a frame, returns pop it, and because JavaScript has a single stack, only the top frame is ever running.
In JavaScript, if a promise is already fulfilled at the moment you call .then(callback) on it, does callback run immediately? Explain when it actually runs and what schedules it.
basics
~20 sNo. .then never calls back synchronously: it schedules a promise reaction job on the microtask queue. The callback runs after the currently executing script or task finishes, when the engine drains microtasks at its checkpoint.
What does this script print, and why? console.log('start'); setTimeout(() => console.log('timeout'), 0); Promise.resolve().then(() => console.log('promise')); console.log('end');
basics
~20 sIt prints start, end, promise, timeout. All the top-level synchronous code runs first, then the promise callback runs as a microtask once the script finishes, and the setTimeout callback runs last as a separate task.
What does setInterval return, and what happens if the code that started a repeating timer is torn down without ever calling clearInterval with that value?
basics
~20 ssetInterval returns a timer handle — a number in browsers, a Timeout object in Node — whose only use is to pass to clearInterval. Without that call the timer fires forever, keeping its callback and everything the callback closes over alive.
Does setTimeout(fn, 100) guarantee that fn runs exactly 100 ms later? What does the delay argument actually promise?
basics
~20 ssetTimeout's delay is a minimum wait, not an exact schedule. When it elapses the callback is only queued as a task; it runs after the currently executing code finishes and after any work already queued ahead of it, so it commonly fires late.
In JavaScript, what does calling an `async` function return, and how do a plain `return 42`, a `throw`, and a `return somePromise` inside its body each show up in that returned value?
basics
~20 sAn async function always returns a promise, never a raw value: return 42 fulfils that promise with 42, throw rejects it with the thrown error, and returning a promise makes the outer promise adopt that promise's eventual outcome.
In a JavaScript async function, how does wrapping an `await` in try/catch let you handle a rejected promise, and what exactly does `await` do when the promise it is waiting on rejects?
basics
~20 sawait rethrows a rejected promise's reason as an ordinary exception at the await point, so a normal try/catch around that await catches it. The catch parameter is bound to whatever value the promise rejected with, which need not be an Error.
In JavaScript, a function loops over 10 user IDs and does `const user = await fetchUser(id)` inside the loop body, where each call takes about 200 ms. How long does the loop take overall, why, and how would you make the ten requests run concurrently?
basics
~20 sAwaiting inside the loop serializes the requests, so ten 200 ms calls take about 2 seconds. Start all ten promises first and await them together with Promise.all, and the whole batch takes roughly 200 ms.
What is the difference between Promise.all and Promise.allSettled in JavaScript, and what does each of them fulfil with?
basics
~20 sPromise.all rejects the moment any input promise rejects, and otherwise fulfils with an array of the values in input order. Promise.allSettled waits for every input and always fulfils, giving one {status, value or reason} object per entry.
You run `await Promise.all(urls.map(url => fetch(url)))` over an array of 5,000 URLs. What actually happens, and why can Promise.all not limit how many requests are in flight?
basics
~20 sAll 5,000 requests start immediately: map calls fetch for every URL before Promise.all ever runs. Promise.all only observes promises that are already in flight, so any concurrency cap must be applied where the work is started.
In a CommonJS module, when you write `const cfg = require('./config')`, at what point does the code inside config.js actually run, and what value does require() hand back?
basics
~10 srequire() runs the target module's body synchronously at the call site and hands back whatever value module.exports holds when that body finishes. The calling file is blocked until the required module completes.
In JavaScript, what does the expression `import('./math.js')` evaluate to, and how does that differ from a static `import ... from './math.js'` declaration?
basics
~20 simport() is a syntactic form that starts loading a module at runtime and evaluates to a Promise for that module's namespace object. A static import declaration is resolved before any code in the file runs and binds the exported names directly.
In ES modules, what is the difference between a named export and a default export, and how does the import syntax differ for each?
basics
~20 sA named export binds a value to a specific export name that importers must match exactly, using braces: import { parse } from './m.js'. A default export registers the value under the name "default", and the importer may bind it to any identifier it likes.
In an ES module you write `var config = {}` and `function init() {}` at the top level. Are `globalThis.config` and `globalThis.init` defined afterwards, and how does that differ from the same two lines in a classic (non-module) script?
basics
~10 sNo. Top-level declarations in an ES module live in that module's own scope, so globalThis.config and globalThis.init stay undefined. In a classic script the same declarations become properties of the global object.
In an ES module `main.js` you write `console.log('main start');` and then, on the line below it, `import './dep.js';` — and `dep.js` logs `'dep evaluated'` at its top level. Which line prints first, and what rule decides that?
basics
~20 sThe dependency prints first. Import declarations are hoisted and the whole module graph is linked before anything is evaluated, so dep.js runs to completion before any statement in main.js — including a statement written above the import.
What is event delegation in the DOM, and why does a single delegated handler still work for list items that are added after the handler was attached?
basics
~20 sEvent delegation puts one listener on a container instead of one per child and identifies the child from the event object. Items added later are covered automatically, because the listener lives on the container, not on each child.
A script whose first line is `const el = document.body` runs fine in a browser tab but throws `ReferenceError: document is not defined` under `node script.js`. Why is the DOM missing in Node, and what globals does Node supply instead?
basics
~20 sThe DOM is not part of JavaScript. ECMAScript defines only the core language and built-ins such as Object, Array and Promise; document and window are supplied by the browser host. Node is a different host and supplies process, Buffer and its module globals instead.
Why does calling .map() on the result of document.querySelectorAll() throw, and how do you get array methods over the elements it returns?
basics
~20 sdocument.querySelectorAll returns a NodeList, not an Array. It is array-like and iterable and does have forEach, but it does not inherit from Array.prototype, so map, filter and reduce are missing. Convert with Array.from(nodes) or [...nodes].
What is the difference between setting an element's textContent and setting its innerHTML in the browser DOM, and when does that difference cause a bug?
basics
~20 stextContent treats the assigned string as literal text and shows it exactly as written; innerHTML runs it through the HTML parser and builds real nodes from it. Assigning untrusted data to innerHTML turns that data into page structure.
A <ul> has a click handler that does `if (event.target.tagName === 'LI') { ... }`, but each <li> wraps its label in a <span>. Why does clicking the label do nothing, and how do you fix the dispatch?
basics
~20 sevent.target is the innermost element the click landed on — the span, not the li — so the tagName check fails. Match with event.target.closest('li') instead, and verify the result is inside your own container before acting on it.
In JavaScript, what does the == (loose equality) operator do that === does not, and what do '5' == 5 and '5' === 5 each evaluate to?
basics
~20 sThe == operator converts operands to a common type before comparing; === requires matching types and never converts. So '5' == 5 is true, because the string is converted to the number 5, while '5' === 5 is false.
In JavaScript, what does the strict equality operator === compare, and how does it behave when its two operands have different types?
basics
~20 s=== returns true only when both operands have the same type and the same value. Different types are never converted, so the result is immediately false. Objects and arrays compare by reference, not by contents.
In JavaScript, why does '5' + 3 give '53' while '5' - 3 gives 2? Explain what the + operator does with mixed operand types.
basics
~20 sBinary + is the only arithmetic operator that is also string concatenation: after converting both operands to primitives, if either one is a string it concatenates, otherwise it adds numerically. Every other arithmetic operator converts to number unconditionally, so '5' - 3 is 2.
In JavaScript, which values are falsy — that is, which convert to false when used as an `if` condition — and which commonly-assumed-falsy values are actually truthy?
basics
~20 sJavaScript has a fixed falsy list: false, 0, -0, 0n, the empty string, null, undefined, and NaN. Every other value is truthy — including '0', 'false', the empty array, the empty object, and every function.
In JavaScript, why does `0.1 + 0.2 === 0.3` evaluate to false, and what does `0.1 + 0.2` actually produce?
basics
~10 sJavaScript numbers are IEEE-754 binary doubles, and 0.1, 0.2 and 0.3 cannot be written exactly in binary. The stored approximations add up to 0.30000000000000004, a different double from 0.3, so === is false.
In JavaScript, what is the difference between Array.prototype.slice() and Array.prototype.splice(), and what does each one return?
basics
~20 sslice() copies: it leaves the original array untouched and returns a new array holding the selected range. splice() edits in place: it removes and/or inserts elements in the original array and returns an array of the elements it removed.
In JavaScript array destructuring, when does a default value like `const [a = 5] = values` actually apply — and what do you get when the matching element is null, a hole, or missing entirely?
basics
~20 sA destructuring default fires only when the matched value is exactly undefined — a missing index, an array hole, or an explicit undefined. null, 0, empty string and NaN are real values, so they are bound as-is.
In JavaScript, what do Array.prototype.find and Array.prototype.findIndex return when no element satisfies the predicate, and what bug does that difference commonly cause?
basics
~20 sArray.prototype.find returns undefined when nothing matches, while findIndex returns -1. The bug is truthiness: -1 is truthy and index 0 is falsy, so testing findIndex's result directly inverts the check. Compare it against -1 instead.
Why does [10, 9, 1].sort() return [1, 10, 9] in JavaScript, and how do you sort an array of numbers correctly?
basics
~20 sArray.prototype.sort called with no comparator converts every element to a string and compares those strings, so "10" sorts before "9". Pass a numeric comparator, arr.sort((a, b) => a - b), to order numbers by value.
In JavaScript, what is the difference between Array.prototype.map and Array.prototype.filter, and what does each one return?
basics
~20 sArray.prototype.map builds a new array of the same length by replacing each element with whatever its callback returns. Array.prototype.filter builds a new array holding only the elements whose callback returned a truthy value. Neither changes the original array.
What does the JavaScript expression `new Date(2025, 0, 31)` represent, and which of the Date constructor's argument conventions most often cause off-by-one bugs?
basics
~20 snew Date(2025, 0, 31) is 31 January 2025 at local midnight. The month argument is zero-indexed (0 = January) while day-of-month starts at 1, and multi-argument construction is always read in the host's local time zone.
Which built-in error constructors does JavaScript itself throw at runtime — TypeError, ReferenceError, RangeError, SyntaxError, URIError — and what kind of failure does each one signal?
basics
~20 sJavaScript throws TypeError when a value is the wrong kind of thing for the operation, ReferenceError when an identifier does not resolve, RangeError when a value of the right type is out of range, SyntaxError when source cannot be parsed, and URIError for malformed URI escapes.
In JavaScript, which values are legal after `throw`, and what concretely breaks in the catching code when a function throws the string 'Network unavailable' instead of `new Error('Network unavailable')`?
basics
~20 sJavaScript's throw accepts any value: strings, numbers, plain objects, null, even undefined. Only Error instances carry name, message and a stack, so a thrown string leaves the catch block with no diagnostics, err.message undefined, and err instanceof Error false.
In JavaScript, when does a `finally` block actually run? Does it still execute if the `try` block returns a value, or if an error is thrown that no `catch` clause handles?
basics
~20 sA JavaScript finally block runs on every exit from its try statement - normal completion, an early return, a break or continue, a caught error, and an error still propagating outward - which is exactly why cleanup belongs there.
What does JSON.stringify() do with undefined, function values and symbols, and how does the outcome differ between an object property, an array element, and the top-level argument?
basics
~20 sJSON.stringify silently drops undefined, function and symbol object properties, turns those same values into null inside arrays, and returns the value undefined — not a string — when you pass one directly as the top-level argument.