js 变量声明

变量提升 就是:JavaScript 在运行代码之前,会先把代码里的变量声明函数声明 "提前"放到作用域的顶部,但赋值操作留在原地。


生活化比喻

想象你去一家公司上班:

  • 公司人事部(编译器)先花 5 分钟把全公司员工的工位牌(变量名)先摆好
  • 然后你才开始正式工作(执行代码)

所以你可以在公司正式开业前就看到工位牌,但工位上有没有电脑(值),那得看正式上班后有没有发。


1. var 的提升:最容易理解

ini 复制代码
console.log(a); // undefined(不会报错,但也不等于 10)
var a = 10;
console.log(a); // 10

这段代码在 JS 眼里,相当于这样执行:

javascript 复制代码
var a;          // 声明被"提升"到顶部,此时 a = undefined
console.log(a); // undefined
a = 10;         // 赋值留在原地
console.log(a); // 10

所以:

  • var a = 10; 在 JS 内部被拆成了两步
  • 第一步 var a; 被提到顶部,初始值是 undefined
  • 第二步 a = 10; 留在原位置

2. 函数提升:整个函数都会被提前

scss 复制代码
sayHi(); // "Hi!"  可以正常调用

function sayHi() {
  console.log("Hi!");
}

因为函数声明会被整体提升,所以即使写在后面,前面也能调用。


3. 但函数表达式不会整体提升

javascript 复制代码
sayHello(); // ❌ TypeError: sayHello is not a function

var sayHello = function() {
  console.log("Hello!");
};

原因:这里的 sayHello变量 ,不是函数声明。它只会按 var 的规则提升:

scss 复制代码
var sayHello;        // 提升到顶部,值是 undefined
sayHello();          // undefined() → 报错
sayHello = function() { ... };  // 赋值留在原地

4. letconst:也有提升,但不能提前用

ini 复制代码
console.log(b); // ❌ ReferenceError: Cannot access 'b' before initialization
let b = 20;

letconst 声明也会被提升,但 JS 给它们设了一个"暂时性死区"(TDZ):在声明之前不能访问,访问就报错。

可以理解为:工位牌摆好了,但上面盖着红布,在正式揭牌之前你看一眼就报错。

javascript 复制代码
{
  // TDZ 开始
  // console.log(a); // ❌ ReferenceError: Cannot access 'a' before initialization

  let a;          // ← 声明在这里,TDZ 结束
  console.log(a); // ✅ undefined(不是报错!)

  a = 10;         // 赋值
  console.log(a); // 10
}

关键点: let a; 声明之后,变量已经存在,只是值为 undefined,不会报 TDZ 错误。


5. 引擎实际执行流程

以 V8 为例,真实流程大致是:

sql 复制代码
源代码
  ↓
词法分析 + 语法分析 → 抽象语法树 AST
  ↓
作用域分析(Scope Analysis):确定有哪些变量、函数、作用域链
  ↓
编译成字节码(Bytecode,Ignition 解释器)
  ↓
执行字节码
  ↓
热点代码被优化编译成机器码(TurboFan JIT)

所以**"变量提升"真正发生的位置**,是上面第二步和第三步之间------也就是 AST 解析完成、作用域建立的时候

6. 简单总结表

arduino 复制代码
console.log(x); // 这一行之前有没有声明?
声明方式 声明之前访问会怎样 原因
var x = 1 undefined 声明提升,初始化为 undefined
let x = 1 报错 ReferenceError 声明提升,但处于暂时性死区
const x = 1 报错 ReferenceError 同上
function x(){} 可以正常调用 整个函数被提升

7. 一个容易混淆的例子

ini 复制代码
var x = 1;

function foo() {
  console.log(x); // undefined,不是 1!
  var x = 2;
  console.log(x); // 2
}

foo();

为什么第一次打印不是 1

因为函数内部的 var x = 2 发生了函数作用域内的提升:

Javascript

javascript 复制代码
function foo() {
  var x;          // 提升到函数顶部
  console.log(x); // undefined
  x = 2;          // 赋值
  console.log(x); // 2
}

这里提升的是函数自己的 x,它"遮住"了外部的 x。这叫做变量遮蔽(Shadowing)


最终一句话

变量提升就是:声明先被 JS 放到顶部,但赋值不动;var 会提前变成 undefinedlet/const 会提前让你报错,函数声明可以提前调用。

相关推荐
Cicada1281 小时前
Web 服务器怎么选
运维·服务器·前端
不可能片场1 小时前
接口返回 200 不代表活着:catch-all 路由的陷阱
前端·electron
用户921080262861 小时前
Cookie 和 Token,到底谁在负责登录态
前端
阳火锅1 小时前
2025,记住这一年!它是古法编程的最后一年。
前端·javascript·面试
sibylyue1 小时前
【报表和BI大屏】ECharts数据可视化大屏 / 驾驶舱
前端·信息可视化·echarts
用户921080262861 小时前
为什么 Header 是 HTTP 的灵魂
前端
特立独行的猫A1 小时前
AtomMQTT Broker — 用 Rust 实现的轻量级高性能 MQTT 消息代理
前端·后端·架构
IMPYLH1 小时前
HTML 的 <s> 元素
前端·html
lhldsg1 小时前
折扣卡CPS小程序定制全流程实战指南:从需求分析到上线部署
java·前端·小程序