Frontend Developer — Interview Questions
In short: Using elements that describe meaning — header, nav, main, article, section, aside, footer, button, headings — instead of generic div/span.
- Benefits: accessibility (screen readers use landmarks and roles), SEO (search engines understand structure), built-in behaviour (buttons are focusable and keyboard-operable), and readable code.
In short: Each element is content + padding + border + margin.
- With the default
content-box,widthsets only the content, so padding and border are added on top. - With
border-box,widthincludes content, padding and border, which makes sizing predictable — most projects set it globally.
In short: Specificity scores selectors as (ids, classes/attributes/pseudo-classes, elements);
- higher wins, inline styles beat selectors,
!importantbeats normal declarations, and with equal specificity the later rule wins. - Example:
#nav a(1,0,1) beats.menu a.active(0,2,1). - Keep specificity low with classes to avoid override wars.
In short: Flexbox is one-dimensional (a row or a column), content-driven — nav bars, toolbars, centring, distributing items.
- Grid is two-dimensional (rows and columns together), container-driven — page layouts, card galleries, forms.
- Often Grid for the overall layout and Flexbox inside components.
In short: Flexbox: parent display:flex; justify-content:center; align-items:center;.
- Grid: parent
display:grid; place-items:center;. - Horizontal only for a block with a width:
margin: 0 auto. - Absolute:
position:absolute; inset:0; margin:auto;with a set size, ortop:50%; left:50%; transform:translate(-50%,-50%).
Viewport meta tag, mobile-first CSS with min-width media queries, fluid layouts (Flexbox/Grid, %, fr, minmax, clamp), responsive images (max-width:100%, srcset, <picture>), relative units (rem), container queries for reusable components, testing on real devices and DevTools device mode, and avoiding fixed widths and horizontal scroll.
In short: Media queries apply styles based on the viewport/device (@media (min-width: 768px), prefers-color-scheme, prefers-reduced-motion, print).
- Container queries apply styles based on the size of a parent container (`container-type: inline-size;
- @container (min-width: 400px)`), so a component adapts wherever it is placed — e.g. a card in a narrow sidebar vs a wide main area.
In short: Semantic HTML, labels for inputs, alt text, real buttons/links, keyboard navigation with visible focus, colour contrast ≥ 4.5:1, logical heading order, lang attribute, ARIA only where native HTML can't (live regions, expanded states), respect reduced motion, and test with Lighthouse/axe and a screen reader.
- Target WCAG 2.1/2.2 AA.
In short: Use DevTools device mode and find the element wider than the viewport (temporarily * { outline: 1px solid red }, or check elements whose right edge exceeds the width).
- Usual causes: fixed widths (
width: 500px), long unbroken text/URLs (overflow-wrap: anywhere), images withoutmax-width:100%,100vwwith a scrollbar, negative margins, tables and code blocks (wrap them in anoverflow-x:autocontainer), and grid/flex children that can't shrink (min-width: 0on grid/flex items,minmax(0, 1fr)).
== and ===?In short: === (strict equality) compares type and value without conversion.
==(loose equality) converts operands to a common type first, which produces surprises ("" == 0is true,null == undefinedis true,[] == falseis true).- Always use
===(except, by some style guides,x == nullto check null or undefined).
var, let and const?In short: var is function-scoped, hoisted and initialised as undefined, can be re-declared, and creates a property on the global object at top level.
letandconstare block-scoped and hoisted but in the Temporal Dead Zone until their declaration (accessing them earlier throws).- Neither can be re-declared in the same scope.
constcan't be reassigned, but the object it references can still be mutated.- Default to
const, useletwhen reassigning, avoidvar.
In short: During the creation phase of an execution context, declarations are registered before code runs.
varvariables are hoisted and initialised toundefined.- Function declarations are hoisted with their body (callable before the line they appear on).
let,constandclassare hoisted but uninitialised (TDZ → ReferenceError if used early).- Function expressions and arrow functions follow the rules of the variable they're assigned to.
In short: string, number, bigint, boolean, undefined, null and symbol.
- They are immutable and compared by value.
- Everything else (objects, arrays, functions, dates) is an object compared by reference.
typeof nullreturns "object" due to a historical bug.
null and undefined?In short: undefined means a variable has been declared but not assigned, a missing property, or a function with no return value — set by the engine.
nullis an intentional "no value" assigned by the programmer.typeof undefinedis "undefined",typeof nullis "object";null == undefinedis true butnull === undefinedis false.??and?.treat both as nullish.
In short: A concise function syntax (x => x * 2).
- Differences: no own
this(lexically inherited — great for callbacks), noargumentsobject, can't be used as constructors withnew, noprototype, and implicit return for single expressions. - Don't use them as object methods or prototype methods that need dynamic
this.
In short: A closure is a function that remembers the variables from the scope where it was created, even after that scope has finished executing.
- Example:
function counter() { let c = 0; return () => ++c; }— each returned function has its own privatec. - Practical uses: data privacy, function factories, memoisation caches, debounce/throttle timers, partial application, event handlers capturing loop values (with
let).
In short: JavaScript executes on a single thread with a call stack.
- Asynchronous operations (timers, network, events) are handled by the browser/Node APIs, which queue callbacks when done.
- When the call stack is empty, the event loop first runs all microtasks (promise reactions,
queueMicrotask), then (in browsers) lets rendering happen, then picks the next macrotask (setTimeout, I/O, UI events). - That's why a resolved promise's
.thenruns before asetTimeout(..., 0)callback.
In short: A Promise represents the eventual result of an asynchronous operation, in state pending, fulfilled or rejected; .then/.catch/.finally register reactions.
Promise.allwaits for all and rejects on the first failure.allSettledwaits for all and returns each outcome.racesettles with the first settled promise (success or failure).anyfulfils with the first success and rejects with AggregateError only if all fail.
this work in JavaScript?In short: It depends on the call site: method call → the object before the dot;
- plain function call →
undefinedin strict mode (global object otherwise); - constructor with
new→ the new instance; call/apply/bind→ explicitly set;- event listeners with regular functions → the element;
- arrow functions →
thisof the enclosing lexical scope. - Losing
thiswhen passing methods as callbacks is fixed withbindor arrow functions.
call, apply and bind?In short: All set this explicitly.
call(thisArg, a, b)invokes immediately with individual arguments.apply(thisArg, [a, b])invokes immediately with an array of arguments.bind(thisArg, a)doesn't invoke — it returns a new function withthis(and optionally leading arguments) permanently bound, useful for callbacks and event handlers.
In short: Objects inherit directly from other objects through the internal [[Prototype]] link.
- When a property isn't found on an object, the engine looks up its prototype chain.
- Constructor functions share methods via
Constructor.prototype; Object.create(proto)creates objects with a chosen prototype;- ES6
class/extendsis syntax over the same mechanism (Dog.prototype.__proto__ === Animal.prototype).
In short: Attaching a single event listener to a common ancestor instead of each child, using event bubbling and event.target (often with closest()) to identify which child was interacted with.
- Benefits: fewer listeners (memory/performance), works for elements added later dynamically, simpler cleanup.
- Note: some events don't bubble (
focus,blur— usefocusin/focusout).
Debounce delays execution until calls stop for a given time (search-as-you-type, auto-save, resize end). Throttle guarantees execution at most once per interval (scroll position, mousemove, rate-limited API calls).
const debounce = (fn, ms) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; };
const throttle = (fn, ms) => { let last = 0; return (...a) => { const now = Date.now(); if (now - last >= ms) { last = now; fn(...a); } }; };In short: Unremoved event listeners and timers (setInterval never cleared), references to detached DOM nodes, accidental globals, closures capturing large objects, ever-growing caches/maps, and subscriptions not unsubscribed in SPA components.
- Find them with Chrome DevTools: Performance monitor (JS heap growing), Memory tab heap snapshots compared over time, "Detached elements" panel, and allocation timelines.
- Fix with cleanup on unmount,
WeakMap/WeakReffor metadata, and bounded caches.
In short: Debounce the input (e.g. 300 ms) so the request fires only after typing pauses;
- ignore queries shorter than 2–3 characters;
- cancel the previous in-flight request with
AbortControllerso stale responses don't overwrite newer results (or track the latest request id); - cache recent results;
- show a loading state.
- On the server, add rate limiting and make the search endpoint efficient (indexes, paging).
In short: The main thread is blocked by long synchronous work.
- Move parsing/processing into a Web Worker (postMessage the data or a File/Blob, return results);
- stream the file with
ReadableStream/ process in chunks; - better, process it on the server and fetch paginated or aggregated results;
- virtualise large lists when rendering;
- show progress.
- Measure with the DevTools Performance panel (long tasks).
In short: A typed superset of JavaScript that compiles to plain JavaScript.
- Static types catch errors at compile time, give IntelliSense and safe refactoring, document APIs and scale better for large teams.
- Types are erased at runtime, so there is no runtime cost — and no runtime checking.
In short: Both describe object shapes.
typecan also name unions, intersections, tuples, primitives and mapped/conditional types;interfacesupports declaration merging andextends, and is often preferred for public object contracts.- Interfaces can't express unions; types can't be reopened.
In short: any disables type checking for that value (errors pass silently).
unknownaccepts any value but must be narrowed (typeof/guards/schema) before use — the safe choice for external data andcatcherrors.neverrepresents values that never occur — functions that always throw, or the impossible branch in an exhaustive switch.
In short: A union A | B means the value is one of several types.
- Narrow with
typeof,instanceof,in, equality on a discriminant property (kind), truthiness, or custom type guards (x is T) so TypeScript knows which member you have in each branch.
In short: Type parameters that make code reusable while keeping types: function first<T>(xs: T[]): T | undefined.
- With constraints (
T extends { id: number }) andkeyof(K extends keyof T) you get type-safe helpers such aspluck(items, "name"). - Common built-ins:
Array<T>,Promise<T>,Record<K,V>.
In short: Partial<T> makes all props optional (update/PATCH payloads).
Pick<T,K>keeps chosen keys (summary DTO).Omit<T,K>removes keys (create payload withoutid).Record<K,V>builds an object type with keys K (lookup tables likeRecord<Status, string>).
In short: strict: true enables noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, alwaysStrict, useUnknownInCatchVariables and others.
- It catches the most common bugs — especially null/undefined access.
- New projects should always use it; legacy projects can turn flags on gradually.
In short: A union of object types that share a literal "tag" property (kind, type, status).
- Switching on the tag narrows to the exact member, and a
nevercheck indefaultmakes the switch exhaustive — adding a new member produces compile errors wherever it isn't handled. - Ideal for state (
{ status: "loading" } | { status: "error"; error: string } | { status: "ok"; data: T }) and Redux/NgRx actions.
In short: Types are erased at runtime; http.get<Order[]>() or as Order[] only told the compiler what to expect.
- Prevent: generate client types from the backend OpenAPI document in the build (so contract changes show up as compile errors), validate responses at the boundary with Zod/Valibot, version the API, and add contract/end-to-end tests.
In short: A JavaScript runtime built on Chrome's V8 engine and libuv.
- It runs your JavaScript on a single main thread with an event loop and performs I/O (network, disk) asynchronously, so while one request waits for the database the thread serves other requests.
- Without thread-per-request overhead it handles many concurrent connections efficiently — ideal for APIs, proxies and real-time apps.
In short: The event loop is libuv's loop that repeatedly checks for completed work and runs callbacks on the main thread in phases: timers (setTimeout/setInterval), pending callbacks, poll (I/O events), check (setImmediate), close callbacks.
- Between callbacks Node drains the
process.nextTickqueue and then the promise microtask queue. - Blocking the loop with long synchronous code delays every other request.
In short: Your JavaScript runs on one main thread, but Node itself is not: libuv uses a thread pool (default 4) for file system, DNS lookup, crypto and compression, and the OS handles network I/O asynchronously.
- You can also use worker threads for CPU-heavy JavaScript and the cluster module or multiple containers to use all CPU cores.
In short: CommonJS uses require()/module.exports, loads synchronously at runtime and was Node's original system.
- ES modules use
import/export, are statically analysable (tree-shaking), support top-level await and are the JavaScript standard — enabled with.mjsor"type": "module". - Modern Node supports both and recent versions can
require()ES modules.
In short: A function (req, res, next) in the request pipeline that can read/modify the request and response, end the response, or call next() to pass control on.
- Used for logging, parsing bodies, authentication, CORS, validation and error handling (error middleware has four parameters
(err, req, res, next)). - Middleware runs in the order it's registered.