Back to Developer

JavaScript

Core JavaScript for testers and developers — language fundamentals, async, and OOP.

Fundamentals

const vs let vs var: which do you use and why?
Use const by default (the binding cannot be reassigned), let when you need to reassign, and avoid var — it is function-scoped and hoisted with confusing behavior, while const/let are block-scoped. Note const prevents reassignment, not mutation: a const object's properties can still change.
What do map, filter and reduce do, and how do they differ from forEach?
map transforms each element and returns a new array of the same length; filter returns a new array with only the elements passing a predicate; reduce accumulates the array into a single value. forEach just iterates for side effects and returns nothing. map/filter/reduce are pure (don't mutate the original); push/pop/splice/sort do mutate.
What are destructuring and the spread operator used for?
Destructuring unpacks values from arrays or objects into variables: const { name, age } = user, or in function params function f({ id }). Spread (...) copies/merges: const merged = { ...base, env: 'prod' } or const all = [...a, ...b]. Spread copies are shallow — nested objects are shared, not cloned.
What do optional chaining (?.) and nullish coalescing (??) do?
Optional chaining short-circuits to undefined instead of throwing when an intermediate value is null/undefined: res.data?.user?.name. Nullish coalescing returns the right side only when the left is null or undefined (not for 0 or '', unlike ||): const total = res.data?.order?.total ?? 0. Both are common when asserting on API response shapes.
In a React coding challenge, a component defines its state using a `let` variable instead of the proper React mechanism. What is wrong with this approach and how do you fix it?
Using a let variable for state in a React component does not trigger re-renders when the value changes, so the UI will not update. The fix is to replace the let variable with the useState hook. For example, change let count = 0 to const [count, setCount] = useState(0), and update the value using the setter function (setCount). This ensures React tracks the state change and re-renders the component accordingly.
Write a recursive function to calculate the factorial of a number.
A recursive factorial function works by multiplying the number by the factorial of (number - 1), with a base case that returns 1 when the input is 0 or 1: function factorial(n) { if (n == 1 || n == 0) return 1; else return n * factorial(n - 1); } Note: In a production implementation, you should also add a condition to handle negative numbers (e.g., throw an error or return an appropriate value).
How do you call API services in a React application?
In React, you can call API services using either axios (a promise-based HTTP client library) or the native fetch API. Both allow you to make HTTP requests to external services from your components, typically within a useEffect hook.
How do you test React components?
React components can be tested using Jest as the test runner and assertion library, along with React Testing Library for rendering components and simulating user interactions in a testing environment.
What are the key areas to focus on when preparing for a frontend coding challenge on platforms like HackerRank?
Key areas to focus on include DOM manipulation and consuming APIs to render data in the UI. Being comfortable with fetching data from endpoints and dynamically updating the page is essential for most frontend coding challenges.
Implement a log parsing solution using Node.js streams that reads a log file and categorizes log entries by level (ERROR, WARN, DEBUG, INFO).
There are two common approaches: **Simple approach using readline:** javascript import fs from "fs"; import readline from "readline"; const stream = fs.createReadStream("app.log"); const rl = readline.createInterface({ input: stream, crlfDelay: Infinity }); const counters = { ERROR: 0, WARN: 0, DEBUG: 0, INFO: 0, UNKNOWN: 0 }; rl.on("line", (line) => { if (line.includes("[ERROR]")) counters.ERROR++; else if (line.includes("[WARN]")) counters.WARN++; else if (line.includes("[DEBUG]")) counters.DEBUG++; else if (line.includes("[INFO]")) counters.INFO++; else counters.UNKNOWN++; }); rl.on("close", () => { console.log("Log summary:"); console.log(counters); }); **Using pure Transform streams:** javascript import fs from "fs"; import { Transform } from "stream"; class LogParser extends Transform { constructor() { super({ readableObjectMode: true }); this.buffer = ""; } _transform(chunk, encoding, callback) { this.buffer += chunk.toString(); const lines = this.buffer.split("\n"); this.buffer = lines.pop(); // incomplete line for (const line of lines) { this.push(this.parseLine(line)); } callback(); } _flush(callback) { if (this.buffer) { this.push(this.parseLine(this.buffer)); } callback(); } parseLine(line) { if (line.includes("[ERROR]")) return { level: "ERROR", line }; if (line.includes("[WARN]")) return { level: "WARN", line }; if (line.includes("[DEBUG]")) return { level: "DEBUG", line }; if (line.includes("[INFO]")) return { level: "INFO", line }; return { level: "UNKNOWN", line }; } } fs.createReadStream("app.log") .pipe(new LogParser()) .on("data", (log) => { console.log(log); }); **Improvement with regex for more flexible matching:** javascript const patterns = { ERROR: /\[ERROR\]/, WARN: /\[WARN(ING)?\]/, DEBUG: /\[DEBUG\]/, INFO: /\[INFO\]/ }; function detectLevel(line) { for (const [level, regex] of Object.entries(patterns)) { if (regex.test(line)) return level; } return "UNKNOWN"; } The Transform stream approach is preferred because it processes data chunk by chunk without loading the entire file into memory, handles incomplete lines via a buffer, and outputs structured objects for downstream processing.
How would you detect overlapping events in a list of time-based events using JavaScript?
Sort the events array by their start time, then iterate through the sorted array comparing each event's start time with the previous event's end time. If the current event's start is before the previous event's end, there is an overlap. Here is an example implementation: const hasOverlappingEvents = (events) => { const sorted = [...events].sort((a, b) => a.start.localeCompare(b.start) ); for (let i = 1; i < sorted.length; i++) { if (sorted[i].start < sorted[i - 1].end) { return true; } } return false; }; This approach has O(n log n) time complexity due to sorting, followed by a single O(n) pass through the array.
When should you use event.preventDefault() in JavaScript?
event.preventDefault() is used to stop the default behavior of certain HTML elements. The most common use case is preventing a page from reloading when a form is submitted, which is the default behavior of an HTML form's submit event. It is specifically needed when you implement an onClick handler on elements such as an anchor tag (<a>) or a button inside a form with type="submit". Many developers know what event.preventDefault() does, but the key is knowing the correct use case: you use it when you want to handle an event with custom JavaScript logic while suppressing the browser's built-in response to that event.
What are common JavaScript theory questions asked in frontend interviews?
Common JavaScript theory questions in frontend interviews include: 1) How does hoisting work in JS? 2) What is the event loop and how does it work? 3) What is the difference between var, let, and const? 4) What is a closure and how would you implement one? 5) How does the 'this' keyword work in JS? 6) What is destructuring and how do you do it with objects and arrays? 7) What is the difference between map and forEach? 8) What is the difference between find and some? 9) What is the difference between == and ===? 10) How do promises work? 11) What does 'finally' do in a try/catch block? These are core language concepts that help interviewers assess whether a candidate has taken the time to deeply understand JavaScript fundamentals.
In a React application, why is it generally recommended to use CSS classes or a separate CSS file instead of inline styles?
Using inline styles in React (via the style={{}} syntax) is considered a bad practice. Even if you only need a couple of style declarations, it is better to create a CSS file or use CSS classes. Inline styles reduce maintainability, reusability, and separation of concerns. During code reviews and technical interviews, using inline styles can be viewed negatively because it indicates a lack of adherence to best practices in frontend development. If a CSS file is already available (as in environments like CodePen), you should use it by creating appropriate classes or IDs rather than applying styles directly on elements.
What are common live coding exercises in fullstack (React/Node) technical interviews?
Common live coding exercises vary, but frequently reported ones include: (1) Algorithm challenges such as Fibonacci sequence, finding the shortest path to a node, and string manipulation (e.g., counting how many times a word can be formed from a given string). (2) For React-specific interviews: implementing a list component, building an infinite scroll, debugging and fixing an existing component, refactoring a component to separate responsibilities, and eliminating unnecessary re-renders. (3) Backend/general exercises: consuming an API with pagination, and implementing a table with server-side pagination. It is recommended to practice on platforms like LeetCode or HackerRank at medium to hard difficulty, covering edge cases and striving for optimal solutions.
What is `this` in JavaScript?
this in JavaScript is a reference to the current object — specifically, the object that is executing or calling the function at the moment of invocation. Within classes, it is used to reference properties or methods of that same instance. However, its behavior varies depending on the context: in regular functions, this refers to the object that invoked the function, while in arrow functions, this is inherited from the enclosing lexical scope (one scope above). Essentially, this resolves based on the execution context at runtime. The concept is analogous to self in Python and this in C# or Java, where it references the attributes or methods of the current object instance.
What React concepts are commonly asked about in frontend interviews?
Common React topics that frequently appear in frontend interviews include: the purpose of useState (managing local component state) and useEffect (handling side effects such as data fetching or subscriptions), the difference between state and props (state is internal and mutable within a component, while props are external inputs passed from a parent and are read-only), and the concept of the Virtual DOM (a lightweight in-memory representation of the real DOM that React uses to compute the minimum set of changes needed before updating the browser).
Write a function that detects whether an array contains any duplicate elements. Provide solutions in both TypeScript and Java.
Two approaches are shown below, both with O(n) time complexity. **TypeScript (using Set — idiomatic and concise):** typescript function isDuplicated(array: number[]): boolean { const seen = new Set<number>(); for (const value of array) { if (seen.has(value)) { return true; } seen.add(value); } return false; } Iterate through the array; if the current value already exists in the Set, a duplicate has been found and true is returned immediately. Otherwise the value is added to the set. **Java (using HashMap):** java private static boolean isDuplicated(Integer[] array) { Map<Integer, Integer> tempMemory = new HashMap<>(); for (int i = 0; i < array.length; i++) { // O(n) if (tempMemory.containsKey(array[i])) { return true; } else { tempMemory.put(array[i], 1); } } return false; } Each element is stored as a key in a HashMap. If the key is already present, a duplicate is detected. Both solutions short-circuit as soon as the first duplicate is found.
How would you implement a Node.js solution that reads a large log file as a stream, parses each line by log level (ERROR, DEBUG, INFO, WARNING), writes matching lines to separate output files, and applies additional conditional logic (e.g., flagging WARNING lines where a CPU value is ≥ 98%)?
Use a Node.js Transform stream to process the log file chunk by chunk without loading the entire file into memory. Buffer incomplete lines across chunks, then split on newlines and process each complete line. Parse the log level from each line and pipe matching lines to the appropriate writable output stream. Apply any extra conditional logic (such as a regex match for CPU percentage) before writing. Example approach: js import fs from "fs"; import { Transform } from "stream"; const input = fs.createReadStream("./data/logfile.log", { encoding: "utf8" }); const outputs = { ERROR: fs.createWriteStream("./data/error.log"), DEBUG: fs.createWriteStream("./data/debug.log"), INFO: fs.createWriteStream("./data/info.log"), WARNING: fs.createWriteStream("./data/warning.log"), HIGHCPU: fs.createWriteStream("./data/highcpu.log"), }; const transformStream = new Transform({ decodeStrings: false, transform(chunk, _, cb) { this.buffer = (this.buffer || "") + chunk; const lines = this.buffer.split("\n"); this.buffer = lines.pop(); // keep incomplete trailing line for (const line of lines) { if (!line.trim()) continue; const parts = line.split(" - "); if (parts.length < 3) continue; const status = parts[1].trim(); const message = parts.slice(2).join(" - ").trim(); if (outputs[status]) { outputs[status].write(message + "\n"); } // Extra rule: WARNING lines with CPU >= 98% go to HIGHCPU as well if (status === "WARNING") { const match = message.match(/(\d+)%/); if (match && Number(match[1]) >= 98) { outputs.HIGHCPU.write(message + "\n"); } } this.push(line + "\n"); } cb(); }, }); transformStream.on("end", () => { Object.values(outputs).forEach(s => s.end()); }); input.pipe(transformStream); For a simpler read-only summary (counting lines per level without writing separate files), the readline interface over a createReadStream is sufficient: js import fs from "fs"; import readline from "readline"; const rl = readline.createInterface({ input: fs.createReadStream("app.log"), crlfDelay: Infinity }); const counters = { ERROR: 0, WARN: 0, DEBUG: 0, INFO: 0, UNKNOWN: 0 }; const patterns = { ERROR: /\[ERROR\]/, WARN: /\[WARN(ING)?\]/, DEBUG: /\[DEBUG\]/, INFO: /\[INFO\]/ }; rl.on("line", line => { const level = Object.entries(patterns).find(([, re]) => re.test(line))?.[0] ?? "UNKNOWN"; counters[level]++; }); rl.on("close", () => console.log(counters)); Key points: - Always buffer the last (potentially incomplete) line and prepend it to the next chunk. - Use readableObjectMode: true on the Transform if you want to push parsed objects downstream instead of raw strings. - Regex patterns are cleaner than chained includes calls and easier to extend (e.g., matching both [WARN] and [WARNING]).
Implement a function `merge_coverage(segments)` that receives a list of segments in the form `(member_id, start_day, end_day)`. For the same `member_id`, merge any segments that overlap or touch at their endpoints (inclusive). Return a dictionary mapping each `member_id` to its list of merged `(start_day, end_day)` tuples. Example: ``` segments = [ ("m1", 1, 3), ("m1", 2, 6), ("m1", 8, 10), ("m1", 15, 18), ] // Expected: { "m1": [(1, 6), (8, 10), (15, 18)] } segments = [("m1", 1, 4), ("m1", 4, 5)] // Expected: { "m1": [(1, 5)] } // touching endpoints are merged ```
The algorithm has three steps: (1) group segments by member_id, (2) sort each group by start_day, (3) iterate and merge any segment whose start is less than or equal to the end of the current merged segment. javascript function mergeCoverage(segments) { // Step 1: group by member_id const groups = {}; for (const [memberId, start, end] of segments) { if (!groups[memberId]) groups[memberId] = []; groups[memberId].push([start, end]); } // Steps 2 & 3: sort then merge const result = {}; for (const [memberId, segs] of Object.entries(groups)) { segs.sort((a, b) => a[0] - b[0]); const merged = [[...segs[0]]]; for (let i = 1; i < segs.length; i++) { const [start, end] = segs[i]; const last = merged[merged.length - 1]; if (start <= last[1]) { // overlapping or touching — extend the current segment last[1] = Math.max(last[1], end); } else { merged.push([start, end]); } } result[memberId] = merged; } return result; } Key detail: the merge condition is start <= last[1] (not strictly <) so that touching endpoints such as [1,4] and [4,5] are also merged into [1,5]. A related helper — detecting whether any two events overlap (without merging) — follows the same sort-then-compare pattern: javascript const hasOverlappingEvents = (events) => { const sorted = [...events].sort((a, b) => a.start - b.start); for (let i = 1; i < sorted.length; i++) { if (sorted[i].start < sorted[i - 1].end) return true; } return false; }; Time complexity: O(n log n) per member due to sorting; space: O(n).
Given an array of event objects, each with a `userId`, `start` timestamp, and `end` timestamp, write a function that returns the total minutes each user has spent across all their events.
Use Array.reduce() to accumulate minutes per userId. For each event, calculate the duration in minutes by converting the start and end strings to Date objects, subtracting them to get milliseconds, then dividing by (1000 * 60). Add that value to the running total for the corresponding userId in the accumulator object. js // dateUtils.js const calcDiffInMinutes = (start, end) => { const startDate = new Date(start); const endDate = new Date(end); return (endDate - startDate) / (1000 * 60); }; // eventService.js const calculateMinutesByUser = (events) => { return events.reduce((acc, event) => { const minutes = calcDiffInMinutes(event.start, event.end); acc[event.userId] = (acc[event.userId] || 0) + minutes; return acc; }, {}); }; // Example const input = [ { userId: 1, start: '2024-01-01T10:00:00', end: '2024-01-01T10:30:00' }, { userId: 1, start: '2024-01-01T11:00:00', end: '2024-01-01T11:45:00' }, { userId: 2, start: '2024-01-01T09:00:00', end: '2024-01-01T10:00:00' } ]; console.log(calculateMinutesByUser(input)); // { '1': 75, '2': 60 }
Given an array of event objects, each with a `start` and `end` time string, write a function that returns `true` if any two events overlap, and `false` otherwise.
Sort the events array by start time using localeCompare (since the times are ISO-format strings that sort lexicographically). Then iterate from the second element and check whether each event's start time is earlier than the previous event's end time. If so, an overlap exists. js const hasOverlappingEvents = (events) => { const sorted = [...events].sort((a, b) => a.start.localeCompare(b.start) ); for (let i = 1; i < sorted.length; i++) { if (sorted[i].start < sorted[i - 1].end) { return true; } } return false; }; // Example const events = [ { start: '10:00', end: '11:00' }, { start: '10:30', end: '11:30' } ]; console.log(hasOverlappingEvents(events)); // true
Given a string, determine whether it is a palindrome. The solution must handle strings that contain spaces, punctuation, and mixed-case characters (e.g., 'A man, a plan, a canal, Panama' should return true).
Use a two-pointer strategy: place one pointer at the start of the string and another at the end. Move both pointers toward the center, skipping any non-alphanumeric characters and comparing the remaining characters in a case-insensitive manner. If all corresponding pairs match by the time the pointers meet, the string is a palindrome.
What types of challenges are commonly given in live coding interviews for React frontend developer positions?
Common React live coding challenges include: implementing a list component and infinite scroll, debugging and fixing errors in an existing component, refactoring a component to properly separate responsibilities, and optimizing the code to avoid unnecessary re-renders.
What is the difference between throttling and debounce when handling JavaScript events?
Both throttling and debounce are techniques used to control how often a function is executed in response to events. Throttling limits execution by blocking or ignoring calls that occur more frequently than a specified interval, ensuring the function runs at most once per period. Debounce, on the other hand, introduces a delay and waits until a certain amount of time has elapsed since the last call before executing the function.
Given a string of lowercase characters, return the index of the first unique (non-repeating) character in the string. If no such character exists, return -1.
Use a frequency map to count how many times each character appears in the string. Then iterate through the string a second time and return the index of the first character whose count is exactly 1. If no such character is found, return -1. A common mistake is storing only whether the character was seen (a boolean flag) rather than how many times it appeared (a count), which makes it impossible to distinguish truly unique characters from repeated ones. javascript function firstUniqueChar(s) { const freq = new Map(); for (let i = 0; i < s.length; i++) { const char = s[i]; freq.set(char, (freq.get(char) || 0) + 1); } for (let i = 0; i < s.length; i++) { if (freq.get(s[i]) === 1) { return i; } } return -1; } // Example: firstUniqueChar("dummyadummy") => 5 (character 'a' at index 5 appears only once) Time complexity: O(n). Space complexity: O(1) since the character set is bounded (26 lowercase letters).
What is the difference in handling exceptions in promises, callbacks, and async/await?
Callbacks use the error-first convention: the first argument is an error object (if any), and you handle it manually in each callback. Promises use .catch() or the second argument of .then() to handle rejections, and errors propagate down the chain. Async/await lets you use standard try/catch blocks around awaited calls, making error handling look synchronous and linear. Unhandled promise rejections can silently fail, whereas uncaught exceptions in async/await will throw in the try/catch scope.
What is debouncing and why is it important in an autocomplete input?
Debouncing delays execution of a function until a specified time has passed since the last invocation. In an autocomplete, it prevents firing an API call on every keystroke; instead, the request is only sent after the user pauses typing (e.g., 300ms). This reduces server load, avoids flickering results, and improves performance. It's typically implemented with setTimeout and clearTimeout inside a useEffect or a custom hook.
What HTML5 feature allows you to create a fully functional component without relying on external frameworks?
Web Components combine three browser APIs: Custom Elements (to define new HTML tags), Shadow DOM (to encapsulate styles and markup), and HTML Templates. Together they let you build reusable, self-contained UI components natively in the browser without any framework.
What JavaScript feature provides a way to track changes in data and update the UI reactively without a framework?
The Proxy object lets you intercept and redefine fundamental operations on an object—property reads, assignments, and deletions. By trapping these operations you can detect mutations and trigger DOM updates. Vue 3 uses exactly this mechanism to power its reactivity system.
What is Tree Shaking in JavaScript bundlers?
Tree Shaking is a dead-code elimination technique used by modern bundlers (Webpack, Rollup, Vite) to remove exports that are never imported anywhere in the application. It relies on ES6 modules (import/export) because their dependency graph is statically analyzable at build time, unlike CommonJS require() calls which are dynamic. The result is a smaller production bundle.
What is the difference between Microtasks and Macrotasks in the JavaScript event loop?
Microtasks (Promise callbacks, queueMicrotask(), MutationObserver) execute immediately after the current synchronous task finishes, before the browser renders or picks up any macrotask. Macrotasks (setTimeout, setInterval, I/O events, UI events) are processed one per event-loop iteration after all pending microtasks are drained. The cycle is: synchronous code → drain microtask queue → render → one macrotask → repeat.
What is the difference between async/await and Promises in JavaScript?
They are different layers of the same mechanism: every async function returns a Promise under the hood, and every await expression is syntactic sugar over .then(). async/await produces more readable, synchronous-looking code and simplifies error handling with try/catch. Promises expose combinators such as Promise.all(), Promise.race(), and Promise.allSettled() that have no direct async/await equivalent.
What causes memory leaks in JavaScript and how do you prevent them?
JavaScript uses garbage collection to free unreachable objects. Leaks occur when references are held unintentionally: forgotten timers/intervals, stale event listeners, closures capturing large objects, and detached DOM nodes. SPAs are especially prone because components mount and unmount frequently. Prevention: always clear timers and remove event listeners in cleanup functions (useEffect cleanup in React, AbortController for fetch requests).
What is a closure in JavaScript?
A closure is a function that retains access to variables from its outer (enclosing) lexical scope even after the outer function has returned. This gives the inner function a persistent reference to its creation context. Closures underpin patterns like data encapsulation, factory functions, memoisation, and React hooks, which rely on closures to capture per-render state.
What is the difference between Service Workers and Web Workers?
Both run JavaScript off the main thread but serve different purposes. Web Workers handle CPU-intensive computation (e.g., image processing) without blocking the UI; they exist only while the page is open and cannot access the DOM. Service Workers act as a network proxy, enabling offline caching, push notifications, and background sync; they persist after all tabs are closed and intercept fetch events.
What is Type Narrowing in TypeScript?
Type Narrowing is the process of refining a variable from a broader union type to a more specific type based on runtime conditional checks such as typeof, instanceof, or in. After the check, the TypeScript compiler knows the precise type inside that branch and enforces the correct methods and properties. For example, after if (typeof value === 'string'), TypeScript treats value as string inside the block.
What is the difference between any, unknown, and never in TypeScript?
any completely opts out of type checking—you can call anything on it; it is convenient but unsafe and should be a last resort. unknown is the type-safe counterpart: you must narrow it (typeof, instanceof, or a type guard) before performing any operation. never represents values that can never exist; it is used for functions that always throw and for exhaustive checks in discriminated unions.
What is the difference between interface and type alias in TypeScript?
Both can describe object shapes, but they differ in capabilities. Interfaces support declaration merging (two declarations with the same name are automatically combined) and use extends for inheritance. Type aliases use intersection (&) for composition and can also represent primitives, union types, tuples, and mapped types—not just object shapes. Prefer interface for public API contracts and type for complex compositions or when merging is undesired.
What is the difference between a Function Declaration and a Function Expression in JavaScript?
A Function Declaration (function foo() {}) is fully hoisted—the engine moves both the name and body to the top of the scope, so it can be called before it appears in source. A Function Expression (const foo = function() {} or an arrow function) assigns a function to a variable; the variable is hoisted but initialised to undefined, so calling it before the assignment throws a TypeError. Arrow functions are a type of function expression and additionally lack their own this binding.
How would you implement a custom version of `Promise.all` without using the native `Promise.all` method? The function must accept an array of values that may be promises or plain values, and must simulate the same resolve/reject behaviour.
The custom function should return a new Promise that resolves with an array of all resolved values once every input has settled successfully, and rejects immediately with the first rejection reason encountered. Each element in the input array should be wrapped with Promise.resolve() so that plain (non-promise) values are handled correctly. Example implementation using forEach (more readable than reduce): javascript function myPromiseAll(items) { return new Promise((resolve, reject) => { const results = []; let resolvedCount = 0; if (items.length === 0) { resolve(results); return; } items.forEach((item, index) => { Promise.resolve(item) .then(value => { results[index] = value; resolvedCount++; if (resolvedCount === items.length) { resolve(results); } }) .catch(reject); }); }); } Key points: - Promise.resolve(item) normalises both promise and non-promise inputs. - Results are stored by index to preserve input order regardless of settlement order. - The outer Promise rejects as soon as any inner promise rejects. - async/await can also be used inside the helper if preferred.
Implement a `debounce` function in JavaScript. It should accept a function and a wait time in milliseconds, and return a new function that delays execution of the original until the specified time has elapsed without any new calls.
A debounce function uses a closure to keep a reference to a timer variable. Each time the returned function is invoked, the previous timer is cancelled with clearTimeout and a new one is scheduled with setTimeout. The original function is only executed once the full wait period passes without another call. javascript function debounce(fn, wait) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, wait); }; } Key concept: the closure preserves the timer reference between calls. Every new invocation resets the countdown, so the wrapped function fires only after a period of inactivity equal to wait milliseconds.
How would you extend a built-in JavaScript Array method, or add a custom method available on all array instances?
In JavaScript you can extend built-in objects such as Array by adding properties or methods directly to their prototype. For example: Array.prototype.myCustomMethod = function() { /* implementation */ }; adds myCustomMethod to every array instance, because all arrays inherit from Array.prototype through the prototype chain. While this technique works, it should be used with caution in production code, since it can conflict with future native methods or third-party libraries that also modify the same prototype.
Write a function that detects whether an array of numbers contains any duplicate values.
Both a HashMap (Java) and a Set (TypeScript/JavaScript) can solve this in O(n) time complexity. TypeScript solution using a Set (clean and idiomatic): typescript function isDuplicated(array: number[]): boolean { const seen = new Set<number>(); for (const value of array) { if (seen.has(value)) { return true; } seen.add(value); } return false; } Java solution using a HashMap: java private static boolean isDuplicated(Integer[] array) { Map<Integer, Integer> tempMemory = new HashMap<>(); for (int i = 0; i < array.length; i++) { if (tempMemory.containsKey(array[i])) { return true; } else { tempMemory.put(array[i], 1); } } return false; } Both approaches iterate the array once, storing each element in a hash-based structure. As soon as a repeated element is found, the function returns true immediately (early exit). If the loop completes without finding a duplicate, the function returns false. Time complexity is O(n); space complexity is O(n) in the worst case.
Given a log file where each line follows the format `<timestamp> - <STATUS> - <message>`, implement a Node.js `Transform` stream named `transformStream` that reads the file and routes each line to a separate output file based on its status (ERROR, DEBUG, INFO, WARNING). Additionally, any WARNING line that contains a CPU percentage value of 98% or higher should also be written to a HIGHCPU log file.
Use the Node.js stream.Transform class to process the file chunk by chunk, buffering incomplete lines across chunks by splitting on newlines and keeping the last (potentially incomplete) segment in a buffer. For each complete line, split on - to extract the status field and message. Then write the message to the corresponding output write stream. For WARNING lines, additionally check with a regex (/\d+%/) whether the percentage value is >= 98 and, if so, also write to the HIGHCPU stream. Example implementation: js function captureLogs(includeSignature = false) { const fs = require('fs'); const { Transform } = require('stream'); const input = fs.createReadStream('./data/logfile.log', { encoding: 'utf8' }); const outputs = { ERROR: fs.createWriteStream('./data/error.log'), DEBUG: fs.createWriteStream('./data/debug.log'), INFO: fs.createWriteStream('./data/info.log'), WARNING: fs.createWriteStream('./data/warning.log'), HIGHCPU: fs.createWriteStream('./data/highcpu.log'), }; // MUST be named transformStream const transformStream = new Transform({ decodeStrings: false, transform(chunk, _, cb) { this.buffer = (this.buffer || '') + chunk; const lines = this.buffer.split('\n'); this.buffer = lines.pop(); for (const line of lines) { if (!line.trim()) continue; const parts = line.split(' - '); if (parts.length < 3) continue; const status = parts[1].trim(); const message = parts.slice(2).join(' - ').trim(); if (outputs[status]) { outputs[status].write(message + '\n'); } if (status === 'WARNING') { const match = message.match(/(\d+)%/); if (match && Number(match[1]) >= 98) { outputs.HIGHCPU.write(message + '\n'); } } this.push(line + '\n'); } cb(); }, }); transformStream.on('end', () => { if (includeSignature) { const signature = { signature: generateSignature() }; fs.writeFileSync('./data/signature.json', JSON.stringify(signature)); } Object.values(outputs).forEach((s) => s.end()); }); input.pipe(transformStream); } Key points: (1) Buffer incomplete chunks across transform calls to avoid splitting a line mid-chunk. (2) Use outputs[status] lookup instead of a long if-else chain for clarity. (3) The HIGHCPU check is additive — the line is still written to the WARNING file as well. (4) Close all write streams in the end event handler.

Async & OOP

What is a Promise and what states can it have?
A Promise represents the eventual result of an async operation. It is pending while in progress, fulfilled on success (handled with .then), or rejected on failure (handled with .catch); .finally always runs. Promise.all runs many in parallel and rejects if any fails, Promise.allSettled waits for all regardless of outcome, and Promise.race resolves with the first to settle.
How does async/await relate to Promises, and sequential vs parallel awaits?
async/await is syntactic sugar over Promises: an async function always returns a Promise, and await pauses inside it until the Promise settles, with errors handled by try/catch. Awaiting in sequence (await a; await b) is slow; for independent work run them in parallel with await Promise.all([a, b]).
What do you need to know about JavaScript classes?
A class has a constructor that sets instance fields with this, instance methods, and static members accessed on the class itself (MathUtils.PI), not on instances. Getters/setters expose computed or guarded properties (get fahrenheit(), set celsius(v) with validation). Static factory methods like ApiResponse.ok(body) are a clean way to build common instances.
How do inheritance and polymorphism work in JavaScript?
A subclass uses extends and must call super(...) in its constructor before using this; it can override methods and call super.method() to extend behavior. Polymorphism means the same call behaves differently per class — e.g. a list of Vehicle/Car/Motorbike each implementing info(). instanceof checks the prototype chain.
How do you encapsulate state in JavaScript?
Use private class fields prefixed with # (this.#balance) so they cannot be read or written from outside; expose controlled access through getters and methods that validate input. The classic pre-# approach is a closure (a factory function with a private variable). Returning a copy (e.g. [...this.#history]) avoids leaking internal references.
Which design patterns show up often in test frameworks?
Singleton (one shared instance, e.g. a Config), Factory (build configured objects, e.g. an HttpClient per environment), Builder (fluent step-by-step construction, e.g. a RequestBuilder), and Observer/EventEmitter (subscribe to events like a test runner emitting pass/fail). Dependency injection — passing collaborators in rather than hard-coding them — makes all of these easy to mock.