JavaScript Curriculum
Where Does a Variable Live?
mediumTwo engineers on the Nexus team both declare a variable called count in their code. Neither one is wrong. The variables don't conflict. The program doesn't explode. JavaScript has a system for this — a set of rules that decides exactly where each variable exists and who can reach it. Understanding that system is the difference between writing code that works reliably and writing code that breaks in mysterious ways.
The invisible containers around your code
Every variable you declare lives somewhere. JavaScript divides a program into nested containers called scopes — and a variable can only be seen from inside the scope it was declared in, or from any scope nested inside that one.
The rule is one-directional: inner scopes can see outward, outer scopes cannot see inward.
Click each variable below to see exactly which scopes can and cannot access it:
Click a variable to see which scopes can access it.
The three scope levels
Global scope — the outermost container. Variables declared here are accessible from everywhere in your program. Use sparingly: global variables are shared state, and shared state is the source of many bugs.
Function scope — every function creates its own scope. Variables declared inside a function (var, let, or const) are invisible from outside that function.
Block scope — any pair of curly braces { } creates a block scope for let and const declarations. This includes if bodies, for loops, while loops, and plain { } blocks.
Hoisting — what the engine does before running your code
JavaScript doesn't just run your code top to bottom. Before execution begins, the engine does a preparation pass — scanning for declarations and doing something different depending on which keyword you used.
Click each keyword and each line position to see exactly what happens:
Line BEFORE declaration — result
undefined
Hoisted and initialised to undefined — no crash, but wrong value
what the JS engine does
JS hoists var declarations to the top of their function scope and sets them to undefined before any code runs. The assignment stays in place.
⚠️ Technically works but silently returns undefined — a common source of bugs. Never use var.
The summary:
var— hoisted and initialised toundefined. Accessing before declaration gives youundefinedsilently — a hidden bug waiting to happen.let/const— hoisted but not initialised. The gap from the start of their scope to their declaration is the Temporal Dead Zone (TDZ). Any access in this zone throws aReferenceErrorimmediately — loud and obvious.
Loud errors are better than silent wrong values. This is why let and const are safer.
Closures — functions that remember
A closure is what happens when an inner function retains access to variables from its outer function's scope — even after the outer function has finished executing.
Step through the counter factory below. Pay close attention to count: it's declared inside makeCounter, but the returned inner function keeps a live reference to it forever.
closed-over: count
—
not created yet
output log
nothing yet…
Start — factory function not yet called
The key insight: every call to makeCounter creates a fresh scope with its own count. The two counters are completely independent — they don't share state.
Closures in real code
Closures aren't an exotic edge case — they're used constantly:
Your challenge
Build a greeter factory — same structure as the counter factory, but instead of remembering a number it remembers a greeting string.
makeGreeter("Hello") should return a function. That function takes a name and returns "Hello, Alex!" (or whatever name is passed). makeGreeter("Hi") should return a completely independent function that says "Hi, ..." instead.
Two closures. Two independent backpacks. Same structure, different contents.
Challenge
Write a function called makeGreeter(greeting) that returns a new function. The returned function should accept a name argument and return the string greeting + \", \" + name + \"!\". Create two greeters — one with \"Hello\" and one with \"Hi\" — and call each with a name to prove they work independently.
Where Does a Variable Live?
mediumTwo engineers on the Nexus team both declare a variable called count in their code. Neither one is wrong. The variables don't conflict. The program doesn't explode. JavaScript has a system for this — a set of rules that decides exactly where each variable exists and who can reach it. Understanding that system is the difference between writing code that works reliably and writing code that breaks in mysterious ways.
The invisible containers around your code
Every variable you declare lives somewhere. JavaScript divides a program into nested containers called scopes — and a variable can only be seen from inside the scope it was declared in, or from any scope nested inside that one.
The rule is one-directional: inner scopes can see outward, outer scopes cannot see inward.
Click each variable below to see exactly which scopes can and cannot access it:
Click a variable to see which scopes can access it.
The three scope levels
Global scope — the outermost container. Variables declared here are accessible from everywhere in your program. Use sparingly: global variables are shared state, and shared state is the source of many bugs.
Function scope — every function creates its own scope. Variables declared inside a function (var, let, or const) are invisible from outside that function.
Block scope — any pair of curly braces { } creates a block scope for let and const declarations. This includes if bodies, for loops, while loops, and plain { } blocks.
Hoisting — what the engine does before running your code
JavaScript doesn't just run your code top to bottom. Before execution begins, the engine does a preparation pass — scanning for declarations and doing something different depending on which keyword you used.
Click each keyword and each line position to see exactly what happens:
Line BEFORE declaration — result
undefined
Hoisted and initialised to undefined — no crash, but wrong value
what the JS engine does
JS hoists var declarations to the top of their function scope and sets them to undefined before any code runs. The assignment stays in place.
⚠️ Technically works but silently returns undefined — a common source of bugs. Never use var.
The summary:
var— hoisted and initialised toundefined. Accessing before declaration gives youundefinedsilently — a hidden bug waiting to happen.let/const— hoisted but not initialised. The gap from the start of their scope to their declaration is the Temporal Dead Zone (TDZ). Any access in this zone throws aReferenceErrorimmediately — loud and obvious.
Loud errors are better than silent wrong values. This is why let and const are safer.
Closures — functions that remember
A closure is what happens when an inner function retains access to variables from its outer function's scope — even after the outer function has finished executing.
Step through the counter factory below. Pay close attention to count: it's declared inside makeCounter, but the returned inner function keeps a live reference to it forever.
closed-over: count
—
not created yet
output log
nothing yet…
Start — factory function not yet called
The key insight: every call to makeCounter creates a fresh scope with its own count. The two counters are completely independent — they don't share state.
Closures in real code
Closures aren't an exotic edge case — they're used constantly:
Your challenge
Build a greeter factory — same structure as the counter factory, but instead of remembering a number it remembers a greeting string.
makeGreeter("Hello") should return a function. That function takes a name and returns "Hello, Alex!" (or whatever name is passed). makeGreeter("Hi") should return a completely independent function that says "Hi, ..." instead.
Two closures. Two independent backpacks. Same structure, different contents.
Challenge
Write a function called makeGreeter(greeting) that returns a new function. The returned function should accept a name argument and return the string greeting + \", \" + name + \"!\". Create two greeters — one with \"Hello\" and one with \"Hi\" — and call each with a name to prove they work independently.