You Know JavaScript. Your Brain Just Forgets. (Part 1: How JavaScript Thinks)
Before We Begin
You’ve shipped features. Fixed bugs at 2 a.m. Reviewed a mountain of pull requests.
Then the interviewer smiles and asks, “So, what’s the temporal dead zone?”
And your brain returns… undefined.
Or it’s closures. Or Promise.race. Or starvation. Or inversion of control.
Quick check: if you just answered all of those in your head, congratulations. Close this tab and go get a coffee, you’ve earned it. Still here? Welcome. You’re in very good company.
This isn’t a knowledge problem. It’s a recall problem. We use these things every day. We just stop calling them by their names.
So here’s your cheat sheet, in two parts. Part 1 is how JavaScript thinks: 11 fundamentals, each with a one-line answer, a tiny example and a picture.
Five minutes before the interview? Just read the one-liners at the top of each section and the Remember it lines.
Let’s go.
1. JavaScript and the Execution Context: Where It All Begins
In one line: JavaScript is a synchronous, single-threaded language, and all code runs inside an execution context.
Picture JavaScript as a chef with one pair of hands. One dish at a time, in order. No multitasking, no skipping ahead.
The kitchen: execution context
Every program gets one big kitchen, the Global Execution Context. It’s set up in two phases:
- Memory phase (gather the ingredients): variables and functions get memory as key-value pairs. Variables start as
undefined. - Code phase (start cooking): code runs one line at a time.
Call a function, and a new mini kitchen opens inside the big one. When the function returns, it closes.
var n = 10;function square(num) {
var ans = num * num;
return ans;
}square(n);
Memory side = Variable Environment. Code side = Thread of Execution. (The diagram shows n: 10, which is memory after the code has run.)
Remember it: Two phases, memory then code. Every function call gets its own execution context, which disappears when it returns.
2. Hoisting: Using Things Before You Write Them
In one line: variables and functions get memory before the code runs, so you can use them before they’re declared.
Think of a restaurant that reserves your table before you arrive. The table is ready, but the chair is empty. That empty chair is undefined.
Functions are the VIP guests. They arrive fully seated, so calling them early works.
console.log(n); // undefined
greet(); // "Hello!"var n = 10;function greet() {
console.log("Hello!");
}
Remember it: Hoisting = memory first, code later. var gives undefined early, functions work fully.
3. let, const and the Temporal Dead Zone: The Waiting Room
In one line:
letandconstare hoisted too, but you can't touch them until their line runs.
var is the relaxed guest: "I'm here, I'm just empty."
let and const sit in a waiting room from the moment they're hoisted until their line runs. That waiting room is the temporal dead zone. Call them out early, and JavaScript throws an error.
console.log(a); // undefined
console.log(b); // ReferenceError: Cannot access 'b' before initializationvar a = 10;
let b = 20;Interview trap: two errors that look alike
Cannot access 'b' before initialization: it exists, it's just still in the waiting room.b is not defined: it was never declared at all.
Remember it: The temporal dead zone is the time between hoisting and initialization of let and const. Access them there, and you get a ReferenceError.
4. Scope and the Scope Chain: Who Can See What
In one line: scope is where a variable can be used, and it’s decided by where the code is written.
Think of rooms inside rooms. A child room can peek into its parent’s room, all the way out to the front door. But a parent can never peek into the child’s room.
“Lexical” sounds fancy, but it only means where the code physically sits.
- Lexical environment = local memory + a reference to the parent’s lexical environment.
- Scope chain = the path JavaScript follows to find a variable: own room, then parent, then all the way to global.
function a() {
var x = 7;function c() {
console.log(x); // 7, found in the parent's room
} c();
}a();
Fun fact: even an empty JavaScript file gets a global execution context. In the browser, you also get a free window object and a this that points to it.
Remember it: Lexical environment = local memory + parent’s reference. Following those references upward is the scope chain.
5. Block Scope and Shadowing: What Happens Inside { }
In one line:
letandconststay inside the curly braces.varescapes.
A block is code wrapped in { }, also called a compound statement. It lets you put many statements where JavaScript expects just one, like after an if.
Inside a block, let and const get their own private memory space. var doesn't care about walls. It walks right out.
{
var a = 10;
let b = 20;
}console.log(a); // 10, var escaped
console.log(b); // ReferenceError, let stayed insideShadowing: the inner one wins
Same name inside and outside a block? Inside the block, the inner variable “shadows” the outer one. Outside, the outer one is untouched.
let b = 100;{
let b = 20;
console.log(b); // 20
}console.log(b); // 100
Illegal shadowing: when var breaks the rules
let a = 10;{
var a = 20; // SyntaxError: Identifier 'a' has already been declared
}var tries to escape the block and crashes into the let that already lives outside. (The reverse, var outside and let inside, is perfectly legal.)
Remember it: Block = code in { }. Shadowing = the inner variable hides the outer one. Shadowing a let with a var is illegal.
6. Closures: The Function With a Backpack
In one line: a closure is a function bundled together with its lexical environment.
When a function leaves home, it packs a backpack with every variable it could see. Even after its parent function is long gone, the backpack stays on.
function x() {
var a = 7;
function y() {
console.log(a);
}
a = 100;
return y;
}var z = x(); // x is done and gone
z(); // 100Plot twist: it prints 100, not 7. The backpack holds references, not photocopies. Change the value, and the closure sees the change.
The famous setTimeout loop
for (var i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), i * 1000);
}
// 4, 4, 4for (let i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), i * 1000);
}
// 1, 2, 3With var, all three callbacks share one i. With let, every loop round gets its own fresh i.
Data hiding: a secret only the closure knows
function createCounter() {
let count = 0;
return () => ++count;
}const counter = createCounter();
counter(); // 1
counter(); // 2
// nobody outside can touch countWhere you’ll meet closures: the module pattern, currying, once, memoize, async state, setTimeout and iterators.
One caution: a backpack you never take off gets heavy. Closures keep variables alive, so careless use can hold memory longer than needed.
Get Sonia Goplani’s stories in your inbox
Join Medium for free to get updates from this writer.
Remember it: A closure remembers the variables from where it was written, even after the outer function has returned. It holds references, not copies.
7. Currying: One Argument at a Time
In one line: currying turns
f(a, b)intof(a)(b), and it works because of closures.
Ordering coffee: “Size?” Large. “Milk?” Oat. “Sugar?” None. One question at a time, and the barista remembers every answer. That memory is a closure.
const add = a => b => a + b;add(2)(3); // 5const addTwo = add(2); // a = 2 is remembered
addTwo(10); // 12
Where it helps: reusing a function with some arguments already fixed, like addTwo above.
Remember it: Currying = one argument at a time, powered by closures.
8. The this Keyword: It Depends Who’s Asking
In one line:
thisis decided by how a function is called, not where it is written.
this is like the word "here". Where "here" points depends on where you're standing when you say it.
Where does this point?
- Global scope: the global object (
windowin the browser). - Inside a regular function call:
undefinedin strict mode,windowotherwise. - Inside a method,
obj.fn(): the object before the dot. - Inside an arrow function: it has no
thisof its own, so it borrows one from the surrounding code. - With
call,applyorbind: whatever you choose. - Inside an event listener (regular function): the element that received the event.
const user = {
greet() {
console.log(this === user);
}
};user.greet(); // true, called with user before the dotconst greet = user.greet;
greet(); // false, the object got left behind
Interview trap: copy a method into a variable, and it forgets its object. this is not stored with the function, it's decided at every call.
Remember it: this is decided at call time. Quick trick: look left of the dot.
9. Arrow Functions: Short, Sweet, and Borrowing Their this
In one line: arrow functions are a shorter way to write functions, and they don’t have their own
this.
// Regular function
function square(n) {
return n * n;
}// Arrow function
const squareArrow = n => n * n;An arrow function is like a guest who never brings their own umbrella. When it rains, they share yours. Their this is whatever this was around them when they were written.
Where that actually helps
const timer = {
seconds: 0,
start() {
setTimeout(function () {
console.log(this.seconds); // undefined, this is not timer
}, 1000);setTimeout(() => {
console.log(this.seconds); // 0, borrowed from start()
}, 1000);
}
};timer.start();
How arrows are different
- No own
this. It comes from the surrounding code. This is called lexicalthis. call,applyandbindcan't change it.- No
argumentsobject. Use rest parameters,(...args), instead. - Can’t be used with
new. They are not constructors. - Not hoisted like function declarations. They live in a variable, so a
constarrow sits in the temporal dead zone until its line runs.
Interview trap: don’t write object methods as arrow functions. They won’t see the object as this.
Remember it: Arrow functions are shorter, and they borrow this from where they are written.
10. call, apply and bind: Choosing Your this
In one line:
callandapplyrun a function right now with thethisyou choose.bindgives you a new function withthislocked in.
Now that this makes sense, here are the three ways to take control of it.
const person = { name: "Sonia" };function greet(greeting, mark) {
console.log(greeting + ", " + this.name + mark);
}greet.call(person, "Hello", "!"); // Hello, Sonia!
greet.apply(person, ["Hello", "!"]); // Hello, Sonia!const later = greet.bind(person);
later("Hi", "!"); // Hi, Sonia!
The ABC trick:
- Call = Comma-separated arguments.
- Apply = Array of arguments.
- Bind = Brings back a new function for later.
Bonus: give bind some arguments up front, and you get partial application, currying's close cousin.
const multiply = (a, b) => a * b;
const double = multiply.bind(null, 2);
double(5); // 10Remember it: call and apply run now, bind returns a function for later. None of them can change an arrow function's this.
11. Prototypes: JavaScript’s Family Tree
In one line: every object has a hidden link to another object, its prototype, and JavaScript follows that chain to find properties the object doesn’t have itself.
Your wallet is empty, so you ask your parent. Their wallet is empty too, so they ask your grandparent. That’s the prototype chain.
Ever wondered where push comes from? You never wrote it.
const arr = [1, 2, 3];
arr.push(4); // push lives on Array.prototype, not on arrThe chain: arr → Array.prototype → Object.prototype → null. When the chain reaches null, JavaScript gives up and returns undefined.
Building your own family tree
const animal = {
eat() { console.log("Eating"); }
};const dog = Object.create(animal); // animal is dog's prototype
dog.bark = () => console.log("Woof");dog.bark(); // found on dog itself
dog.eat(); // not on dog, found on its prototype
This is prototypal inheritance: objects inheriting directly from other objects.
Sound familiar? It works just like the scope chain. The scope chain finds variables, and the prototype chain finds properties.
The two names that confuse everyone
__proto__is the link every object has to its prototype. (The modern way to read it isObject.getPrototypeOf(obj).)prototypeis a property on functions and classes. Objects created withnewget linked to it.
class Person {
greet() { console.log("Hi"); }
}const sonia = new Person();
Object.getPrototypeOf(sonia) === Person.prototype; // trueInterview trap: classes in JavaScript are syntactic sugar. Under the hood, it’s prototypes all the way down.
Remember it: Every object links to a prototype. JavaScript walks that chain to find properties, ending at null.
Two-Minute Revision: Read This in the Elevator
All of Part 1 in 16 lines. Screenshot it, save it, read it on the way in.
- JavaScript: a synchronous, single-threaded language.
- Execution context: built in two phases, memory creation and code execution.
- Function call: creates a new execution context, deleted when the function returns.
- Hoisting: memory is given before code runs, so
vargivesundefinedearly and functions work fully. - Temporal dead zone: the time between hoisting and initialization of
letandconst. - Lexical environment: local memory plus a reference to the parent’s lexical environment.
- Scope chain: the chain of lexical environments JavaScript follows to find a variable.
- Block scope:
letandconststay inside{ },varleaks out. - Shadowing: an inner variable hides an outer one. Shadowing a
letwith avaris illegal. - Closure: a function bundled with its lexical environment. It remembers references, not copies.
- Currying:
f(a, b)becomesf(a)(b), powered by closures. this: decided at call time. Look left of the dot.- Arrow functions: shorter, with no own
this. They borrow it from where they're written. - call, apply, bind: comma-separated, array, and a new function for later.
- Prototype chain: objects link to prototypes, and JavaScript walks the chain to find properties, ending at
null. - Classes: syntactic sugar over prototypes.
One Last Thing
If a word slips away mid-interview, don’t panic. Your brain didn’t delete it. It’s just in the temporal dead zone for a moment.
Take a breath. Picture the boxes, the backpack and the VIP line. Start with the one-liner, and the rest will follow.
Coming up in Part 2, How JavaScript Waits and Reacts: callbacks, event bubbling, promises, the event loop, async/await, and a peek under the engine’s hood.
Save this. Share it with someone who has an interview this week. Come back whenever your memory returns undefined.
You’ve got this.










