This bonus pack is designed to train your JavaScript intuition.
The goal is not to run the code — the goal is to predict the behavior before execution.
First 3 chapters of the book here https://basescripts.com/the-weird-the-subtle-and-the-powerful-in-javascript-book-free-first-3-chapters
The Weird, the Subtle, and the Powerful in JavaScript
US : https://www.amazon.com/dp/B0GBTM5SCY
Canada : https://www.amazon.ca/dp/B0GBTM5SCY
Why JavaScript feels weird (and why that’s actually useful)
JavaScript wasn’t designed in a perfect lab environment. It was built fast, had to run in browsers, and—most importantly—it can’t break the web. That means some “odd” behaviors are permanent, and modern features have to stack on top of older rules.
If you learn the rules behind the weirdness, you stop guessing—and start predicting.
Here are a few of the famous “wait…what?” moments (and what they teach):
1) Coercion: JavaScript loves converting types
[] == false // true
0 == ” // true
true + true // 2
What’s happening?
== triggers type conversion. Arrays can become strings ([] -> “”), booleans become numbers (true -> 1), and comparisons start behaving like puzzles.
✅ Practical rule: prefer === unless you really know the coercion path you want.
2) Hoisting: declarations are processed before execution
console.log(a);
var a = 10;
Output: undefined
Why?
var a is hoisted and initialized to undefined during the creation phase, then assigned later.
Now compare:
console.log(b);
let b = 10;
This throws a ReferenceError due to the Temporal Dead Zone (TDZ)—b exists but isn’t initialized yet.
✅ Practical rule: use let/const, and declare before use.
3) Closures: the “superpower” behind modern JS patterns
function counter(){
let count = 0;
return () => ++count;
}
const c = counter();
c(); // 1
c(); // 2
Closures are why functions can remember state, even after the outer function finishes. They power:
- event handlers
- memoization
- debouncing
- private variables
- module patterns
✅ Practical rule: closures store live references, so be mindful of shared state and memory.
🧠 How to Use This Bonus Pack
For each item:
- Pause
- Predict the result
- Explain why it happens
- Run the code
- Compare your mental model with reality
If your prediction was wrong — you just learned something valuable.
🔹 CHAPTER 1 — JavaScript’s Weird Parts
🔮 Proactive Thinking Prompts
Use these in ChatGPT / Gemini:
- “Explain why JavaScript keeps historical bugs instead of fixing them.”
- “Walk me step-by-step through how JavaScript evaluates [] == ![] internally.”
- “Explain JavaScript’s type coercion rules like I’m debugging production code at 2AM.”
- “Show how == comparisons differ from === with real bug scenarios.”
- “Explain why JavaScript tries to ‘help’ developers and how that backfires.”
❓ Mini Quiz — Predict the Output
Q1
console.log([] + []);
A) []
B) “”
C) “[]”
D) Error
✅ Answer: B
Arrays convert to strings → “” + “”
Q2
console.log(1 + ‘2’ – 1);
A) 11
B) 12
C) 2
D) NaN
✅ Answer: C
1 + ‘2’ → “12” → “12” – 1 → 11 ← wait… trick question
Correct flow: “12” – 1 → 11
✔ Correct answer: B (11)
Q3
console.log(false == ‘0’);
A) true
B) false
✅ Answer: A
‘0’ → 0, false → 0
🛠 Exercise — Rewrite Safely
Rewrite without coercion bugs:
if (value == false) { … }
Solution
if (value === false) { … }
—or explicitly convert:
if (Boolean(value) === false) { … }
🔹 CHAPTER 2 — Hoisting & TDZ
🔮 Proactive Thinking Prompts
- “Simulate the JavaScript creation phase for this script.”
- “Explain why let variables exist but cannot be accessed before initialization.”
- “Show how hoisting prevents or causes bugs in real applications.”
- “Compare hoisting behavior between var, let, and function.”
- “Explain TDZ using a timeline diagram.”
❓ Mini Quiz — Predict the Output
Q1
console.log(a);
let a = 5;
A) undefined
B) null
C) ReferenceError
D) 5
✅ Answer: C
TDZ prevents access before initialization.
Q2
test();
function test() {
console.log(‘Hi’);
}
A) Error
B) Hi
✅ Answer: B
Function declarations are fully hoisted.
Q3
run();
var run = () => console.log(‘Run’);
A) Run
B) undefined
C) TypeError
✅ Answer: C
run is hoisted as undefined, not as a function.
🛠 Exercise — Fix the Hoisting Bug
calculate();
const calculate = () => console.log(‘Done’);
Solution
const calculate = () => console.log(‘Done’);
calculate();
🔹 CHAPTER 3 — Scope & Closures
🔮 Proactive Thinking Prompts
- “Explain closures using a memory snapshot diagram.”
- “Why do closures store references instead of copies?”
- “Show how closures can accidentally leak memory.”
- “Explain lexical scope vs dynamic scope.”
- “Rewrite this closure to reduce memory usage.”
❓ Mini Quiz — Predict the Output
Q1
let x = 1;
function outer() {
let x = 2;
return () => x;
}
console.log(outer()());
A) 1
B) 2
✅ Answer: B
Closures use lexical scope, not call location.
Q2
const funcs = [];
for (let i = 0; i < 3; i++) {
funcs.push(() => i);
}
console.log(funcs[0](), funcs[1](), funcs[2]());
A) 0 1 2
B) 3 3 3
✅ Answer: A
let creates a new binding per iteration.
Q3
function test() {
console.log(a);
let a = 10;
}
test();
A) undefined
B) 10
C) ReferenceError
✅ Answer: C
TDZ inside function scope.
🛠 Exercise — Build a Real Closure
Task: Create a once() utility
const once = fn => { /* your code */ };
Solution
function once(fn) {
let called = false;
let result;
return (…args) => {
if (!called) {
result = fn(…args);
called = true;
}
return result;
};
}
🚀 Advanced Challenge (Newsletter Bonus)
Before running this code — write down the output and WHY:
let a = 1;
(function() {
console.log(a);
var a = 2;
})();
✅ Answer
undefined
Why?
- var a is hoisted inside the IIFE
- Inner a shadows outer a
- Assignment happens after console.log
🧩 Final Mental Model Check
Ask yourself:
- Can I predict execution before running code?
- Do I understand why hoisting exists?
- Can I explain closures without saying “magic”?
- Can I debug by reasoning instead of trial-and-error?
If yes — you’re no longer using JavaScript.
You’re thinking in JavaScript.