I used to "learn" the same JavaScript concepts every few months. I'd read about the event loop, nod along, and a month later get a predict-the-output question wrong in an interview.
What finally worked was seeing the rule every day. I turned each concept into a phone wallpaper with a short why at the bottom, so every time I unlock my phone, I revise one thing.
Here are the five that helped most. Try to predict each output before reading the answer.
1. The event loop: why promises beat setTimeout
setTimeout(() => console.log('C'));
Promise.resolve().then(() => console.log('B'));
console.log('A');
Output: A, B, C
The rule that made it stick:
- Run all synchronous code on the call stack.
- When the stack is empty, drain every microtask (promise callbacks, code after
await,queueMicrotask). - Then take one macrotask (
setTimeout, I/O, clicks) and repeat.
So a promise callback always runs before setTimeout(fn, 0), even though both look "async". The 0 doesn't mean "now"; it means "next macrotask".
2. Closures: functions remember where they were born
function counter() {
let count = 0;
return () => ++count;
}
const next = counter();
next();
next();
console.log(next());
Output: 3
The returned function keeps a live reference to count, not a copy. That's a closure: a function plus the variables that were in scope when it was created.
The classic interview trap is the same idea:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i));
}
// 3, 3, 3
All three callbacks close over the same var i, which is 3 by the time they run. Change var to let and you get 0, 1, 2, because let creates a new binding for each loop iteration.
3. this: ask how the function was called
const user = {
name: 'Asha',
greet() { return `Hi, ${this.name}`; },
};
const greet = user.greet;
console.log(greet());
Output: in an ES module or strict mode, TypeError: Cannot read properties of undefined (reading 'name'). In an old-style non-strict script, this falls back to the global object, so you get a wrong name instead of an error. Either way, it's not Hi, Asha.
this isn't decided where a function is written; it's decided by how it's called. Check these in order:
-
Arrow function: never gets its own
this; uses the one from the surrounding code. -
new Fn():thisis the brand-new object. -
fn.call(obj)/apply/bind:thisisobj. -
obj.fn():thisis whatever is left of the dot. -
Plain
fn():undefinedin strict mode.
greet() has no dot at call time, so it lost user.
4. Type coercion: + and - play by different rules
Cover the answers and predict:
[] + {} // ?
"5" - 2 // ?
"5" + 2 // ?
null == 0 // ?
null >= 0 // ?
Answers: "[object Object]", 3, "52", false, true
-
+switches to string concatenation if either side ends up a string.[]becomes""and{}becomes"[object Object]". -
-has no string version, so it always converts to numbers. -
null >= 0convertsnullto0, so0 >= 0istrue. But==has a special rule:nullonly equalsundefined. That's hownullcan be>= 0without being== 0.
5. Hoisting: let is hoisted too, just locked
console.log(a); // ?
var a = 1;
console.log(b); // ?
let b = 2;
Output: undefined, then ReferenceError: Cannot access 'b' before initialization
Both declarations are hoisted. The difference:
-
varis hoisted and initialized toundefined. -
let,constandclassare hoisted but sit in the temporal dead zone until their line runs. - Function declarations are hoisted completely, so you can call them before they appear.
The error is a feature: it catches bugs that var would silently hide as undefined.
Want them on your lock screen?
I run TechCookies, a small platform built around this "predict first, then learn the why" style. These wallpapers came out of it, and there are 26 in total: JavaScript, TypeScript, SQL, DSA, system design, Java and Python.
π Get the full wallpaper pack
Tip for iPhone: put them in one album and use Photo Shuffle on the lock screen, so you get a new concept every day.
Which JavaScript concept took you the longest to really understand? I'm making more wallpapers and would like to cover the ones people actually struggle with.
Top comments (0)