为何函数可读取声明在其下方的全局变量?与函数提升有何差异?
Great question — this gets to the heart of how JavaScript executes code, which is super important to wrap your head around. Let's break down both examples and clarify what's happening under the hood.
First Example: Why fn() can access foo declared below it?
Let's look at your code again (I assume you meant fn() instead of run() — a common typo!):
function fn() { console.log(foo); } const foo = 3; fn(); // Logs 3
This works because of variable declaration hoisting, combined with when you actually call the function. Here's the step-by-step breakdown of JavaScript's execution process:
Compilation Phase: Before any code runs, JavaScript scans the entire scope (here, the global scope) and picks up all declarations:
- The function declaration
function fn() {...}is fully hoisted to the top of the scope — meaning the entire function is available before any execution starts. - The
const foodeclaration is also hoisted to the top, but unlikevar,const(andlet) variables enter a Temporal Dead Zone (TDZ). This means you can't access them until the line where they're initialized (const foo = 3;) runs.
- The function declaration
Execution Phase:
- First, JavaScript runs
const foo = 3;— this exits foo from the TDZ and assigns it the value 3. - Then you call
fn()— when the function runs, it tries to accessfoo. Since foo has already been initialized at this point, it can successfully log the value 3.
- First, JavaScript runs
The key here is that even though foo's declaration is hoisted, you're only accessing it after it's been assigned a value.
Second Example: Function Hoisting in Action
Your second code:
fn(); // Logs "hello" function fn() { console.log("hello"); }
This is classic function hoisting. Unlike variable declarations, function declarations are fully hoisted — meaning the entire function definition is moved to the top of the scope during the compilation phase. So when fn() is called at the start, the function already exists in the scope, so it runs without error.
Are These "Different" Cases?
Short answer: No, they're both part of JavaScript's core hoisting mechanism — just different types of declarations being hoisted.
- Function declarations get full hoisting (entire function body is available early).
- Variable declarations (
var,let,const) get declaration hoisting (only the name is hoisted; initialization stays in place, with TDZ forlet/const).
The first example works because you're calling the function after the variable is initialized, even though the variable's declaration was hoisted. The second works because the entire function was hoisted, so you can call it before its definition.
内容的提问来源于stack exchange,提问作者shu

