Web/Frontend Concept Questions
This section covers foundational concepts tested in frontend interviews. Questions assess understanding of browser internals, JavaScript behavior, and framework implementation details.
JavaScript Fundamentals
Q1: Explain the event loop
JavaScript executes on a single thread. The event loop enables asynchronous operations without blocking execution.
Components:
| Component | Function |
|---|---|
| Call Stack | Executes synchronous code |
| Web APIs | Handle async operations (setTimeout, fetch) |
| Callback Queue | Holds callbacks ready for execution |
| Microtask Queue | Holds Promise callbacks (higher priority than callback queue) |
Execution order:
- Execute all synchronous code on the call stack
- Process all microtasks (Promises)
- Process one macrotask (setTimeout callback)
- Repeat
Q2: Differences between var, let, and const
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (initialized to undefined) | Yes (TDZ) | Yes (TDZ) |
| Reassignment | Yes | Yes | No |
| Redeclaration | Yes | No | No |
TDZ (Temporal Dead Zone): The period between entering a scope and the variable declaration where accessing the variable throws a ReferenceError.
Usage guidelines:
- Use
constby default - Use
letwhen reassignment is required - Avoid
varin modern JavaScript
Q3: Closures
A closure is a function that retains access to variables from its lexical scope, even after the outer function has returned.
Example: A createCounter function returns an inner function. The inner function has access to the count variable from the outer function's scope, even after createCounter has returned. Each call to the returned function increments and returns the count (1, then 2, and so on).
Use cases:
| Use Case | Description |
|---|---|
| Data encapsulation | Private variables inaccessible from outer scope |
| Function factories | Functions that generate configured functions |
| State in callbacks | Maintaining state across asynchronous operations |
Q4: Difference between == and ===
| Operator | Behavior |
|---|---|
== (loose equality) | Compares values after type coercion |
=== (strict equality) | Compares values and types without coercion |
Examples: With loose equality, 1 == '1' returns true because the string is coerced to a number. With strict equality, 1 === '1' returns false because the types differ. Similarly, null == undefined is true with loose equality but false with strict equality.
Use === to avoid unexpected type coercion behavior.
Q5: The this keyword
The value of this is determined by how a function is called, not where it is defined.
| Call Context | this Value |
|---|---|
| Global scope | window (or undefined in strict mode) |
| Object method | The object |
Constructor (new) | The new instance |
call/apply/bind | Explicitly specified value |
| Arrow function | Inherited from enclosing lexical scope |
Arrow functions do not have their own this binding. They use this from the surrounding scope.
DOM and Browser
Q6: Event bubbling and capturing
Event propagation occurs in three phases:
| Phase | Description |
|---|---|
| Capture phase | Event travels from document root to target element |
| Target phase | Event reaches the target element |
| Bubble phase | Event travels from target back up to document root |
Listener registration: By default, addEventListener registers handlers for the bubble phase. To register for the capture phase instead, pass true as the third argument.
Event delegation: Attach a single event listener to a parent element instead of multiple listeners on child elements. Events bubble up from children to the parent handler.
Q7: Critical rendering path
The browser renders a page through these steps:
| Step | Output |
|---|---|
| Parse HTML | DOM Tree |
| Parse CSS | CSSOM Tree |
| Combine | Render Tree |
| Layout | Element positions and sizes |
| Paint | Pixels on screen |
| Composite | Layer composition |
Optimization techniques:
- Minimize render-blocking resources (CSS, synchronous JS)
- Inline critical CSS
- Defer non-critical JavaScript
- Reduce DOM depth
Q8: Storage mechanisms
| Feature | localStorage | sessionStorage | Cookies |
|---|---|---|---|
| Capacity | ~5-10MB | ~5-10MB | ~4KB |
| Expiration | Persistent | Tab close | Configurable |
| Server access | No | No | Sent with every request |
| Scope | Same origin | Same tab and origin | Configurable path |
Use cases:
- localStorage: User preferences, cached data
- sessionStorage: Temporary form data, session state
- Cookies: Authentication tokens, server-side session tracking
Q9: CORS (Cross-Origin Resource Sharing)
CORS is a browser security mechanism that controls cross-origin HTTP requests.
Same-origin policy: Two URLs have the same origin if they share the same protocol, host, and port.
CORS flow:
- Browser sends request with
Originheader - Server responds with
Access-Control-Allow-Originheader - Browser allows response if origins match or wildcard is specified
Preflight requests (OPTIONS):
- Required for non-simple requests (non-GET, custom headers)
- Browser checks permissions before sending actual request
Q10: HTTP caching
Request headers:
| Header | Purpose |
|---|---|
Cache-Control: no-cache | Revalidate before using cached response |
If-Modified-Since | Conditional request based on modification date |
If-None-Match | Conditional request based on ETag |
Response headers:
| Header | Purpose |
|---|---|
Cache-Control: max-age=3600 | Cache for 1 hour |
ETag | Version identifier for validation |
Last-Modified | Timestamp of last modification |
Cache hierarchy: Browser cache, CDN, Origin server
Best practice: Use long cache durations for static assets with content hashing in filenames for cache busting.
React
Q11: Virtual DOM
The Virtual DOM is an in-memory representation of the actual DOM.
Reconciliation process:
- State change triggers new Virtual DOM tree creation
- React diffs new tree against previous tree
- Minimal set of DOM operations calculated
- Batch updates applied to actual DOM
Benefits:
| Benefit | Description |
|---|---|
| Performance | Batched updates reduce DOM manipulation |
| Declarative model | Describe UI state, React handles updates |
| Cross-platform | Same model enables React Native |
Q12: Rules of Hooks
Two rules govern React Hooks usage:
| Rule | Reason |
|---|---|
| Only call at top level | React tracks hooks by call order |
| Only call from React functions | Must be in function components or custom hooks |
Incorrect pattern: Calling a hook inside a conditional statement means the hook may not be called on some renders, breaking React's tracking mechanism.
Correct pattern: Call hooks unconditionally at the top level, then use conditional logic after the hook call to determine how to use the returned values.
React relies on consistent hook call order between renders. Conditional hook calls break this ordering.
Q13: useEffect vs useLayoutEffect
| Hook | Timing | Blocking |
|---|---|---|
| useEffect | After paint, asynchronous | No |
| useLayoutEffect | After DOM update, before paint | Yes |
Use useLayoutEffect when:
- Measuring DOM elements (getBoundingClientRect)
- Preventing visual flashes from synchronous DOM updates
- Reading layout and synchronously re-rendering
Use useEffect for most cases unless visual flickering occurs.
Q14: Reconciliation algorithm
React's reconciliation determines DOM updates through these rules:
| Scenario | Behavior |
|---|---|
| Different element types | Replace entire subtree |
| Same element type | Keep node, update changed attributes |
| Component update | Re-render, recursively check children |
| Lists | Match items using keys |
Keys in lists:
| Key Strategy | Behavior |
|---|---|
| Without keys | Compare by index (fails on reorder) |
| With stable keys | Efficient matching of old and new items |
Use stable, unique keys. Array indices cause incorrect behavior when items are reordered, added, or removed.
Q15: React performance optimization
Preventing unnecessary renders:
| Technique | Purpose |
|---|---|
| React.memo | Memoize component based on props |
| useMemo | Memoize computed values |
| useCallback | Memoize callback functions |
Code splitting:
| Technique | Purpose |
|---|---|
| React.lazy | Dynamic component imports |
| Suspense | Loading states for lazy components |
| Route-based splitting | Load code per route |
Virtualization:
- react-window, react-virtualized
- Render only visible items in long lists
State management:
- Keep state close to where it is used
- Avoid unnecessary global state
Profiling:
- React DevTools Profiler
- Identify components with wasted renders
Performance
Q16: Core Web Vitals
| Metric | Measurement | Target |
|---|---|---|
| LCP (Largest Contentful Paint) | Loading performance | < 2.5s |
| INP (Interaction to Next Paint) | Responsiveness | < 200ms |
| CLS (Cumulative Layout Shift) | Visual stability | < 0.1 |
Optimization strategies:
| Metric | Techniques |
|---|---|
| LCP | Optimize images, use CDN, preload critical resources |
| INP | Reduce JavaScript execution, break up long tasks |
| CLS | Set explicit dimensions on images, avoid dynamic content injection |
Q17: Lazy loading and code splitting
Lazy loading: Defer loading resources until needed.
| Type | Implementation |
|---|---|
| Images | loading="lazy" attribute |
| Components | Load on route navigation or user interaction |
| Data | Load on scroll or interaction |
Code splitting: Divide application bundle into smaller chunks.
React lazy loading: Use React.lazy with a dynamic import to load components on demand. The component is loaded only when it is first rendered.
Route-based splitting: Configure routes to use lazy-loaded components, so each route's code is loaded only when the user navigates to that route.
Benefits: Faster initial load, reduced initial bundle size.
Q18: Bundle size reduction
| Strategy | Description |
|---|---|
| Analysis | Use webpack-bundle-analyzer to identify large dependencies |
| Tree shaking | Remove unused exports during build |
| Code splitting | Lazy load routes and components |
| Dynamic imports | Load libraries on demand |
| Library selection | Use lighter alternatives (date-fns vs moment) |
| Compression | Enable gzip or Brotli compression |
| Modern syntax | Serve ES6+ to modern browsers |
Q19: Service workers
A service worker is a JavaScript file that runs in a separate thread from the main page.
Capabilities:
| Feature | Description |
|---|---|
| Offline support | Cache assets and data for offline access |
| Push notifications | Receive notifications when tab is closed |
| Background sync | Defer actions until network is available |
Lifecycle:
- Register
- Install (cache assets)
- Activate (take control of pages)
- Fetch events (intercept network requests)
Service workers enable Progressive Web App (PWA) functionality.
Q20: Debounce vs throttle
Both limit function execution frequency.
| Technique | Behavior | Use Case |
|---|---|---|
| Debounce | Execute after activity stops for specified duration | Search input (wait for typing to stop) |
| Throttle | Execute at most once per time period | Scroll handler (update at fixed intervals) |
Debounce example: Wrap a search function with a 300ms debounce. The search executes only after the user stops typing for 300ms.
Throttle example: Wrap a scroll handler with a 100ms throttle. The handler executes at most once every 100ms during scrolling.