Scope, Closures and this
Know exactly where a name is resolved and what `this` is for any call: scopes, hoisting and the TDZ, closures over variables, the four `this` rules plus arrows, and the quirks of `arguments`, `bind` and default parameters.
Key points
- 1
All declarations are hoisted:
varas undefined, functions with their body, andlet/const/classuninitialised (the temporal dead zone) until their line runs. Eventypeofthrows in the TDZ. - 2
Closures capture variables, not values.
for (let i ...)makes a fresh binding per iteration; aletdeclared outside the loop, or avar, is shared by every closure. - 3
thisis set by the call:newbeats explicit (call/apply/bind), which beats a method call, which beats a plain call (undefinedin strict mode,globalThisin sloppy mode). - 4
Arrow functions have no own
this,arguments,superornew.target. They resolve them lexically, which is why they make good callbacks but poor object methods. - 5
bindfixesthisand leading arguments, returns a new function every time (so keep the reference forremoveEventListener), and is ignored undernewexcept for its arguments. - 6
Default parameters run left to right in their own scope. With defaults, the body's
vars live in a separate scope, andfn.lengthstops counting at the first default. - 7
Only sloppy functions with simple parameter lists get a mapped
argumentsobject. Prefer rest parameters.
Common traps
(0, obj.method)()and(obj.m = obj.m)()losethis, while(obj.method)()keeps it.In sloppy scripts, a function declared inside a block also leaks to the enclosing scope (Annex B), first as
undefined.V8 (Chrome, Node.js) does not implement proper tail calls, so deep recursion still overflows the stack.
Test yourself on Scope, Closures and this
Ten questions, with the answer and explanation after each one.