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. let 和 const:也有提升,但不能提前用

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

let 和 const 声明也会被提升,但 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 会提前变成 undefined,let/const 会提前让你报错,函数声明可以提前调用。

相关推荐
派小心.9 小时前
React StrictMode埋点双执行的验收方法
前端·前端框架
IT_陈寒9 小时前
Python的GIL锁让我把多线程代码全重写了!
前端·人工智能·后端
百度一下吧9 小时前
umi后台管理项目实战:从工程搭建到生产构建
java·前端·javascript
派小心.9 小时前
App埋点排查:iOS冷启动事件漏报先看初始化还是上报
前端·数据分析
事圆则缓9 小时前
Flutter 状态管理框架对比(六):同一个购物车,四种方案怎么落地?
前端·javascript·flutter
peter67689 小时前
css揭秘-背景和边框
前端·css
派小心.9 小时前
小程序埋点验收:自定义事件上报后页面参数怎么核对
前端·数据分析
前端snow19 小时前
ai agent --- postgreSQL 关系型数据库
前端
卷无止境20 小时前
ECharts:把数据变成故事的可视化利器
前端
Eric_见嘉20 小时前
在职前端 Skill 和 MCP 分享
前端·后端·agent