Scoping and Hoisting

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).

What hoisting creates for each kind of declaration
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
Hoisting in a module (strict mode), run as hoisting.mjsJavaScriptLive
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 block

In a sloppy-mode classic script, legacy web-compatibility rules let the block function leak out, and typeof inBlock prints function.