JavaScript is the most widely used programming language in the world (Stack Overflow Developer Survey 2024, 62.3% of professional developers). That ubiquity means "I know JavaScript" is the least useful signal in a developer's profile — everyone says it. The interview questions below test the mechanisms that separate developers who understand the language from those who have memorised framework patterns.
What "Knowing JavaScript" Actually Requires
Most developers who describe themselves as JavaScript developers have built React or Vue applications, written Node.js backends, or worked on frontend integrations. Very few have studied the language itself at the mechanism level — the event loop, the prototype chain, how closures capture scope, what this refers to in different contexts. That language-level knowledge is what predicts the ability to debug production issues, not write features in development.
The Event Loop — Not Just "Async Works"
JavaScript in browsers and Node.js is single-threaded: one call stack, one sequence of execution. Asynchronous operations — setTimeout, fetch, fs.readFile — do not run in parallel with JavaScript code. They are registered, their callbacks queued, and the event loop executes them when the call stack is empty.
The specific mechanism: the event loop continuously checks whether the call stack is empty. When it is, it picks the next task from the task queue (macrotask queue). Microtasks (Promise.then(), queueMicrotask()) are handled separately — the microtask queue is drained completely after every task, before the next macrotask runs. This is why Promise.then() callbacks run before setTimeout(fn, 0) callbacks even when both are scheduled: a resolved promise queues a microtask, which runs before the event loop processes the next macrotask.
A developer who cannot explain why the following logs in this specific order has not understood the event loop:
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Output: 1, 4, 3, 2
The explanation: 1 and 4 are synchronous. The setTimeout callback is a macrotask, queued for the next event loop tick. The Promise.resolve().then() callback is a microtask, queued immediately after the current synchronous code completes. Microtasks drain before the next macrotask, so 3 runs before 2.
Closures — The Most Misunderstood Concept
A closure is a function that captures its surrounding lexical scope — the variables that existed in scope where the function was defined, not where it is called. This persists even after the outer function has returned. The captured variables are not copied — they are references to the same binding.
The classic closure bug:
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => console.log(i));
}
fns[0](); // 3, not 0
fns[1](); // 3, not 1
fns[2](); // 3, not 2
All three functions capture the same i binding. By the time they're called, the loop has completed and i is 3. The fix — let instead of var — creates a new binding per iteration because let is block-scoped; each loop iteration creates a new scope with a new i. A developer who cannot explain this bug and its fix has not maintained JavaScript code through async operations where closure bugs surface.
Prototype Chain — How Inheritance Actually Works
JavaScript inheritance is prototype-based, not class-based. ES6 class syntax is syntactic sugar over prototype chains. When you access a property on an object, JavaScript looks for it on the object itself, then on its prototype (Object.getPrototypeOf(obj)), then on that prototype's prototype, traversing the chain until it finds the property or reaches null.
function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { return `${this.name} makes a noise.`; };
const dog = new Animal("Dog");
dog.speak(); // Found on Animal.prototype, not on dog itself
This matters practically: modifying Array.prototype, the prototype pollution vulnerability pattern, understanding why instanceof checks the prototype chain, and why Object.create(null) produces objects with no inherited properties. A developer who treats class as a real Java-style class has a mental model that diverges from how the runtime actually works.
Five Interview Questions That Filter Effectively
"Explain why this has different values in different contexts, and how arrow functions change that."
Expected: this in a regular function is determined at call time — it's the object that called the function, or undefined in strict mode if called without an object. Arrow functions capture this from the surrounding lexical scope at definition time — they don't have their own this. This distinction matters in event listeners, class methods, and callbacks where this is lost. A developer who cannot give three examples of this behaving unexpectedly has not debugged a real JavaScript application.
"What is the output of the closure example above, and how do you fix it?"
Described above. The answer must include an explanation of var vs let scoping, not just "use let."
"When would you use Promise.all vs Promise.allSettled?"
Promise.all rejects when any promise rejects; use it when all results are required and any failure should abort. Promise.allSettled waits for all promises to resolve or reject; use it when you need all results regardless of individual failures. A developer who doesn't know Promise.allSettled exists has not built batch operations with partial failure handling.
"How does the JavaScript module system work — CommonJS vs ES Modules?"
CommonJS (require, module.exports) is synchronous and the module object is cached after first load — require('./module') returns the same cached export on subsequent calls. ES Modules (import, export) are statically analysed at parse time (not runtime), asynchronously loaded, and live bindings — when an exported value changes, the imported binding reflects the change. The practical difference: ES Modules support tree-shaking (bundlers can eliminate unused exports at build time because the dependency graph is known statically); CommonJS cannot be tree-shaken reliably. A developer who does not know this distinction has not worked on performance-conscious production codebases.
"Walk me through what happens when a JavaScript exception is not caught."
In a browser: the error is reported to window.onerror (or caught by window.addEventListener('error', fn)), the script continues executing from the next task. In Node.js: the process emits an uncaughtException event; if no handler is registered, the process exits with a non-zero code. In an async context: an unhandled promise rejection emits unhandledRejection in Node.js; in browsers, it triggers unhandledrejection on window. In production, these events should be captured by an error monitoring service (Sentry, Rollbar). A developer who doesn't know these hooks has not set up production error monitoring.
What TypeScript Proficiency Signals
Require TypeScript. The reasoning: TypeScript's type system catches a specific class of bugs before code review. More importantly, TypeScript proficiency signals that a developer thinks about the contracts between code units — what a function accepts and what it returns — not just the implementation of the function itself. See JavaScript vs TypeScript: why I now require TypeScript for the full reasoning.
Rates for JavaScript Developers in 2026
| Region | Mid (3–5 yrs) | Senior (6+ yrs) |
|---|---|---|
| India | $18–32/hr | $28–48/hr |
| Eastern Europe | $32–55/hr | $50–75/hr |
| Latin America | $22–40/hr | $38–60/hr |
| UK / Western Europe | $60–95/hr | $80–125/hr |
| USA / Canada | $75–115/hr | $95–140/hr |
JavaScript developer rates are roughly comparable to React/Node.js rates at the same seniority level — the framework is often the differentiator within the JavaScript developer pool. A vanilla JavaScript developer without a dominant framework skill sits at the lower end of these ranges; a TypeScript + React or TypeScript + Node.js specialist sits at the upper end.
Frequently Asked Questions
Should I hire a JavaScript developer or a TypeScript developer? In 2026, these should be the same person. TypeScript has become the production standard for JavaScript — any JavaScript developer who cannot write TypeScript is limiting the quality of the codebase they will produce. If a candidate says they know JavaScript but not TypeScript, treat that as a flag to probe further.
What is the difference between a JavaScript developer and a web developer? "Web developer" is a broader term that includes HTML, CSS, and design skills alongside JavaScript. A JavaScript developer is typically a programmer who works with JS logic — frontend JavaScript (React, Vue), backend JavaScript (Node.js), or both. For a project that needs complex UI logic, you want a JavaScript developer. For a project that needs pages styled and built, you want a web developer.
Can a JavaScript developer also handle DevOps? Many can, particularly Node.js developers who have deployed their own applications. Ask specifically: do they know Docker, can they write a CI/CD pipeline, do they understand environment variable management? These are learnable skills but not automatic for every JavaScript developer.