Skip to content

resonantdoghouse/javascript-riddles

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 

Repository files navigation

🧩 JavaScript Bug Riddles

A collection of fun and tricky JavaScript bugs that test your understanding of how JavaScript really works.
Great for workshops, interviews, or self-study!


πŸ” 1. Loop Closure Madness

const buttons = [];

for (var i = 0; i < 3; i++) {
  buttons.push(function () {
    console.log(`Button ${i} clicked`);
  });
}

buttons[0](); // ??
buttons[1](); // ??
buttons[2](); // ??
πŸ’‘ Answer

They all log Button 3 clicked.

Because var is function-scoped, not block-scoped, all closures share the same i β€” which ends at 3 after the loop.


🧠 2. The Curious Case of this

const obj = {
  value: 42,
  getValue: () => {
    return this.value;
  },
};

console.log(obj.getValue()); // ??
πŸ’‘ Answer

Logs undefined.

Arrow functions don't bind their own this β€” they use the one from their outer lexical scope (likely the global object in this case).


πŸ“¦ 3. Object Keys That Disappear

const a = {};
const b = { key: "b" };
const c = { key: "c" };

a[b] = 123;
a[c] = 456;

console.log(a[b]); // ??
πŸ’‘ Answer

Logs 456.

Objects used as keys are converted to the same string: "[object Object]". So b and c overwrite the same key.


πŸ’€ 4. The Lazy Promise

const promise = new Promise((resolve, reject) => {
  console.log("Promise created");
  resolve("Done");
});

promise.then((res) => console.log(res));

console.log("After promise");
πŸ’‘ Answer

Logs:

Promise created
After promise
Done

Creating a Promise runs its executor function immediately (sync). .then() runs async (microtask).


🧡 5. Microtasks vs Macrotasks

console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise");
});

console.log("End");
πŸ’‘ Answer

Logs:

Start
End
Promise
Timeout

Microtasks (Promise.then) run before macrotasks (setTimeout), even with 0 delay.


πŸ”„ 6. Reverse Coercion Riddle

console.log([] == false); // true
console.log([] == ![]); // true
πŸ’‘ Answer
  • [] == false β†’ true due to coercion: [] β†’ '' β†’ false.
  • [] == ![] β†’ [] == false β†’ again true.

πŸ§™β€β™‚οΈ 7. The Case of the Invisible Method

function Wizard() {}
Wizard.prototype.castSpell = function () {
  return "πŸ’₯ Fireball!";
};

const merlin = new Wizard();
merlin.castSpell = () => "✨ Sparkles!";
delete merlin.castSpell;

console.log(merlin.castSpell()); // ??
πŸ’‘ Answer

Logs 'πŸ’₯ Fireball!'.

Deleting the instance method reveals the prototype method.


🧭 8. Array Gaps

const arr = [1, 2, 3];
arr[10] = 11;

console.log(arr.length); // ??
console.log(arr.map((x) => x * 2)); // ??
πŸ’‘ Answer
  • arr.length is 11 (last index + 1).
  • map skips empty slots, so [2, 4, 6, <7 empty items>, 22].

πŸ§ͺ 9. Falsy Funhouse

const weird = [0, null, undefined, false, "", NaN];

for (let value of weird) {
  if (value) {
    console.log(`${value} is truthy`);
  } else {
    console.log(`${value} is falsy`);
  }
}
πŸ’‘ Answer

All log as falsy. These are the six falsy primitives in JS.


πŸ•³οΈ 10. Hoisting Hole

function test() {
  console.log(value);
  let value = 10;
}

test();
πŸ’‘ Answer

Throws a ReferenceError. let is hoisted but not initialized β€” it's in the Temporal Dead Zone.


🧩 11. typeof null

console.log(typeof null); // ??
πŸ’‘ Answer

Returns 'object'.

This is a long-standing bug in JavaScript β€” null is not actually an object, but typeof null returns 'object'.


🧩 12. NaN is not NaN?

console.log(NaN === NaN); // ??
πŸ’‘ Answer

Returns false.

NaN is the only value in JS that is not equal to itself.


🧩 13. Chained Comparisons Lie

console.log(3 > 2 > 1); // ??
πŸ’‘ Answer

Returns false.

3 > 2 β†’ true β†’ true > 1 β†’ 1 > 1 β†’ false.


🧩 14. Function Declaration vs Expression

hoisted();

function hoisted() {
  console.log("I am hoisted");
}

notHoisted();

var notHoisted = function () {
  console.log("I am not hoisted");
};
πŸ’‘ Answer
  • hoisted() runs fine.
  • notHoisted() throws TypeError: notHoisted is not a function.

Only function declarations are hoisted with their definitions. var is hoisted as undefined.


🧩 15. Unexpected Implicit Globals

function surprise() {
  oops = 42;
}
surprise();
console.log(oops); // ??
πŸ’‘ Answer

Logs 42.

Undeclared variables become implicit globals (in sloppy mode).


🧩 16. Destructuring Undefined

const { length } = undefined;
πŸ’‘ Answer

Throws TypeError: Cannot destructure property.

You can't destructure properties from undefined or null.


🧩 17. Array Holes Aren’t Undefined

const arr = [1, , 3];

console.log(arr[1]); // ??
console.log(1 in arr); // ??
πŸ’‘ Answer
  • arr[1] logs undefined.
  • 1 in arr returns false.

There's a hole in the array, not an actual undefined value.


🧩 18. setTimeout in Loops

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
πŸ’‘ Answer

Logs: 3, 3, 3.

All callbacks share the same i (3) due to var. Use let to capture each iteration.


🧩 19. The Missing Return

function foo() {
  return;
  {
    ok: true;
  }
}

console.log(foo()); // ??
πŸ’‘ Answer

Returns undefined.

The return statement is terminated before the object β€” it's a line break bug.


🧩 20. parseInt Quirk

console.log(parseInt("08")); // ??
πŸ’‘ Answer

Returns 8, but older engines may interpret '08' as octal and return 0.

Always pass radix: parseInt('08', 10).


🧩 21. isNaN vs Number.isNaN

console.log(isNaN("foo")); // ??
console.log(Number.isNaN("foo")); // ??
πŸ’‘ Answer
  • isNaN('foo') β†’ true (due to coercion).
  • Number.isNaN('foo') β†’ false.

Use Number.isNaN to avoid coercion.


🧩 22. Arguments Object Trap

function test(x) {
  arguments[0] = 99;
  return x;
}

console.log(test(42)); // ??
πŸ’‘ Answer

Returns 99.

In non-strict mode, arguments and named params are linked.


🧩 23. Double Negation Oddity

console.log(!!"false" == !!"true"); // ??
πŸ’‘ Answer

Returns true.

Both 'false' and 'true' are non-empty strings β†’ truthy.


🧩 24. Math with Arrays

console.log([1, 2] + [3, 4]); // ??
πŸ’‘ Answer

Returns '1,23,4'.

Arrays are coerced to strings: '1,2' + '3,4'.


🧩 25. Number + Object

console.log(1 + {}); // ??
πŸ’‘ Answer

Returns '1[object Object]'.

Object gets coerced to string.


🧩 26. Object Coercion to Number

console.log(+{}); // ??
πŸ’‘ Answer

Returns NaN.

+{} β†’ Number('[object Object]') β†’ NaN.


🧩 27. String Padding Confusion

console.log("5" - "2"); // ??
console.log("5" + "2"); // ??
πŸ’‘ Answer
  • '5' - '2' β†’ 3 (coerced to numbers).
  • '5' + '2' β†’ '52' (string concatenation).

🧩 28. typeof Function.prototype

console.log(typeof Function.prototype); // ??
πŸ’‘ Answer

Returns 'function'.

Function prototype is still a function.


🧩 29. Weird instanceof

console.log([] instanceof Array); // ??
console.log([] instanceof Object); // ??
console.log(function () {} instanceof Function); // ??
πŸ’‘ Answer

They're all true.

Everything in JS is ultimately an object, and functions are also objects.


🧩 30. Deleting Variables

var x = 10;
console.log(delete x); // ??
πŸ’‘ Answer

Returns false.

delete only works on object properties, not variables declared with var, let, or const.

πŸ”„ 31. Going Loopy

What will the result of the following code be?

for (;;) {
  console.log("What will happen?");
}
πŸ’‘ Answer

Infinite Loop.

Technically the for loop will run although since there's no exit condition it will create an infinite loop 🀯

About

πŸ’₯ JavaScript behaves. Just not the way you expect.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published