Ace your JavaScript interview with 50+ essential questions covering closures, async programming, and modern ES6+ features.
10 Questions
~30 min read
A closure is a function that has access to variables from its outer (enclosing) scope, even after the outer function has returned. Closures are useful for: data privacy (creating private variables), function factories, maintaining state in async operations, and implementing modules. They're fundamental to JavaScript's functional programming patterns.
"this" refers to the execution context and varies by how a function is called: (1) Global context: window/global, (2) Object method: the object, (3) Constructor: new instance, (4) Arrow functions: lexically inherited from parent, (5) call/apply/bind: explicitly set. Arrow functions are commonly used to preserve "this" in callbacks.
var is function-scoped and hoisted (initialized as undefined). let and const are block-scoped and hoisted but not initialized (temporal dead zone). const requires initialization and can't be reassigned (but objects/arrays can be mutated). Best practice: use const by default, let when reassignment is needed, avoid var.
The event loop is JavaScript's concurrency model. It continuously checks: (1) Execute synchronous code in the call stack, (2) When stack is empty, check the microtask queue (Promises), (3) Then check the macrotask queue (setTimeout, DOM events). This allows non-blocking I/O despite JavaScript being single-threaded.
Promises represent eventual completion or failure of async operations. They have three states: pending, fulfilled, rejected. async/await is syntactic sugar over Promises - async functions return Promises, and await pauses execution until the Promise resolves. async/await makes async code look synchronous and easier to read, while still being non-blocking.
JavaScript uses prototypal inheritance where objects can inherit directly from other objects. Every object has a [[Prototype]] (accessible via __proto__ or Object.getPrototypeOf). When accessing a property, JavaScript looks up the prototype chain until it finds the property or reaches null. This differs from classical inheritance in languages like Java.
== (loose equality) performs type coercion before comparison, which can lead to unexpected results (e.g., "1" == 1 is true). === (strict equality) compares both value and type without coercion ("1" === 1 is false). Best practice: always use === to avoid subtle bugs from type coercion.
Higher-order functions either take functions as arguments or return functions. Examples: map, filter, reduce (take callbacks), function factories (return functions), decorators. They enable functional programming patterns, code reuse, and abstraction. Example: array.map(x => x * 2) - map is a higher-order function taking a callback.
WeakMap and WeakSet hold "weak" references to objects, allowing garbage collection when no other references exist. WeakMap keys must be objects; WeakSet values must be objects. Use cases: storing private data associated with objects, caching, tracking DOM nodes without preventing their cleanup. They're not iterable and have no size property.
Debouncing delays function execution until a pause in events (e.g., wait 300ms after user stops typing). Throttling ensures a function runs at most once per time interval. Use debouncing for search inputs, form validation. Use throttling for scroll/resize handlers, rate-limiting API calls. Both optimize performance by reducing function calls.