JavaScript
JavaScript Output-Based Questions (with answers)
Difficulty: 🟡 Medium · Est. time:
1.5h· Tags:#output-based#tricky#interview
The classic "guess the output" set. Cover the answer, predict the output, then reveal. Each one teaches a core concept — hoisting, closures, the event loop, this, coercion, and promises. Great warm-up before any JS interview.
Related: JavaScript section · Promise/debounce flagship · DSA for Frontend
🪜 Hoisting & scope
Q1
console.log(a);
var a = 5;
Output: undefined — var a is hoisted (declaration only); the assignment stays in place.
Q2
console.log(b);
let b = 5;
Output: ReferenceError: Cannot access 'b' before initialization — let/const are in the Temporal Dead Zone until declared.
Q3
let x = 1;
(function () {
console.log(x);
let x = 2;
})();
Output: ReferenceError — inside the IIFE, x is block-scoped and hoisted into the TDZ, so the outer x is shadowed and not yet initialized.
🔒 Closures & loops
Q4
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
Output: 3 3 3 — var is function-scoped; all three callbacks close over the same i, which is 3 when they run.
Q5
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
Output: 0 1 2 — let creates a new binding each iteration.
Q6
function counter() {
let count = 0;
return () => ++count;
}
const c = counter();
console.log(c(), c(), c());
Output: 1 2 3 — the returned closure keeps a private count alive.
⏳ Event loop (micro vs macrotasks)
Q7
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
Output: A D C B — synchronous first (A, D), then the microtask (C), then the macrotask (B).
Q8
async function f() {
console.log(1);
await null;
console.log(2);
}
console.log(0);
f();
console.log(3);
Output: 0 1 3 2 — code up to the first await runs synchronously; everything after await is queued as a microtask.
Q9
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => {
console.log(3);
setTimeout(() => console.log(4));
});
console.log(5);
Output: 1 5 3 2 4 — sync (1,5) → microtask (3) → macrotasks in order queued (2, then 4).
🎯 this
Q10
const obj = {
name: 'JS',
greet() { return this.name; },
};
const g = obj.greet;
console.log(g());
Output: undefined (strict mode) / '' (sloppy, this = window) — this is determined at call time; detaching the method loses the receiver.
Q11
const obj = {
name: 'JS',
greet() {
return (() => this.name)();
},
};
console.log(obj.greet());
Output: JS — the arrow function has no own this; it uses greet's this, which is obj.
🔀 Coercion & operators
Q12
console.log(1 + '2' + 3);
console.log('5' - 2);
Output: "123" and 3 — + with a string concatenates; - forces numeric coercion.
Q13
console.log([] + []);
console.log([] + {});
console.log(0.1 + 0.2 === 0.3);
Output: "", "[object Object]", false (it's 0.30000000000000004 — floating point).
Q14
console.log([1, 2, 3].map(parseInt));
Output: [1, NaN, NaN] — map passes (value, index), so it calls parseInt(1,0)=1, parseInt(2,1)=NaN, parseInt(3,2)=NaN.
Q15
console.log(typeof null, typeof NaN, typeof undefined, typeof function () {});
Output: "object" "number" "undefined" "function" — typeof null === "object" is a historic bug.
Q16
console.log(null == undefined, null === undefined, NaN === NaN);
Output: true false false — NaN is never equal to anything, including itself.
Q17
console.log([] == ![]);
Output: true — ![] is false; then [] == false → '' == 0 → 0 == 0 → true.
Q18
console.log(1 < 2 < 3);
console.log(3 > 2 > 1);
Output: true and false — evaluated left-to-right: (3 > 2) > 1 → true > 1 → 1 > 1 → false.
📦 References, arrays & objects
Q19
const a = { x: 1 };
const b = a;
b.x = 2;
console.log(a.x);
Output: 2 — objects are held by reference; a and b point to the same object.
Q20
console.log([3, 1, 10, 2].sort());
Output: [1, 10, 2, 3] — default sort compares elements as strings ("10" < "2"). Use sort((a,b)=>a-b).
Q21
const arr = [1, 2, 3];
arr.length = 0;
console.log(arr[0]);
Output: undefined — setting length = 0 truncates the array.
Q22
const { x, y = 10 } = { x: 1, y: undefined };
console.log(x, y);
Output: 1 10 — destructuring defaults apply when the value is undefined.
🤝 Promises
Q23
Promise.resolve(1)
.then(() => 2)
.then((v) => console.log(v));
Output: 2 — the first .then ignores 1 and returns 2, which flows down the chain.
Q24
Promise.reject('e')
.catch((e) => e)
.then((v) => console.log('then:', v));
Output: then: e — .catch handles the rejection and returns a value, so the chain recovers and continues to .then.
Q25
console.log('start');
Promise.resolve().then(() => console.log('promise'));
setTimeout(() => console.log('timeout'), 0);
console.log('end');
Output: start end promise timeout — sync → microtask → macrotask.
🧪 More brain-teasers
Q26
console.log(typeof typeof 1);
Output: "string" — typeof 1 is "number"; typeof "number" is "string".
Q27
const obj = {};
obj[[1, 2]] = 'a';
console.log(obj['1,2']);
Output: "a" — object keys are strings; the array key becomes "1,2".
Q28
console.log(0.1.toFixed(20));
Output: "0.10000000000000000555" — reveals the real stored floating-point value.
Q29
let a = { n: 1 };
let b = a;
a.x = a = { n: 2 };
console.log(a.x, b.x);
Output: undefined { n: 2 } — a.x is evaluated on the old object (bound before assignment) so b.x gets {n:2}; the new a never gets an x.
Q30
console.log([1, 2, 3, 4].reduce((acc, x) => acc + x));
Output: 10 — no initial value, so it starts from the first element.
🧠 Part 2 — more predict-the-output
Q31 — function declaration vs expression hoisting
foo();
bar();
function foo() { console.log('foo'); }
var bar = function () { console.log('bar'); };
Output: foo, then TypeError: bar is not a function — declarations hoist fully; var bar is hoisted as undefined.
Q32 — this in a setTimeout callback
const timer = {
seconds: 5,
start() { setTimeout(function () { console.log(this.seconds); }, 0); },
};
timer.start();
Output: undefined — the plain function's this isn't timer. Fix with an arrow callback.
Q33 — __proto__ vs prototype
function A() {}
const a = new A();
console.log(a.__proto__ === A.prototype, A.__proto__ === Function.prototype);
Output: true true.
Q34 — Object.freeze is shallow
const obj = Object.freeze({ a: 1, nested: { b: 2 } });
obj.a = 10;
obj.nested.b = 20;
console.log(obj.a, obj.nested.b);
Output: 1 20 — only the top level is frozen.
Q35 — spread is a shallow copy
const original = { a: 1, nested: { b: 2 } };
const copy = { ...original };
copy.nested.b = 99;
console.log(original.nested.b);
Output: 99 — nested objects are shared by reference.
Q36 — sparse arrays skip holes
const arr = [1, , 3];
arr.forEach((x) => console.log(x));
console.log(arr.length);
Output: 1, 3, then 3 — forEach skips the hole, but length counts it.
Q37 — default params are evaluated at call time
let count = 0;
function next(x = count++) { return x; }
console.log(next(), next(), count);
Output: 0 1 2.
Q38 — async/await interleaving
async function a1() { console.log(1); await a2(); console.log(2); }
async function a2() { console.log(3); }
console.log(4); a1(); console.log(5);
Output: 4 1 3 5 2 — sync runs first; the code after await resumes as a microtask.
Q39 — throwing inside an async function
async function run() { throw new Error('boom'); }
run().catch((e) => console.log('caught', e.message));
Output: caught boom — an async function turns a throw into a rejected promise.
Q40 — generators are lazy
function* g() { console.log('a'); yield 1; console.log('b'); yield 2; }
const it = g();
console.log('start');
it.next(); it.next();
Output: start, a, b — nothing runs until .next().
Q41 — finally overrides return
function test() { try { return 1; } finally { return 2; } }
console.log(test());
Output: 2.
Q42 — Proxy get trap
const t = { a: 1 };
const p = new Proxy(t, { get: (o, k) => (k in o ? o[k] : `no ${k}`) });
console.log(p.a, p.b);
Output: 1 no b.
Q43 — Symbols are always unique
console.log(Symbol('id') === Symbol('id'));
Output: false.
Q44 — NaN: includes vs indexOf
console.log([NaN].includes(NaN), [NaN].indexOf(NaN));
Output: true -1 — includes uses SameValueZero; indexOf uses === (and NaN === NaN is false).
Q45 — loose-equality traps
console.log(0 == '', 0 == '0', '' == '0');
Output: true true false.
🎓 What these test
| Concept | Questions |
|---|---|
| Hoisting / TDZ | Q1–Q3 |
| Closures | Q4–Q6 |
| Event loop | Q7–Q9, Q25 |
this binding |
Q10–Q11 |
| Type coercion | Q12–Q18 |
| References | Q19, Q29 |
| Promises | Q23–Q25 |