| title | Tricky JavaScript Code Snippets Questions Asked in the Interview 2026 (ES6/ES7/ES8/ES9) |
|---|---|
| description | Sharpen your JavaScript skills with these tricky 2026 interview code snippets. Covering closures, hoisting, async patterns, destructuring, generators, and modern ES6+ features with detailed explanations. |
| githubPath | https://github.com/stackinterviewteam/JavaScript-Interview-Questions |
Updated May 25, 2026
In this article, we cover a curated set of tricky JavaScript interview questions for 2026 — including output-guessing challenges, coding problems, and MCQs based on modern ES6+ syntax.
Whether you're targeting a FAANG-level role or a fast-growing startup, understanding these nuances will help you stand out. We cover closures, this binding, async behavior, destructuring edge cases, generators, and more.
*Discover the answers by clicking on the questions.
let p = {};
let q = { id: "q" };
let r = { id: "r" };
p[q] = 10;
p[r] = 20;
console.log(p[q]);View Answer
The output is 20.
When a non-string value is used as an object key, JavaScript converts it to a string using .toString(). Both q and r are plain objects, so both convert to "[object Object]".
That means p[q] and p[r] refer to the same key. The second assignment (p[r] = 20) overwrites the first (p[q] = 10), so reading p[q] returns 20.
The object p internally looks like:
{ "[object Object]": 20 }let m = { val: "original" };
let n = m;
let o = { ...m };
m.val = "updated";
console.log(n.val);
console.log(o.val);View Answer
Output:
updated
original
n = m creates a reference — both n and m point to the same object in memory. So mutating m.val also changes n.val.
o = { ...m } uses the spread operator, which performs a shallow copy. o gets its own copy of the properties at that moment. Future changes to m don't affect o.
const config = {
env: "production",
getEnv: function () {
return this.env;
},
};
const fn = config.getEnv;
console.log(config.getEnv());
console.log(fn());View Answer
Output:
production
undefined
When config.getEnv() is invoked as a method on the config object, this inside it refers to config, so this.env is "production".
When the function reference is stored in fn and called as a standalone function (fn()), this no longer refers to config. In non-strict mode, this defaults to the global object (window in browsers, global in Node), which has no env property — so the result is undefined.
const multiply = (x, y) => x * y;
console.log(multiply(3, 4));
console.log(multiply(3));View Answer
Output:
12
NaN
The first call passes both arguments, so 3 * 4 = 12.
The second call passes only one argument (x = 3), so y is undefined. 3 * undefined evaluates to NaN (Not a Number) because arithmetic with undefined produces NaN.
const items = ["a", "b", "c"];
for (var i = 0; i < items.length; i++) {
setTimeout(() => {
console.log(items[i]);
}, 500);
}View Answer
Output:
undefined
undefined
undefined
Because var is function-scoped (not block-scoped), all three setTimeout callbacks share the same i variable. By the time the callbacks execute after 500ms, the loop has finished and i is 3. Since items[3] doesn't exist, it's undefined — printed three times.
To fix this, use let instead of var, which creates a new binding per iteration.
const nums = [10, 20, 30];
const doubled = nums.map((n) => n * 2);
console.log(doubled);
console.log(nums);View Answer
Output:
[20, 40, 60]
[10, 20, 30]
Array.map() returns a new array — it never mutates the original. Each element is multiplied by 2 in doubled, while nums stays unchanged.
let count = 0;
(function loop() {
if (count++ < 3) {
setTimeout(loop, 0);
}
})();
console.log(count);View Answer
Output:
1
The IIFE runs synchronously. When count++ is evaluated, count is 0 (post-increment means the check uses 0, then increments to 1). The condition 0 < 3 is true, so setTimeout(loop, 0) is scheduled. But setTimeout is async — it gets pushed to the task queue.
console.log(count) runs immediately after the IIFE (still on the call stack), at which point count is 1.
The recursive loop calls will continue asynchronously afterwards, but console.log has already executed.
📖 Want to go deeper on async execution, closures, and hoisting? Check out the full JavaScript Interview Questions 2026 guide on StackInterview.
let user = { name: "Alice", score: 100 };
let clone = user;
clone.name = "Bob";
clone = { name: "Charlie", score: 200 };
console.log(user.name);
console.log(clone.name);View Answer
Output:
Bob
Charlie
Initially, clone = user — both point to the same object. So clone.name = "Bob" mutates the shared object, making user.name also "Bob".
Then clone = { name: "Charlie", score: 200 } reassigns the clone variable to a brand new object. user still points to the original (now mutated) object with name: "Bob".
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
let arr3 = arr1;
console.log(arr1 == arr2);
console.log(arr1 === arr2);
console.log(arr1 === arr3);
console.log(arr3 == arr2);View Answer
Output:
false
false
true
false
In JavaScript, arrays and objects are compared by reference, not by value. arr1 and arr2 are separate objects in memory, even though they hold identical values — so both == and === return false.
arr3 = arr1 makes arr3 point to the same object as arr1, so arr1 === arr3 is true.
arr3 == arr2 is still false because arr3 references arr1's object, not arr2's.
var counter = 0;
for (let j = 0; j < 3; j++) {
setTimeout(() => {
counter++;
console.log(counter);
}, 0);
}View Answer
Output:
1
2
3
Unlike var, let creates a block-scoped binding per iteration — but here it doesn't matter because counter is a var in the outer scope and is shared across all callbacks.
Each setTimeout callback increments counter by 1 when it runs. All three callbacks are queued and run sequentially after the synchronous code finishes. So they log 1, 2, 3 in order.
const points = [
{ x: 1 },
{ x: 2 },
{ x: 3 },
];
points.forEach((pt) => (pt.x *= 10));
console.log(points[0].x, points[1].x, points[2].x);View Answer
Output:
10 20 30
forEach iterates over the array and the callback receives a reference to each object. Mutating pt.x directly modifies the original objects inside the points array. So after the loop, all x values are multiplied by 10.
let total = 100;
function calculate() {
var total = 50;
return total * 2;
}
console.log(calculate());
console.log(total);View Answer
Output:
100
100
Inside calculate(), a local total is declared with var and assigned 50. The function returns 50 * 2 = 100. This local variable is completely separate from the global total = 100.
After the function call, the outer total is still 100, untouched.
const defaults = { theme: "dark", lang: "en" };
const custom = { ...defaults, lang: "fr", fontSize: 14 };
console.log(defaults.lang);
console.log(custom.lang);
console.log(custom.fontSize);View Answer
Output:
en
fr
14
The spread operator copies all properties from defaults into the new custom object. Properties listed after the spread override the copied values — so lang: "fr" replaces lang: "en". fontSize: 14 is a new property added only to custom.
The original defaults object is never mutated.
function greet(name = "stranger", greeting = `Hello, ${name}!`) {
return greeting;
}
console.log(greet());
console.log(greet("Riya"));
console.log(greet(undefined, "Hey there!"));View Answer
Output:
Hello, stranger!
Hello, Riya!
Hey there!
Default parameter values in ES6 can reference earlier parameters. When name is omitted (or undefined), it falls back to "stranger", and greeting is computed as `Hello, stranger!`.
When "Riya" is passed, name = "Riya" and greeting = "Hello, Riya!".
When undefined is explicitly passed for name, the default "stranger" kicks in, but the second argument "Hey there!" overrides the greeting default entirely.
const role = "admin";
const level = 5;
const profile = { role, level, active: true };
console.log(profile);View Answer
Output:
{ role: "admin", level: 5, active: true }ES6 shorthand property syntax allows you to write { role, level } instead of { role: role, level: level } when the variable name matches the desired key name. Additional explicit properties like active: true can be mixed in freely.
const colors = ["red", "green", "blue"];
const [primary, , tertiary] = colors;
console.log(primary);
console.log(tertiary);View Answer
Output:
red
blue
Array destructuring assigns values positionally. The ,, (two commas with no variable in between) skips the second element ("green"). primary gets "red" (index 0) and tertiary gets "blue" (index 2).
console.log(typeof NaN);
console.log(NaN === NaN);
console.log(Number.isNaN(NaN));
console.log(Number.isNaN("hello"));View Answer
Output:
number
false
true
false
typeof NaN is "number" — a well-known JavaScript quirk. NaN stands for "Not a Number" but belongs to the number type.
NaN === NaN is false — NaN is the only value in JS that is not equal to itself.
Number.isNaN(NaN) correctly returns true.
Number.isNaN("hello") returns false — unlike the older global isNaN(), Number.isNaN() does not coerce its argument. It only returns true for actual NaN values.
📖 Tripped up by
typeofquirks and NaN behaviour? The JavaScript Interview Questions 2026 guide on StackInterview covers all these edge cases in depth.
18. Write a function that returns the product of all negative numbers in an array. Beginner 🚂
Answer:
function productOfNegatives(numbers) {
let product = 1;
let hasNegative = false;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] < 0) {
product *= numbers[i];
hasNegative = true;
}
}
return hasNegative ? product : 0;
}
// Example usage:
console.log(productOfNegatives([-2, 3, -4, 5, -1])); // Output: -8
console.log(productOfNegatives([1, 2, 3])); // Output: 0The function iterates through the array, multiplying together only the negative numbers. A hasNegative flag tracks whether any negatives were found — if none were, we return 0 instead of the misleading 1.
19. Write a function that reverses each word in a sentence individually, without reversing the sentence itself. Intermediate 🚁
Answer:
function reverseEachWord(sentence) {
return sentence
.split(" ")
.map((word) => word.split("").reverse().join(""))
.join(" ");
}
// Example usage:
console.log(reverseEachWord("Hello World from JavaScript"));
// Output: "olleH dlroW morf tpircSavaJ"The sentence is split into words by spaces. Each word is converted to an array of characters, reversed, and joined back. Finally, all words are joined with spaces to reconstruct the sentence.
20. Write a function that groups an array of strings by their first letter. Beginner 🚂
Answer:
function groupByFirstLetter(arr) {
return arr.reduce((acc, word) => {
const key = word[0].toUpperCase();
if (!acc[key]) acc[key] = [];
acc[key].push(word);
return acc;
}, {});
}
// Example usage:
const words = ["apple", "avocado", "banana", "blueberry", "cherry", "apricot"];
console.log(groupByFirstLetter(words));
// Output:
// { A: ['apple', 'avocado', 'apricot'], B: ['banana', 'blueberry'], C: ['cherry'] }We use reduce to build an accumulator object. For each word, we extract its first letter (uppercased) as the key, and push the word into the matching array, creating the array if it doesn't exist yet.
21. Write a function that checks if two strings are anagrams of each other. Beginner 🚂
Answer:
function areAnagrams(str1, str2) {
const normalize = (s) => s.toLowerCase().split("").sort().join("");
return normalize(str1) === normalize(str2);
}
// Example usage:
console.log(areAnagrams("listen", "silent")); // Output: true
console.log(areAnagrams("hello", "world")); // Output: falseAn anagram uses the same characters in a different order. By lowercasing, splitting into characters, sorting alphabetically, and joining back — we get a "normalized signature" for each string. If both signatures match, the strings are anagrams.
22. Write a function that returns the most frequently occurring element in an array. Beginner 🚂
Answer:
function mostFrequent(arr) {
const freq = {};
for (const item of arr) {
freq[item] = (freq[item] || 0) + 1;
}
return Object.keys(freq).reduce((a, b) => (freq[a] > freq[b] ? a : b));
}
// Example usage:
const votes = ["Alice", "Bob", "Alice", "Charlie", "Bob", "Alice"];
console.log(mostFrequent(votes)); // Output: "Alice"We first build a frequency map, counting each element's occurrences. Then we use reduce over the keys to find the one with the highest count.
23. Write a function that deeply flattens a nested array. Beginner 🚂
Answer:
function deepFlatten(arr) {
return arr.reduce((flat, item) => {
return flat.concat(Array.isArray(item) ? deepFlatten(item) : item);
}, []);
}
// Example usage:
console.log(deepFlatten([1, [2, [3, [4]], 5], 6]));
// Output: [1, 2, 3, 4, 5, 6]
// ES2019 native alternative:
// [1, [2, [3]]].flat(Infinity)The function recursively processes each element. If an element is itself an array, it calls deepFlatten on it again before concatenating. The recursion bottoms out when all elements are primitives.
ES2019 introduced Array.flat(Infinity) as a built-in way to achieve the same result.
24. Write a function that implements a basic `pipe` utility — composing functions left to right. Intermediate 🚁
Answer:
function pipe(...fns) {
if (fns.length === 0) return (value) => value;
return (value) => fns.reduce((acc, fn) => fn(acc), value);
}
// Example usage:
const double = (x) => x * 2;
const addTen = (x) => x + 10;
const square = (x) => x * x;
const transform = pipe(double, addTen, square);
console.log(transform(3)); // (3*2 + 10)² = 16² = 256
// Edge case: pipe with no functions acts as identity
const identity = pipe();
console.log(identity(42)); // 42pipe takes any number of functions and returns a new function. When invoked with a value, it passes that value through each function from left to right, feeding each result as the input to the next — similar to how Unix pipes work. The empty-array guard returns an identity function rather than throwing on fns.reduce with no initial value.
25. Write a function that validates whether a string contains balanced brackets. Intermediate 🚁
Answer:
function isBalanced(str) {
const map = { ")": "(", "}": "{", "]": "[" };
const stack = [];
for (const char of str) {
if ("({[".includes(char)) {
stack.push(char);
} else if (")}]".includes(char)) {
if (stack.pop() !== map[char]) return false;
}
}
return stack.length === 0;
}
// Example usage:
console.log(isBalanced("({[hello]})")); // Output: true
console.log(isBalanced("({[})")); // Output: false
console.log(isBalanced("")); // Output: trueWe use a stack. Opening brackets are pushed in. When a closing bracket is encountered, we pop the last item from the stack and check if it matches the expected opening pair. If anything mismatches, or the stack isn't empty at the end, the brackets are not balanced.
26. Write a function that converts an object into a query string. Expert 🚀
Answer:
function toQueryString(params) {
return Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join("&");
}
// Example usage:
const filters = { search: "hello world", page: 2, sort: "asc" };
console.log(toQueryString(filters));
// Output: "search=hello%20world&page=2&sort=asc"
// Handles null/undefined gracefully:
console.log(toQueryString({ q: "js", debug: undefined }));
// Output: "q=js"Object.entries() returns [key, value] pairs. A filter step drops null and undefined values that would otherwise produce key=null in the string. Each remaining pair is URL-encoded with encodeURIComponent.
⚠️ Limitation: this implementation handles flat objects with primitive values only. Array values (e.g.{ tags: ["a","b"] }) and nested objects require a recursive approach or a library likeqs.
27. Write a function that debounces another function. Expert 🚀
Answer:
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// Example usage:
const log = debounce((msg) => console.log(msg), 300);
log("first");
log("second");
log("third"); // Only "third" will be logged after 300msDebouncing ensures a function is only executed after a specified delay following the last call. Each new call resets the timer using clearTimeout. This is extremely common in search inputs, resize handlers, and scroll events to reduce unnecessary executions.
fn.apply(this, args) preserves the calling context and any arguments passed in.
🚀 Debounce, throttle, memoization — these come up constantly in senior roles. See how they fit into the bigger picture in the Senior Frontend Interview Topics 2026 guide on StackInterview.
28. What is the output of this code? Beginner 🚂
const x = 5;
{
const x = 15;
console.log(x);
}
console.log(x);Output:
15
5
const (like let) is block-scoped. The inner const x = 15 exists only within the curly-brace block. Outside that block, the outer const x = 5 is still in scope. The two x variables are completely independent.
29. Predict the output. Beginner 🚂
const settings = { volume: 80, brightness: 60, wifi: true };
const { volume, wifi } = settings;
console.log(volume, wifi);Output:
80 true
Object destructuring extracts named properties from the object and assigns them to local variables with matching names. volume gets 80, wifi gets true, and brightness is simply not destructured — it stays in settings.
30. What is the output? Beginner 🚂
const first = [1, 2, 3];
const second = [4, 5, 6];
const merged = [...first, 0, ...second];
console.log(merged);Output:
[1, 2, 3, 0, 4, 5, 6]
The spread operator unpacks the elements of first and second into the new merged array. The literal 0 is inserted between the two spreads, landing at index 3. Original arrays are not affected.
31. What does this output? Beginner 🚂
const original = [10, 20, 30];
const copy = [...original];
copy[0] = 99;
console.log(original[0]);
console.log(copy[0]);Output:
10
99
[...original] creates a shallow copy — a new array with the same primitive values. Changing copy[0] does not affect original[0] because primitive values (numbers) are copied by value, not by reference.
(Note: if the elements were objects, this would behave differently — nested objects are still shared references in a shallow copy.)
32. What is the output of the following code? Beginner 🚂
function run() {
console.log(value);
let value = 42;
}
run();Output:
ReferenceError: Cannot access 'value' before initialization
Variables declared with let and const are subject to the Temporal Dead Zone (TDZ). Even though the declaration is hoisted, the variable is not accessible until the line where it is initialized. Accessing it before that line throws a ReferenceError.
This is different from var, which would have logged undefined due to initialization hoisting.
📖 The TDZ catches a lot of developers off guard. Brush up on
varvsletvsconsthoisting rules in the JavaScript Interview Questions 2026 guide on StackInterview.
33. Predict the output. Beginner 🚂
const base = [1, 2, 3];
const ref = base;
ref.push(4);
console.log(base);
console.log(ref);Output:
[1, 2, 3, 4]
[1, 2, 3, 4]
ref = base does not copy the array — it copies the reference. Both base and ref point to the exact same array in memory. Calling ref.push(4) mutates that shared array, so both variables reflect the change.
To avoid this, use const ref = [...base] or base.slice() to create an independent copy.
34. What does this code output? Intermediate 🚁
const employees = [
{ id: 1, dept: "Engineering" },
{ id: 2, dept: "Design" },
{ id: 3, dept: "Engineering" },
];
employees.sort((a, b) => (a.dept < b.dept ? -1 : 1));
console.log(employees.map((e) => e.dept));Output:
["Design", "Engineering", "Engineering"]
The comparator returns -1 when a.dept comes before b.dept alphabetically (ascending order). "Design" < "Engineering" lexicographically, so it moves to the front.
sort mutates the original array in place, and then map extracts just the dept strings.
35. What is the output of these `typeof` checks? Beginner 🚂
console.log(typeof 0);
console.log(typeof "");
console.log(typeof false);
console.log(typeof Symbol());
console.log(typeof undefined);
console.log(typeof null);
console.log(typeof {});
console.log(typeof []);
console.log(typeof function () {});Output:
number
string
boolean
symbol
undefined
object
object
object
function
Key quirks to note:
typeof nullis"object"— a historical bug in JavaScript that can't be corrected for backward compatibility.typeof []is"object"— arrays are objects. UseArray.isArray([])to specifically check for arrays.typeof Symbol()is"symbol"— Symbols introduced in ES6 have their own type.typeof function() {}is"function"— even though functions are objects internally,typeofhas a special case for them.
36. Write a function that accurately identifies the true type of any value, returning "array" for arrays and "null" for null. Expert 🚀
console.log(trueType(42));
console.log(trueType("hi"));
console.log(trueType([]));
console.log(trueType({}));
console.log(trueType(null));
console.log(trueType(undefined));
console.log(trueType(() => {}));Answer:
const trueType = (val) => {
if (val === null) return "null";
if (val === undefined) return "undefined";
if (Array.isArray(val)) return "array";
return typeof val;
};Output:
number
string
array
object
null
undefined
function
typeof alone is insufficient because it reports both arrays and null as "object". This utility handles each edge case explicitly:
nullis checked first with strict equality —typeof null === "object"is a historical JS bug.undefinedis checked before any property access —undefined?.constructorwould returnundefined, not throw, butundefined.constructor.namewithout optional chaining does throw aTypeError. The original one-liner usingval?.constructor.namesilently returnsundefinedfor bothnullandundefinedwithout the explicit guards — making the guards load-bearing, not optional.Array.isArray()correctly identifies arrays before thetypeoffallback.- Everything else falls through to
typeof, which correctly handles"number","string","boolean","function","symbol", and"bigint".
37. What will this code output? Beginner 🚂
function init() {
console.log(result);
var a = 5;
let b = 10;
const c = 15;
var result = a + b + c;
}
init();- A: 30
- B: undefined
- C: ReferenceError
- D: TypeError
Answer — B: undefined
var declarations are hoisted to the top of the function with an initial value of undefined. So when console.log(result) executes, result exists in scope but hasn't been assigned yet — it's undefined.
Effectively, JavaScript interprets the code like:
function init() {
var result; // hoisted
console.log(result); // undefined
var a = 5;
let b = 10;
const c = 15;
result = a + b + c; // assigned later
}let and const are also hoisted, but they remain in the Temporal Dead Zone until their declaration line — accessing them before would throw a ReferenceError.
📖 Hoisting is one of the most tested JavaScript concepts in interviews. Get the full breakdown in the JavaScript Interview Questions 2026 guide on StackInterview.
38. What will be the output? Beginner 🚂
let score = 50;
function adjust() {
if (true) {
let score = 100;
console.log(score);
}
console.log(score);
}
adjust();- A: 100, 50
- B: 100, 100
- C: 50, 50
- D: 50, 100
Answer — A: 100, 50
let is block-scoped. The score = 100 inside the if block is a new, separate variable that shadows the outer score = 50 only within that block.
- Inside the
ifblock: logs100(innerscore). - Outside the block: logs
50(outerscore, unchanged).
39. What is the output? Intermediate 🚁
const api = {
baseUrl: "https://example.com",
getUrl: function () {
return this.baseUrl;
},
};
Object.freeze(api);
api.baseUrl = "https://hacked.com";
api.version = "v2";
console.log(api.baseUrl);
console.log(api.version);- A: "https://hacked.com", "v2"
- B: "https://example.com", undefined
- C: TypeError
- D: ReferenceError
Answer — B: "https://example.com", undefined
Object.freeze() prevents any additions, deletions, or modifications to the object's properties.
api.baseUrl = "https://hacked.com"— silently ignored in non-strict mode; throwsTypeErrorin strict mode.api.version = "v2"— also silently ignored;versionis never added.
So api.baseUrl remains "https://example.com", and api.version is undefined.
40. What is the output? Beginner 🚂
function outer() {
const prefix = "Hello";
function inner(name) {
return `${prefix}, ${name}!`;
}
return inner("World");
}
console.log(outer());- A: "Hello, World!"
- B: undefined, World!
- C: ReferenceError
- D: "prefix, World!"
Answer — A: "Hello, World!"
inner is defined inside outer and has access to outer's scope through closure. Even though prefix is declared in the outer function, inner can read it via the scope chain.
outer() invokes inner("World"), which returns "Hello, World!". That string is then returned by outer() and logged.
41. What is the output? Beginner 🚂
var flag = true;
function check() {
console.log(flag);
if (false) {
var flag = false;
}
}
check();- A: true
- B: false
- C: undefined
- D: ReferenceError
Answer — C: undefined
This is a classic var hoisting trap. Even though the if (false) block never executes, the var flag declaration inside it is still hoisted to the top of check(). This creates a local flag variable that shadows the global one.
At the time console.log(flag) runs, the local flag exists but hasn't been assigned — so it's undefined.
📖
varhoisting inside dead code blocks is a favourite interview trick. Master it alongside other scope quirks in the JavaScript Interview Questions 2026 guide on StackInterview.
42. What does this output? Intermediate 🚁
const data = {
user: "John",
address: {
city: "Mumbai",
pin: 400001,
},
};
const {
user,
address: { city, pin: postalCode },
} = data;
console.log(user, city, postalCode);- A: John Mumbai 400001
- B: John Mumbai postalCode
- C: undefined undefined undefined
- D: SyntaxError
Answer — A: John Mumbai 400001
This is nested destructuring with renaming:
userextractsdata.user→"John".address: { city }dives intodata.addressand extractscity→"Mumbai".pin: postalCodeextractsdata.address.pinand assigns it to a variable namedpostalCode→400001.
The key address itself is not bound to any variable — only its nested properties are.
43. What is the output of this generator code? Expert 🚀
function* counter() {
let n = 0;
while (true) {
yield n++;
}
}
const gen = counter();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);- A: 0, 1, 2
- B: 1, 2, 3
- C: 0, 0, 0
- D: Infinite loop — crashes the browser
Answer — A: 0, 1, 2
Even though the generator has a while (true) loop, it does not run forever on each call. Generators are lazy — they pause at every yield and resume only when .next() is called.
- First
.next(): enters loop, yields0(n becomes 1), pauses. - Second
.next(): resumes, yields1(n becomes 2), pauses. - Third
.next(): resumes, yields2(n becomes 3), pauses.
This pattern is useful for creating infinite sequences without blocking execution.
44. Write a function that supports both curried and multi-argument calling for summing numbers. Expert 🚀
console.log(add(1, 2, 3).value); // 6
console.log(add(1)(2)(3).value); // 6
console.log(add(1, 2)(3).value); // 6Answer:
function add(...args) {
const total = args.reduce((sum, n) => sum + n, 0);
function next(...moreArgs) {
return add(total, ...moreArgs);
}
next.value = total;
return next;
}
console.log(add(1, 2, 3).value); // 6
console.log(add(1)(2)(3).value); // 6
console.log(add(1, 2)(3).value); // 6The add function:
- Sums all arguments passed using
reduce. - Returns a
nextfunction that, when called again, recursively callsaddwith the accumulated total plus the new arguments. - Exposes the current total via
.valueon the returned function.
This supports any combination of batched or curried calls.
🚀 Currying and function composition are staple senior frontend interview topics. See more patterns like this in the Senior Frontend Interview Topics 2026 guide on StackInterview.
45. Understanding the `.reduce()` Method Variations Beginner 🚂
const transactions = [200, -50, 300, -100, 150];
// Task 1: Get the final balance (sum of all transactions)
// Task 2: Get the total of only deposits (positive values)
// Task 3: Find the largest single transaction
// Task 4: Count how many transactions are withdrawals (negative)Answer:
Task 1: Final balance
const balance = transactions.reduce((acc, val) => acc + val, 0);
console.log(balance); // 500Task 2: Total deposits only
const totalDeposits = transactions.reduce((acc, val) => (val > 0 ? acc + val : acc), 0);
console.log(totalDeposits); // 650Task 3: Largest transaction
const largest = transactions.reduce((max, val) => (val > max ? val : max), -Infinity);
console.log(largest); // 300Task 4: Count withdrawals
const withdrawalCount = transactions.reduce((count, val) => (val < 0 ? count + 1 : count), 0);
console.log(withdrawalCount); // 2These variations show how reduce can serve as a Swiss-army knife for array operations — replacing filter + length, Math.max, and forEach counters with a single traversal.
🚀 Knowing when to reach for
reduceoverfilter/map/forEachis a sign of a senior engineer. Explore more advanced patterns in the Senior Frontend Interview Topics 2026 guide on StackInterview.
46. Identify and fix the bug in this code. Beginner 🚂
let prices = [100, 200, 300, 400, 500];
prices = prices.map((price) => price * 1.1);
console.log(prices.reduce((sum, price) => sum + price));Answer:
// Output: 1650.0000000000002Explanation:
pricesstarts as[100, 200, 300, 400, 500]..map(price => price * 1.1)applies a 10% increase to each. Due to floating point representation,100 * 1.1is not exactly110in JavaScript — it's110.00000000000001. So the mapped array is[110.00000000000001, 220.00000000000003, 330.00000000000006, 440.00000000000006, 550](values vary by engine)..reduce((sum, price) => sum + price)sums all values, accumulating the floating point error.- The actual output is
1650.0000000000002, not1650.
Two bugs to flag here:
- Floating point: multiplying by
1.1introduces IEEE 754 precision errors. UseMath.round(price * 1.1 * 100) / 100or a decimal library for financial calculations. - Missing initial value: omitting the second argument to
reduceuses the first element as the starting accumulator, which throws on an empty array. Always pass0:.reduce((sum, price) => sum + price, 0).
// Corrected version:
let prices = [100, 200, 300, 400, 500];
prices = prices.map((price) => Math.round(price * 1.1 * 100) / 100);
console.log(prices.reduce((sum, price) => sum + price, 0)); // 165047. Write a JavaScript function that takes an array and returns only the unique values — without using `Set` or `filter`. Beginner 🚂
*Cannot use Set, filter, or includes.
Set, filter, or includes.Answer:
Approach 1: Using an object as a frequency map
function getUniques(arr) {
const seen = Object.create(null); // avoids prototype key collisions
const result = [];
for (let i = 0; i < arr.length; i++) {
const key = String(arr[i]);
if (!Object.prototype.hasOwnProperty.call(seen, key)) {
seen[key] = true;
result.push(arr[i]);
}
}
return result;
}
console.log(getUniques([1, 2, 2, 3, 1, 4])); // [1, 2, 3, 4]
console.log(getUniques([0, false, "", 0, false])); // [0, false, ""]
⚠️ Why the original!seen[arr[i]]is buggy: falsy values like0,"", andfalseevaluate to falsy even after being added toseen, so they'd be incorrectly treated as "not seen" on subsequent iterations and pushed again. UsinghasOwnPropertychecks for key existence regardless of the stored value.
Approach 2: Using reduce with an object tracker
function getUniquesReduce(arr) {
return arr.reduce((acc, val) => {
const key = String(val);
if (!Object.prototype.hasOwnProperty.call(acc.seen, key)) {
acc.seen[key] = true;
acc.result.push(val);
}
return acc;
}, { seen: Object.create(null), result: [] }).result;
}
console.log(getUniquesReduce([5, 5, 6, 7, 6])); // [5, 6, 7]
console.log(getUniquesReduce([0, "", false, 0, ""])); // [0, "", false]Both approaches use a null-prototype object (Object.create(null)) as a lookup map — this avoids accidental collisions with inherited prototype keys like "constructor" or "toString". Key existence is checked with hasOwnProperty rather than truthiness to correctly handle falsy values.