Functions and Scope
What this domain covers
How functions are created, hoisted, and called, and how the scope chain and this binding resolve names. Closure questions dominate this domain.
Forms and hoisting
Function declarations are hoisted completely and are callable before their line. Function expressions and arrow functions assigned to const are not - the binding sits in the temporal dead zone. Parameters can have defaults, evaluated left to right at call time, and ...rest collects trailing arguments into a real array. arguments is an array-like available in normal functions only, not in arrows.
Scope and closures
Scoping is lexical: a function sees variables from where it was defined, not where it was called. A closure is an inner function that keeps a live reference to outer variables after the outer call returns. var in a loop shares one binding across iterations; let creates a fresh binding per iteration, which is why setTimeout callbacks behave differently.
this
Determined at call time: method call → the receiver; plain call in strict mode → undefined (global object in sloppy mode); new → the new instance; explicit via call/apply/bind. Arrow functions have no own this; they capture it lexically, and they also lack arguments and prototype.
Worked example
function makeCounter() {
let count = 0;
return () => ++count;
}
const c1 = makeCounter();
const c2 = makeCounter();
console.log(c1(), c1(), c2());
Reasoning: each makeCounter() call creates a fresh scope. c1 closes over one count, incrementing to 1 then 2; c2 closes over a separate count starting at 0, so the output is 1 2 1.
Traps
- Calling a function expression before its definition line.
- Passing a method as a callback (
setTimeout(obj.run, 0)) and losingthis; bind it or wrap it in an arrow. - Expecting a default parameter to apply when the argument is
null- it only applies forundefined. - Redeclaring
let xin the same block is a SyntaxError.