Before a scope's code runs, the engine creates all of its bindings: hoisting. A function declaration is created with its body, so you can call it above its definition. A var starts as undefined, so a function expression stored in one cannot be called early. let, const and class stay uninitialized, and reading them first throws a ReferenceError: the temporal dead zone (TDZ).
| Declaration | Hoisted as | Scope |
|---|---|---|
| function f() {} | the complete function | enclosing function (block in strict mode) |
| var f = ... | undefined | enclosing function |
| let, const, class | uninitialized (TDZ) | enclosing block |
console.log(declared()); // hoisted with its body
function declared() { return "declared"; }
console.log(typeof expr); // var hoisted, value still undefined
var expr = function () { return "expression"; };
try { later(); } catch (e) { console.log(e.name, e.message); } // temporal dead zone
const later = () => "arrow";
{ function inBlock() { return "block"; } }
console.log(typeof inBlock); // strict mode: the function stays in its blockdeclared undefined ReferenceError Cannot access 'later' before initialization undefined
In a sloppy-mode classic script, legacy web-compatibility rules let the block function leak out, and typeof inBlock prints function.