JavaScript Advanced and Coding Interview Questions and Answers
15 advanced and hands-on questions: currying, polyfills, memoization, flattening, deep equality, memory leaks, and output puzzles.
1. What is a higher-order function?
A function that takes another function as an argument or returns one. map, filter, reduce, setTimeout, and event listeners are examples. They let you write behavior as composable pieces.
2. What is currying? Write a curry function.
Currying turns a function of several arguments into a chain of functions that each take one or more arguments.
function curry(fn) { return function curried(...args) { if (args.length >= fn.length) return fn.apply(this, args); return function (...more) { return curried.apply(this, args.concat(more)); }; }; } const add = curry(function (a, b, c) { return a + b + c; }); add(1)(2)(3); // 6 add(1, 2)(3); // 63. How would you implement memoization?
Cache the result of a function by its arguments so repeated calls are instant. Only memoize pure functions.
function memoize(fn) { const cache = new Map(); return function (...args) { const key = JSON.stringify(args); if (!cache.has(key)) cache.set(key, fn.apply(this, args)); return cache.get(key); }; }4. Write a function to flatten a nested array.
The built-in answer is arr.flat(Infinity). A manual recursive version shows you understand the idea:
function flatten(arr) { return arr.reduce(function (result, item) { return result.concat(Array.isArray(item) ? flatten(item) : item); }, []); } flatten([1, [2, [3, [4]]]]); // [1, 2, 3, 4]5. How would you polyfill Function.prototype.bind?
Return a function that calls the original with the chosen this and the combined arguments.
Function.prototype.myBind = function (context, ...boundArgs) { const fn = this; return function (...args) { return fn.apply(context, boundArgs.concat(args)); }; };A complete polyfill also handles use with new, which interviewers may ask as a follow-up.
6. How do you remove duplicates from an array?
For primitives, use a Set: [...new Set(arr)]. For objects, dedupe by a key using a Map or a Set of seen keys.
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3] function uniqueBy(items, key) { const seen = new Set(); return items.filter(function (item) { if (seen.has(item[key])) return false; seen.add(item[key]); return true; }); }7. Why does this loop print 3 three times, and how do you fix it?
for (var i = 0; i < 3; i++) { setTimeout(function () { console.log(i); }, 0); }var is function scoped, so all callbacks share one i, which is 3 by the time they run. Use let, which creates a new binding for each iteration, or wrap the body in a function that captures the value.
8. How would you check whether two objects are deeply equal?
Compare primitives with Object.is, then check both are objects, compare their key counts, and recursively compare each key. Handle special cases like arrays, dates, and cycles if asked.
function deepEqual(a, b) { if (Object.is(a, b)) return true; if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) return false; const keysA = Object.keys(a); if (keysA.length !== Object.keys(b).length) return false; return keysA.every(function (key) { return deepEqual(a[key], b[key]); }); }9. What causes memory leaks in JavaScript, and how do you find them?
Common causes: forgotten timers and event listeners, detached DOM nodes still referenced, accidental globals, unbounded caches, and closures holding large objects. Find them with the browser DevTools Memory panel by comparing heap snapshots over time, or the Performance monitor, and fix them by removing listeners, clearing timers, and using WeakMap or WeakRef for caches.
10. How does garbage collection work in JavaScript?
Engines use reachability: an object is kept while it can be reached from roots such as global variables and the current call stack, and is collected otherwise. Modern engines use a mark-and-sweep approach with generational and incremental techniques so collection causes short pauses. You cannot force it, only avoid holding references you no longer need.
11. What is the difference between pure and impure functions? Why does it matter?
A pure function returns the same output for the same input and has no side effects (no mutation of outside state, no I/O). Pure functions are easier to test, cache, and reason about, and they are the basis of predictable state management in frameworks such as React and Redux.
12. What is the difference between class-based and prototype-based inheritance, and what is Object.create?
Object.create(proto) makes a new object whose prototype is proto, which is the direct form of prototypal inheritance. The class syntax produces the same prototype chain with clearer syntax, and adds features such as private fields. Prefer composition over deep inheritance in either style.
13. What is the output of this code and why?
const obj = { name: 'Ada', regular: function () { return this.name; }, arrow: () => typeof this, }; console.log(obj.regular()); const fn = obj.regular; console.log(fn());The first call prints Ada because this is obj. The second call loses the object: this is undefined in strict mode (or the global object in sloppy mode), so it throws or prints undefined. The arrow function never has its own this, which is why it is not used for methods like this.
14. What is a WeakRef and when would you use it?
A WeakRef holds a reference to an object without keeping it alive. Calling deref() returns the object or undefined if it was collected. It is for niche cases such as caches of large, recreatable objects. Because collection timing is unpredictable, do not rely on it for program logic.
15. How would you design a simple event emitter?
Keep a map from event names to lists of listeners, with on, off, and emit methods.
class Emitter { constructor() { this.events = new Map(); } on(name, listener) { if (!this.events.has(name)) this.events.set(name, []); this.events.get(name).push(listener); return () => this.off(name, listener); } off(name, listener) { const list = this.events.get(name) || []; this.events.set(name, list.filter(function (l) { return l !== listener; })); } emit(name, ...args) { (this.events.get(name) || []).forEach(function (l) { l(...args); }); } }
Interview Tips
- Narrate your thinking while you code, and test your function with two or three inputs at the end.
- Mention edge cases: empty input, null, very large input, and duplicate values.
- If you do not remember an API, say what it should do and write the logic by hand.