JavaScript

JavaScript Output-Based Questions (with answers)

← Back to JavaScript

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: undefinedvar a is hoisted (declaration only); the assignment stays in place.

Q2

console.log(b);
let b = 5;

Output: ReferenceError: Cannot access 'b' before initializationlet/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 3var 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 2let 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 falseNaN is never equal to anything, including itself.

Q17

console.log([] == ![]);

Output: true![] is false; then [] == false'' == 00 == 0true.

Q18

console.log(1 < 2 < 3);
console.log(3 > 2 > 1);

Output: true and false — evaluated left-to-right: (3 > 2) > 1true > 11 > 1false.


📦 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 3forEach 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 -1includes 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

Next: Implement-this challenges · DSA for Frontend

ESC

Type to search across every question bank.

navigate open 3,610 questions indexed