04提升与严格模式
「还没声明就能调用?」——这是变量提升(Hoisting)。 「写错名字居然不报错?」——这是宽松模式的锅。本课讲清提升规则、暂时性死区 TDZ、 严格模式到底改了什么,帮你避开一批「诡异但常见」的 bug。
一、什么是变量提升
JS 引擎在执行代码前会先「扫一遍」作用域,把声明(不是赋值)提到当前作用域顶部。 函数声明整体提升,变量只提升声明、不提升赋值。
二、var / let / const / function 提升对照
| 声明方式 | 是否提升 | 提升后初始值 | 声明前访问 |
|---|---|---|---|
var |
✓ 声明提升 | undefined |
得到 undefined(不报错) |
let / const |
✓ 提升但进 TDZ | 无(死区) | ReferenceError |
function 声明 |
✓ 整体提升 | 完整函数 | 可以直接调用 |
const fn = () => … |
同 let | TDZ | ReferenceError |
import |
✓ 模块提升 | 绑定已就绪 | 可直接用(模块顶层) |
点击按钮
三、暂时性死区(TDZ)
let / const 从作用域开始到声明语句之间有一段
「不能访问」的区域,叫暂时性死区(Temporal Dead Zone)。
这是故意设计的——让「先用后声明」尽早报错,而不是悄悄变成 undefined。
ReferenceError: Cannot access 'x' before initialization
就是踩了 TDZ。解法:把声明挪到使用之前,别用 var「绕过」。四、函数提升的优先顺序
同一作用域里同时出现 function 声明和 var 同名变量时:
- 先处理
function声明(整体提升) - 再处理
var声明(若同名则跳过,不覆盖已提升的函数) - 最后按代码顺序执行赋值
function 声明工具函数,
要么纯 const fn = () =>,别混。五、严格模式:更干净的 JS
文件或函数顶部写 'use strict' 开启严格模式。
ES Module 默认严格,现代项目基本都在严格模式下运行。
宽松模式会静默犯错
给未声明变量赋值 → 创建全局变量;重复参数名不报错;八进制字面量 010 可用;with 可用;this 默认 window。
严格模式直接报错
未声明赋值 → ReferenceError;重复参数 → SyntaxError;八进制要写 0o10;禁 with;独立调用 this = undefined。
| 行为 | 宽松 | 严格 |
|---|---|---|
| 未声明变量赋值 | 偷偷变全局 | ReferenceError |
| 只读属性写入 | 静默失败 | TypeError |
| 删除不可删属性 | 静默失败 | TypeError |
| 重复参数名 | 允许 | SyntaxError |
| 八进制字面量 | 010 = 8 | 语法错误,用 0o10 |
with | 允许 | 语法错误 |
| 独立函数的 this | window | undefined |
| 新增全局 | eval 污染外层 | eval 有独立作用域 |
点按钮对比
六、怎么开启 / 何时自动开启
- 整个文件:顶部写
'use strict'; - 单个函数:函数体第一行写
- ES Module(
<script type="module">/import/export)默认严格 - class 体内部默认严格
- 老式全局脚本默认宽松——迁移老代码时留意
'use strict'。
维护老代码时,可以在整个文件加一行,把「静默 bug」变成「显式报错」。七、和前面知识的连接
- 作用域:TDZ 只存在于 let/const 的块级/函数作用域;var 没有 TDZ。
- this:严格模式独立调用 this = undefined,影响默认绑定规则。
- 模块:Module = 严格模式 + 独立作用域,两边一起记。
- 调试:看到 TDZ / 严格模式报错,先查声明位置,再查是否「未声明赋值」。
八、本章小结
- 提升:声明(不是赋值)被提前。
function整体提升,var只提声明得 undefined。 let/const有 TDZ,声明前访问 → ReferenceError(故意设计)。- 同名
function声明会赢过var的声明提升,但后续赋值会覆盖。 - 严格模式禁止未声明赋值、重复参数、
with;this 独立调用为 undefined。 - ES Module / class 默认严格,新项目自然干净。
- 调试口诀:
undefined想到 var 提升;before initialization想到 TDZ;is not defined想到未声明。
var / let 做「声明前访问」,对照报错信息;
② 写一个严格模式函数,故意给未声明变量赋值,看抛出什么;
③ 解释为什么 function foo(a = b, b = 1) 会报错,而调换参数顺序就不会。