---
title: "JavaScript Challenges That Trump the Normal Developer"
description: "Tricky JS puzzles that trip up even experienced devs—closures, this, coercion, and the event loop."
canonical_url: "https://isiahjones.dev/blog/javascript-challenges-that-trump-developers"
---

> Index of every page on this site: [llms.txt](https://isiahjones.dev/llms.txt)

Here are some **fun JavaScript challenges** that look simple but often stump developers. Each one exploits a quirk of the language—closures, `this`, type coercion, or the event loop. Try to predict the output (or fix the bug) before peeking at the answers.

---

## Challenge 1: The Closure Loop Trap

**What does this log?**

```javascript
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
```

<details>
<summary>

Answer

</summary>

**3, 3, 3** — not `0`, `1`, `2`.

`var` is function-scoped, so there’s only one `i`. By the time the timeouts run, the loop has finished and `i` is `3`. Fix: use `let` (block-scoped) so each iteration gets its own `i`, or pass `i` into an IIFE/`setTimeout(..., i)`.

</details>

---

## Challenge 2: `this` in a Method

**What does this log?**

```javascript
const obj = {
  name: "JS",
  greet() {
    console.log(this.name);
  },
};

const greet = obj.greet;
greet();
```

<details>
<summary>

Answer

</summary>

**undefined** (or in sloppy mode in a browser, possibly the global object’s `name`).

`this` is determined by **how** the function is called. `greet()` is called with no receiver, so `this` is `undefined` in strict mode (or the global in sloppy mode). The function wasn’t “taken from” `obj` in a way that preserves `this`. Fix: `obj.greet()` or `greet.call(obj)`.

</details>

---

## Challenge 3: Loose Equality Coercion

**What does this log?**

```javascript
console.log([] + []);
console.log([] + {});
console.log({} + []);
console.log(true + true + true === 3);
console.log("5" - 3);
console.log(`5${3}`);
```

<details>
<summary>

Answer

</summary>

- `[] + []` → `""` (arrays become `""` via `toString()`, so `"" + ""`).
- `[] + {}` → `"[object Object]"` (array → `""`, object → `"[object Object]"`).
- `{} + []` → `0` in some environments: `{}` can be parsed as an empty block, so `+[]` is evaluated; `+[]` coerces to `0`.
- `true + true + true === 3` → `true` (booleans coerce to numbers: 1 + 1 + 1 === 3).
- `"5" - 3` → `2` (`-` triggers numeric coercion).
- `"5" + 3` → `"53"` (`+` with a string does string concatenation).

</details>

---

## Challenge 4: The Event Loop Order

**What’s the log order?**

```javascript
console.log("A");

setTimeout(() => console.log("B"), 0);

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

console.log("D");
```

<details>
<summary>

Answer

</summary>

**A, D, C, B.**

Sync code runs first (A, D). Then the **microtask queue** (promises) runs before the **macrotask queue** (setTimeout), so C then B. Many developers guess B before C.

</details>

---

## Challenge 5: Object Reference vs “Copy”

**What does this log?**

```javascript
const a = { x: 1 };
const b = a;
b.x = 2;
console.log(a.x);
```

<details>
<summary>

Answer

</summary>

**2.**

`b` and `a` point to the **same object**. Mutating `b.x` mutates the shared object, so `a.x` is also `2`. “Copy” would require something like `const b = { ...a }` or `structuredClone(a)`.

</details>

---

## Challenge 6: Optional Chaining + Nullish

**What does this log?**

```javascript
const obj = { a: 0, b: "", c: null, d: undefined };
console.log(obj?.e ?? "missing");
console.log(obj?.a ?? "missing");
console.log(obj?.b ?? "missing");
```

<details>
<summary>

Answer

</summary>

- `obj?.e ?? "missing"` → `"missing"` (`e` is undefined, so nullish → fallback).
- `obj?.a ?? "missing"` → `0` (`??` only falls back for `null`/`undefined`; `0` is kept).
- `obj?.b ?? "missing"` → `""` (same idea; empty string is not nullish).

</details>

---

## Challenge 7: Array Sort “In Place”

**What does this log?**

```javascript
const arr = [10, 5, 20, 1];
arr.sort();
console.log(arr);
```

<details>
<summary>

Answer

</summary>

**[1, 10, 20, 5]** — not numeric order.

`.sort()` with no comparator sorts **lexicographically** (as strings). So `"10"` comes before `"5"`. Fix: `arr.sort((a, b) => a - b)`.

</details>

---

## Challenge 8: `parseInt` with a Radix

**What does this log?**

```javascript
console.log(Number.parseInt("08"));
console.log(Number.parseInt("08", 10));
console.log(["1", "2", "3"].map(Number.parseInt));
```

<details>
<summary>

Answer

</summary>

- `parseInt("08")` — historically some engines treated leading `0` as octal; in modern JS it’s usually `8` (treated as decimal). Best practice: always pass radix.
- `parseInt("08", 10)` → `8`.
- `["1", "2", "3"].map(parseInt)` → **[1, NaN, NaN]**. `map` passes `(element, index)`; `parseInt` takes a second argument as radix. So you get `parseInt("1", 0)`, `parseInt("2", 1)`, `parseInt("3", 2)` — only the first is valid. Fix: `.map((s) => parseInt(s, 10))` or `.map(Number)`.

</details>

---

## Challenge 9: Hoisting and Temporal Dead Zone

**What does this log?**

```javascript
console.log(foo);
var foo = "bar";

console.log(baz);
let baz = "qux";
```

<details>
<summary>

Answer

</summary>

- First line: **undefined** — `var foo` is hoisted and initialized with `undefined`; the assignment runs later.
- Third line: **ReferenceError** — `let baz` is in the TDZ until the declaration runs, so accessing it throws.

</details>

---

## Challenge 10: `typeof` and Undeclared

**What does this log?**

```javascript
console.log(typeof x);
console.log(typeof neverDeclared);
```

<details>
<summary>

Answer

</summary>

- If `x` was never declared: **"undefined"** — `typeof` is safe and returns `"undefined"` for undeclared identifiers (no ReferenceError).
- Same for `neverDeclared`: **"undefined"**.

</details>

---

## Use Them Wisely

These aren’t meant to gatekeep—they’re a reminder that JavaScript’s “weird parts” (scoping, `this`, coercion, event loop) can bite you in real code. Use `let`/`const`, avoid relying on `==` coercion, pass radix to `parseInt`, and remember: microtasks before macrotasks.

If you’ve got a favorite JS gotcha or a different solution, share it.

## Sitemap

See the full [sitemap](https://isiahjones.dev/sitemap.md) for all pages.
