01 — Introduction & Setup
Q1. What is V8, in the context of JavaScript?
- A JavaScript runtime that provides
fs,http, and other OS-level APIs - A JavaScript engine that parses and executes JavaScript source code
- A package manager bundled with Node.js
- A transpiler that converts modern JavaScript into ES5
Show Answer
Answer: B — A JavaScript engine that parses and executes JavaScript source code
Explanation: V8 (built by Google) is an engine — it parses, compiles (via JIT), and executes JS code, and implements the ECMAScript spec plus the language's core objects (Array, Promise, etc.). It does not provide fs or http; those come from a runtime built around the engine. Option A describes Node.js, which embeds V8 but adds its own APIs. Option C confuses V8 with npm. Option D describes tools like Babel — V8 runs JS natively, it doesn't transpile.
Q2. Which statement correctly distinguishes Node.js from V8?
- They are the same thing — "Node.js" is just another name for the V8 engine
- Node.js is a runtime that embeds V8 and adds APIs like
fs,http, andprocessthat aren't part of JavaScript itself - V8 is a superset of Node.js that adds browser-specific APIs
- Node.js replaces V8 with its own custom-built engine
Show Answer
Answer: B — Node.js is a runtime that embeds V8 and adds APIs like fs, http, and process
Explanation: Node.js embeds the V8 engine and layers on host APIs (filesystem, networking, process, Buffer) that are not defined by the ECMAScript spec at all. Portability: because fs and window are host APIs, not language features, code using them isn't portable between Node and the browser even though both run "JavaScript." Option A collapses the runtime/engine distinction. Option C reverses the relationship. Option D is false — Node has always used V8, not a custom engine.
Q3. "ES6" and "ES2015" refer to:
- Two different, incompatible versions of JavaScript
- The same ECMAScript edition — ES6 is the older name, ES2015 is the year-based name adopted afterward
- ES6 is the browser implementation, ES2015 is the Node.js implementation
- ES2015 is a strict superset of ES6 released a year later
Show Answer
Answer: B — The same edition; ES6 is the older name, ES2015 is the year-based name
Explanation: TC39 switched to annual, year-based release naming starting with the edition that introduced let, const, classes, arrow functions, and Promises. That edition is interchangeably called "ES6" or "ES2015" — they are not different versions. Options A, C, and D all invent a distinction that doesn't exist.
Q4. Where must a "use strict" directive appear to enable strict mode for an entire script?
function example() {
"use strict";
x = 10;
}
- Anywhere in the file, even after other executable statements
- As the very first statement in the file (or function body), before any other statements
- Inside a comment at the top of the file
- It must be passed as a CLI flag to Node; it cannot appear in source
Show Answer
Answer: B — As the very first statement in the file (or function body), before any other statements
Explanation: "use strict" is a directive prologue — the parser only recognizes it as special when it is literally the first statement. Debug: if any other statement (even a variable declaration) precedes it, it's silently treated as a harmless, no-op string literal expression instead of enabling strict mode — a common silent-failure trap. Option A is the tempting wrong answer for exactly that reason. It's a plain string literal, not a comment (C), and works in any JS file, not just via a Node flag (D).
Q5. By default, how does a browser handle a plain <script src="app.js"></script> tag with no async, defer, or type="module" attributes?
- It downloads and executes the script without blocking HTML parsing
- It blocks HTML parsing while the script downloads and executes, then resumes parsing
- It waits until the entire document is parsed before downloading the script
- It downloads the script in parallel but always executes it after
DOMContentLoaded
Show Answer
Answer: B — It blocks HTML parsing while the script downloads and executes, then resumes parsing
Explanation: A classic synchronous <script> tag is parser-blocking: the browser must fetch and run it before continuing to parse the rest of the HTML. Performance: this is why render-blocking scripts placed in <head> are a common cause of slow page loads, and why defer/async/placing scripts before </body> are recommended. Option A describes async. Option C is closer to defer's execution timing but still wrong about parsing being blocked. Option D is incorrect — plain scripts run as soon as they're evaluated, not after DOMContentLoaded.
Q6. A <script type="module"> tag behaves like which combination of attributes by default?
- Like a plain script — blocking, synchronous
- Like
async— non-blocking, execution order not guaranteed - Like
defer— non-blocking, and execution is deferred until after parsing, in document order - Modules cannot be loaded via
<script>tags at all; they require a bundler
Show Answer
Answer: C — Like defer — non-blocking, deferred, and in document order
Explanation: ES module scripts are deferred by default: the browser fetches them without blocking parsing, and executes them in document order after parsing completes (unless async is also explicitly added, which then allows out-of-order execution). Option A is the common beginner assumption — that "module" means "just a normal script with import support" — but the loading semantics are different. Option B is wrong unless async is explicitly added. Option D is false; native <script type="module"> works without any bundler in modern browsers.
Q7. What is a defining characteristic of Deno compared to Node.js?
- Deno cannot execute TypeScript without a separate compile step
- Deno runs scripts with no filesystem, network, or environment access by default, requiring explicit permission flags
- Deno does not support ES modules
- Deno uses the SpiderMonkey engine instead of V8
Show Answer
Answer: B — Deno runs scripts with no filesystem, network, or environment access by default, requiring explicit permission flags
Explanation: Safety: Deno is secure-by-default — a script needs --allow-read, --allow-net, --allow-env, etc. explicitly granted, unlike Node where any script has full system access the moment it runs. Deno actually supports TypeScript natively without a separate build step (making A false), supports ES modules as its primary module system (making C false), and uses V8 just like Node (making D false).
Q8. What is the key syntactic/behavioral difference between CommonJS and ES modules in Node.js?
const fs = require('fs');
module.exports = { readConfig };
- CommonJS uses
require/module.exportsand loads synchronously; ESM usesimport/exportand is loaded asynchronously with static analysis - There is no real difference — Node treats both identically at runtime
- CommonJS supports
importstatements but ESM does not - ESM is only available in the browser, never in Node.js
Show Answer
Answer: A — CommonJS uses require/module.exports synchronously; ESM uses import/export, resolved asynchronously and statically analyzable
Explanation: CommonJS require() calls are synchronous function calls that can happen conditionally anywhere in code. ESM import/export bindings are statically analyzed at parse time (enabling tree-shaking) and top-level import cannot be conditional. Node determines which system to use per-file based on .mjs/.cjs extensions or the "type" field in package.json. Option B ignores real interop pitfalls (e.g., CJS's require is unavailable by default in ESM files). Option C is backwards. Option D is false — Node has supported ESM natively since Node 12+.
Q9. What happens when you assign to an undeclared variable inside strict-mode code?
"use strict";
function setTotal() {
total = 42;
}
setTotal();
-
totalis silently created as a global variable, same as in non-strict mode - A
ReferenceErroris thrown becausetotalwas never declared - A
TypeErroris thrown becausetotalisundefined - It works fine, but
totalis scoped only tosetTotal
Show Answer
Answer: B — A ReferenceError is thrown because total was never declared
Explanation: In non-strict mode, assigning to an undeclared identifier silently creates a global variable — a notorious source of bugs. Strict mode closes this hole: it throws a ReferenceError instead. Safety: the correct fix is to declare the variable explicitly (let total = 42;) rather than rely on implicit globals. Option A describes the (bug-prone) non-strict behavior. Option C names the wrong error type. Option D is wrong because the assignment never succeeds at all.
Q10. Do ES modules (<script type="module"> or .mjs files) require an explicit "use strict" directive to run in strict mode?
- Yes — without it, modules run in sloppy (non-strict) mode
- No — ES modules are automatically strict mode, with no directive needed
- Only if the module also uses classes
- Only in Node.js, not in browsers
Show Answer
Answer: B — No, ES modules are automatically strict mode
Explanation: The ECMAScript spec mandates that all module code is implicitly strict — there's no opt-out and no need for the directive. Idiom: adding "use strict" at the top of a .mjs file is harmless but redundant. Option A is the common false assumption carried over from script-tag habits. Option C invents a class-specific rule (classes are always strict-mode bodies regardless of module status, but that's a separate rule). Option D is false — this is a language-level guarantee, not runtime-specific.
Q11. What does top-level this refer to in each of: a classic (non-module) browser script, an ES module, and a Node.js CommonJS file?
-
window,window, andmodule.exportsrespectively -
undefinedin all three -
window(global object),undefined, andmodule.exportsrespectively -
globalThisin all three, since ES2020 unified them
Show Answer
Answer: C — window, undefined, and module.exports respectively
Explanation: In a classic script, top-level this is the global object (window in browsers). In an ES module, top-level this is undefined by spec — modules don't have an implicit global receiver. In a Node CommonJS file, top-level this refers to module.exports because the file is wrapped in a function by Node's module loader. Debug: relying on top-level this for global access is fragile precisely because it varies by context — use globalThis instead when you genuinely need the global object. Option D wrongly claims globalThis replaced these semantics; globalThis is a new, separate way to reliably reach the global object, it didn't change what this means.
Q12. Given two scripts loaded with async, in what order do they execute relative to each other?
<script src="a.js" async></script>
<script src="b.js" async></script>
- Always in document order:
a.jsthenb.js - In whichever order each script finishes downloading first — order is not guaranteed
- Always in reverse document order
- Simultaneously, on separate threads
Show Answer
Answer: B — In whichever order each script finishes downloading first; order is not guaranteed
Explanation: async scripts download in parallel and each executes immediately as soon as it finishes downloading, independent of the other or of document order. Portability: this means a smaller/faster-to-fetch b.js can easily execute before a.js, so async is unsafe for scripts with ordering dependencies (use defer for that instead). Option A describes defer's guarantee, not async's. Option C and D describe behaviors JavaScript's single-threaded execution model doesn't produce — script execution itself is never literally parallel/simultaneous even though downloads are.
Q13. What happens when a strict-mode function is declared with duplicate parameter names?
"use strict";
function add(a, a, b) {
return a + b;
}
- It runs fine; the last
aargument silently shadows the first - It throws a
SyntaxErrorat parse time - It throws a
TypeErroronly when the function is called - It works the same as in non-strict mode, using the first
a
Show Answer
Answer: B — It throws a SyntaxError at parse time
Explanation: Non-strict mode silently allows duplicate parameter names (later ones shadow earlier ones) — a foot-gun strict mode explicitly forbids by making it a parse-time SyntaxError, so the code never even runs. Safety: the fix is simply to rename parameters uniquely. Option A and D describe the permissive non-strict behavior. Option C is wrong about timing — this is caught before execution, not at call time.
Q14. How does eval() behave differently in strict mode versus non-strict mode regarding variable declarations?
"use strict";
eval("var leaked = 1;");
console.log(typeof leaked);
- In strict mode,
evalstill leaksleakedinto the surrounding scope, logging"number" - In strict mode,
evalgets its own variable scope, soleakednever escapes; this logs"undefined" -
evalis entirely disabled in strict mode and throws aSyntaxError - Both strict and non-strict
evalbehave identically — there's no scoping difference
Show Answer
Answer: B — In strict mode, eval gets its own scope, so leaked never escapes; this logs "undefined"
Explanation: In non-strict mode, var declarations inside eval() leak into the calling scope — a well-known danger of eval. Strict mode contains this: code run via eval gets its own variable environment, so declarations inside it stay local. Safety: this is one of several reasons strict mode is recommended even when not using classes/modules. Option A describes the leaky non-strict behavior. Option C overstates it — eval still works in strict mode, it's just scoped. Option D ignores this real, spec-mandated difference.
Q15. Is it necessary to add "use strict" at the top of a file that only contains ES class declarations and no other top-level code?
- Yes, classes are not strict by default and need the directive
- No — class bodies are always executed in strict mode regardless of any directive, though it's still good practice to add it for non-class code in the same file
- No, and adding it would cause a
SyntaxErrorinside a class - It depends on whether the class uses
extends
Show Answer
Answer: B — No, class bodies are always strict regardless of a directive; still good practice for non-class code in the same file
Explanation: The ECMAScript spec mandates that the body of every class (constructor and methods) runs in strict mode automatically, independent of directives or module status. Idiom: if a file mixes classes with regular top-level function code, you may still want an explicit "use strict" (or use a module) so that non-class code in the same file also gets strict-mode protections. Option A misunderstands the spec rule. Option C is false — the directive is legal syntax anywhere a directive prologue is allowed. Option D invents an irrelevant condition.
Q16. What is the idiomatic reason modern projects prefer <script type="module"> over the older <script nomodule> fallback pattern?
<script type="module" src="modern.js"></script>
<script nomodule src="legacy.js"></script>
-
nomodulescripts always execute first regardless of load order - The fallback pattern exists to serve modern ESM bundles to browsers that support modules and a transpiled/bundled fallback to those that don't; with legacy browser support largely dropped, many modern projects skip the fallback and ship
type="module"only -
type="module"scripts cannot import other modules, sonomoduleis required as a workaround -
nomoduleis deprecated syntax that throws an error in all current browsers
Show Answer
Answer: B — The pattern serves modern ESM to capable browsers and a fallback bundle to legacy ones; many projects now skip the fallback entirely
Explanation: The dual-script pattern exploited the fact that browsers old enough to not understand type="module" also don't recognize nomodule as special and thus ignore it as an unknown attribute (loading the legacy bundle), while modern browsers understand nomodule and skip that script. Idiom: since evergreen browsers dominate today, many teams now ship ESM-only bundles and drop the legacy fallback and its build complexity entirely. Option A misdescribes the loading logic. Option C is false — modules import other modules constantly via import. Option D is false; nomodule isn't deprecated, it's just often unnecessary now.
Q17. Why is defer generally preferred over placing multiple plain <script> tags right before </body>?
-
deferscripts execute before the DOM is parsed, which is faster -
deferlets the browser download scripts in parallel with HTML parsing (non-blocking) while still guaranteeing document order execution after parsing — plain end-of-body scripts still block parsing when the parser reaches them - There is no real difference; both approaches are functionally identical
-
deferscripts run once per animation frame instead of once on load
Show Answer
Answer: B — defer downloads in parallel with parsing, non-blocking, and still preserves document order after parsing completes
Explanation: Performance: even scripts placed at the bottom of <body> still block parsing for the moment the parser reaches them (they just do so after most content is already visible); defer avoids blocking entirely by downloading concurrently with parsing and only executing once parsing is fully done, in document order. Option A gets the timing backwards — deferred scripts run after parsing, not before. Option C ignores the real performance and blocking difference. Option D describes something scripts don't do at all.
Q18. Setting "type": "module" in a Node.js package.json changes what?
{
"name": "app",
"type": "module"
}
- It only affects TypeScript files, not
.jsfiles - It makes Node interpret
.jsfiles in that package as ES modules (import/export) instead of the CommonJS default, while.cjsfiles remain CommonJS regardless - It disables
require()globally, even in.cjsfiles - It has no effect unless a bundler like webpack is also configured
Show Answer
Answer: B — It makes .js files in that package parse as ES modules; .cjs files stay CommonJS regardless
Explanation: Node picks a module system per file: without "type": "module", .js defaults to CommonJS; with it set, .js defaults to ESM. The explicit extensions .mjs (always ESM) and .cjs (always CommonJS) override the package.json setting entirely — this is the escape hatch for mixed codebases. Option A is wrong; this setting is about .js resolution, unrelated to TypeScript compilation. Option C is too broad — .cjs files are unaffected. Option D is false; this is native Node behavior with zero bundler involvement.
Q19. What is the purpose of the "engines" field in package.json?
{
"name": "app",
"engines": {
"node": ">=18.0.0"
}
}
- It automatically installs the specified Node.js version when
npm installruns - It declares the Node.js (and optionally npm) version range the package expects; by default
npm installonly warns (doesn't block) if the current version doesn't match, unlessengine-strictis enabled - It sets the JavaScript engine (V8, SpiderMonkey, etc.) the app requires
- It is required for ES modules to work
Show Answer
Answer: B — It declares the expected Node/npm version range; npm warns by default rather than blocking, unless engine-strict is set
Explanation: Idiom: teams add engines to document compatibility and catch version mismatches early, but by default npm treats a mismatch as a warning, not a hard failure — many developers are surprised the install still succeeds. Setting engine-strict=true in .npmrc (or using tools like volta/nvm with .nvmrc) is needed to actually enforce it. Option A confuses it with a version manager's job — npm doesn't install Node versions. Option C confuses "engine" as used in package.json (meaning runtime version) with a JS engine like V8. Option D is unrelated; ESM support depends on the "type" field and Node version, not engines.
Q20. What is the recommended way to check whether a JavaScript runtime supports a given feature (e.g., optional chaining) before using it in production code?
- Parse the
navigator.userAgentstring and branch based on browser name/version - Use feature detection — check for the existence of the API/behavior directly (or rely on a transpiler/polyfill pipeline), rather than inferring support from the browser or engine identity
- Assume all evergreen browsers support all features equally, so no check is needed
- Wrap all code in
try/catchand silently ignore failures
Show Answer
Answer: B — Use feature detection rather than inferring support from browser/engine identity
Explanation: Idiom: UA sniffing (option A) is notoriously unreliable — user agents can be spoofed, forks and embedded webviews report misleading strings, and new browser versions ship features unpredictably. Feature detection (checking typeof obj?.prop !== 'undefined', 'querySelector' in document, etc.) or using build tooling (Babel/TypeScript targets, Browserslist-driven polyfills) checks reality directly. Option C is risky since even "evergreen" runtimes lag on brand-new proposals. Option D silently swallowing errors trades a clear compatibility failure for a much harder-to-debug silent one, which is the opposite of good practice.