110 Questions · Click any card to reveal the answer · Track your progress below
var, let, and const with examples.
Easy
▼
undefined, can be reassigned and redeclared, creates global properties.
let is block-scoped, hoisted but in TDZ (Temporal Dead Zone), can be reassigned but not redeclared.
const is block-scoped, hoisted but in TDZ, cannot be reassigned or redeclared (but objects/arrays it references CAN be mutated).
var is function-scoped (leaks out of blocks), let and const are block-scoped. Always use const by default.var, let, and const?
Medium
▼
undefined.
let and const are hoisted but NOT initialized — they sit in a Temporal Dead Zone (TDZ) from the start of the block until the declaration line. Accessing them before declaration throws ReferenceError.
Function declarations are fully hoisted (name + body). Function expressions are NOT.
== and ===?
Easy
▼
5 == "5" → true (string coerced to number)
5 === "5" → false (different types)
null == undefined → true
null === undefined → false
Always use === to avoid subtle bugs.null and undefined?
Easy
▼
typeof undefined === "undefined" but typeof null === "object" (a well-known JS bug).this in: a regular function, an arrow function, and a method.
Medium
▼
this depends on HOW it's called (dynamic binding). Called globally → global object (undefined in strict mode).
Method: this = the object before the dot (the caller).
Arrow function: this = the enclosing scope where it was DEFINED (lexical/static binding) — cannot be changed with call/apply/bind.
this; they inherit from enclosing scope. Regular functions get their own this based on how they're called.
2. arguments object: Arrow functions don't have an arguments object — use rest parameters (...args) instead. Regular functions have arguments.
3. Constructor: Arrow functions CANNOT be used with new. They have no prototype and throw errors as constructors.
4. Syntax: Arrow functions have shorter syntax with implicit return.
5. call/apply/bind: These have no effect on this in arrow functions.addEventListener's third parameter controls which phase: false (default) = bubbling, true = capturing.
event.stopPropagation() stops the event from continuing.call, apply, and bind? Give one example each.
Medium
▼
this refers to.
call: invokes immediately, arguments passed individually.
apply: invokes immediately, arguments passed as an array.
bind: does NOT invoke immediately, returns a NEW function with this permanently bound.
map (transforms each element), filter (keeps elements that pass a test), reduce (accumulates into a single value). Also: forEach, find, sort, every, some.
console.log(a); var a = 10; console.log(a);
Easy
▼
undefined, then 10.
var is hoisted and initialized to undefined. First log sees undefined, then a = 10 runs, second log prints 10.console.log(b); let b = 20;
Easy
▼
ReferenceError: Cannot access 'b' before initialization.
let is in TDZ (Temporal Dead Zone) until the declaration line.function outer() { let count = 0; return function inner() { count++; console.log(count); } } const fn = outer(); fn(); fn(); fn();
Medium
▼
1, 2, 3.
Classic closure — inner function captures count from outer's scope. count persists between calls.obj.getName() and obj.getNameArrow() with name: "TCS"
Medium
▼
TCS, then undefined.
Regular function: this = obj → prints "TCS".
Arrow function: this = enclosing scope (global), not obj → undefined.setTimeout(() => console.log("A"), 0); Promise.resolve().then(() => console.log("B")); console.log("C");
Hard
▼
C, B, A.
Order: Sync code first → microtasks (Promise) → macrotasks (setTimeout).for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }
Medium
▼
3, 3, 3.
var is function-scoped — one shared i. By the time callbacks run, the loop is done and i = 3.for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }
Medium
▼
0, 1, 2.
let is block-scoped — each iteration creates a new i binding. Closures capture specific values.const arr = [1,2,3]; const arr2 = arr; arr2.push(4); console.log(arr);
Easy
▼
[1, 2, 3, 4].
Arrays are reference types — arr2 and arr point to the same array in memory. Pushing to arr2 modifies the shared array.const x = {a:1}; const y = {a:1}; console.log(x === y);
Easy
▼
false.
Objects are compared by reference, not value. Two distinct objects in memory → different references.const data = [1,2,3,4,5]; const result = data.filter(n => n%2===0).map(n => n*10); console.log(result);
Medium
▼
[20, 40].
filter keeps evens → [2, 4], then map multiplies by 10 → [20, 40]..then()/.catch() for handling, supports chaining, avoids callback hell.
Async/await: syntactic sugar over Promises, async makes function return Promise, await pauses until Promise settles, makes async code look synchronous, easier error handling with try/catch..then() chaining.
Async/await makes it even cleaner with synchronous-looking code and try/catch error handling.Promise.then/catch/finally, queueMicrotask(), MutationObserver, process.nextTick (Node).
Macrotasks (lower priority): setTimeout, setInterval, setImmediate, I/O callbacks.
Execution order: after each macrotask, event loop drains ALL microtasks before next macrotask.
Rule: microtasks always run before macrotasks.process.nextTick — fastest, runs after current op, before other microtasks.
(2) Promise.then / other microtasks.
(3) setTimeout / setImmediate / macrotasks..catch() or try/catch.new Promise:
util.promisify (Node.js).
(3) Use built-in promise versions (e.g., fs.promises).async before a function? What does it return?
Easy
▼
async makes a function treatable as asynchronous and allows using await inside.
It always returns a Promise. If you return a non-Promise value, it's automatically wrapped in Promise.resolve(). If it throws, the promise rejects with that error.try/catch. Async functions that throw cause the returned promise to reject.
Without try/catch, leads to unhandled rejection.
Also can use .catch() per-await: await fn().catch(err => default).
Always handle errors.console.log("1"); setTimeout(() => console.log("2"), 0); Promise.resolve().then(() => console.log("3")); console.log("4");
Hard
▼
1, 4, 3, 2.
Sync first → microtask (Promise) → macrotask (setTimeout).X, A, Y, C.
"X" sync, "A" sync inside test until await, "Y" sync (test paused), "C" after await resolves.D, A, B, C.
Sync first → all microtasks (chained .thens) → macrotask.Done, Caught: Oops.
f1 returns a rejected promise. .catch() is async. "Done" runs first (sync), then catch.C, A, B Err.
"C" sync, "A" microtask, "B" catches the rejected promise from .then().require() syntax, synchronous loading, module.exports, no tree shaking.
ES Modules: import/export syntax, asynchronous compile-time loading, supports tree shaking, top-level await.
CommonJS uses .js default, ES modules use .mjs or "type":"module" in package.json.module.exports is the actual exported object. exports is a shorthand alias initially pointing to the same object.
Use module.exports for default export, exports.name for named exports.
Never reassign exports directly (breaks the link).join, resolve, extname).
os: system info (cpus, freemem, hostname).readFile is async (non-blocking, callback/promise).
readFileSync is sync (blocks the event loop).
Avoid sync in web servers/APIs — blocks ALL other requests.
Sync is fine for scripts, CLI tools, startup init.fs.createReadStream, stdin).
Writable (destination — fs.createWriteStream, stdout).
Duplex (both — net.Socket).
Transform (modifies data — zlib.createGzip).
Use .pipe() or .pipeline() for automatic backpressure handling.stream.pipe() (auto-handles), stream.pipeline() (safer), or manually: check write() return value (false when full), listen for 'drain' event.on()/addListener() — register, emit() — fire, once() — fires once then removed, removeListener().
Extended by streams, HTTP servers, process, and custom services.process.nextTick: runs immediately after current operation, before any other async (even microtasks).
setImmediate: runs in "check" phase of next event loop iteration, after I/O callbacks (macrotask).
Priority: sync → nextTick → Promise.then → setImmediate.
Use nextTick for ASAP operations; setImmediate for after I/O.name, version, scripts, dependencies, devDependencies, main, type, engines.npm install --production) skips devDependencies.node_modules.process.env global object. Use the dotenv package: require("dotenv").config() reads a .env file.
Access via process.env.VAR_NAME. Values are always strings.
Add .env to .gitignore.process.memoryUsage(), Chrome DevTools, --inspect flag.fs.readFileSync blocks ALL other requests.
Fix: use async fs.promises.readFile or streams.
res.json, res.status), centralized error handling, huge ecosystem.(req, res, next) signature. Can execute code, modify req/res, end the cycle, or call next().
A route handler is middleware that ends the response (no next()).
Global middleware runs for all routes; route-specific runs only for that route.next() to continue. If it sends a response, the pipeline stops.
Global app.use() runs for ALL routes. Error middleware (4 params) must be defined LAST.express.json(): parses JSON payloads (application/json).
express.urlencoded(): parses form data (application/x-www-form-urlencoded).
Without these, req.body is undefined. Most common beginner mistake!app.use(): middleware for ALL HTTP methods at a path.
app.get(): handler for GET requests only.
Also: app.post(), app.put(), app.delete().router.get(), router.post() — then mount with app.use("/users", router).
Benefits: separation of concerns, avoids massive app.js, routes can have own middleware.try/catch or an asyncHandler wrapper. Async throws won't auto-trigger Express error handler.
(err, req, res, next).cors package: app.use(cors()).
For specific origin: app.use(cors({ origin: "http://localhost:3000" })).jwt.sign(), verify with jwt.verify().Authorization header → extract token → verify with jwt.verify() → attach decoded user to req.user → call next().
For roles: create a roleCheck middleware checking req.user.role.res.send(): auto-detects content type, can send strings/buffers/objects.
res.json(): sets Content-Type: application/json, converts to JSON.
res.sendFile(): sends a file with appropriate headers.req.params.id (from /users/:id).
Query params: req.query.role (from ?role=admin).
Request body: req.body (POST, needs express.json() middleware).
Query params are always strings..env file variables into process.env. Keeps secrets out of code.
Add .env to .gitignore. Create .env.example for team reference.fetchData() throws, Express won't catch it.
Fix: wrap in try/catch or asyncHandler.
user_id, email (if unique). Auto-incrementing integers are common.WHERE, JOIN, ORDER BY).NULLs if no match).
db.collection.insertOne({ name: "Alice", age: 25 }) or insertMany([...]).
Documents get an auto-generated _id.