前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库ESM import 声明提升与执行顺序
JSJavaScript模块与语言机制

ESM 的 import 声明是否会被提升?这对代码执行顺序有何影响?

ESM 的 import 声明会被提升,因为模块的实例化阶段先于执行阶段,import 绑定在代码运行前已创建。但这不表示值已可用,实际值依赖导出模块的执行进度。

前端进阶之旅 · 一题精讲更新于 2026.09.05
JavaScript#模块与语言机制
先看核心答案
理解线索

模块求值顺序

  1. 三阶段加载解析、实例化、执行,顺序不可逆
  2. 绑定早于值绑定先创建,值由导出初始化决定
  3. 只读即时不可赋值,但内容可能变化

排除了循环依赖,否则 TDZ 更复杂

核心回答

先记住这个答案

会提升。ES6 模块的加载分三个步骤:解析、实例化、执行。在解析阶段,import 被静态分析;实例化阶段创建所有模块绑定,然后才执行模块体。因此 import 声明的绑定在模块代码执行前已建立,import 语句的位置不影响绑定是否可用。但这不同于 var 提升:import 绑定是只读实时视图,没有自身初始化过程。在没有循环依赖的常规加载中,依赖模块会先于导入模块完成执行,所以绑定已完成初始化(若导出的变量是 let x; 未赋值,则为 undefined);若存在循环依赖,导出模块可能尚未执行到初始化语句,此时访问绑定会因暂时性死区而抛出 ReferenceError。

  • import 绑定在模块求值前已创建
  • 绑定只读实时,值随导出模块变化
  • 变量提升概念不适用于 import 的值

机制:链接先于执行

ESM 加载不像传统脚本即时执行,而是经历三阶段。解析阶段只分析语法,构建模块记录。随后实例化阶段根据 import 引用递归加载依赖模块,为每个模块创建环境记录,并将导入绑定连接到对应导出绑定。最后才执行模块顶层代码。由于 import 的绑定在实例化时已经建立,运行时无论在代码哪里写 import,绑定都已存在,表现出“提升”。

这种提升不同于变量初始化:var 声明会被初始化为 undefined,而 import 绑定只是对导出绑定的实时引用,本身没有默认值。真正影响值可用性的是模块的执行顺序:在没有循环依赖的常规加载中,导出模块会先于导入模块执行完毕,因此导出绑定已完成初始化(未赋值的 let 变量会是 undefined)。但若存在循环依赖,某模块可能在导出绑定初始化之前就被访问,此时会触发暂时性死区(TDZ),抛出 ReferenceError。也就是说,提升只保证绑定存在,不保证值可用——值是否可用取决于导出模块的执行进度,而不是 import 的位置。

具体场景:工具模块与调用顺序

考虑一个简单的配置模块 config.js,它导出常量 export const apiUrl = '...'。在入口 app.js 中,我们在文件第一行使用 console.log(apiUrl),而 import 语句写在最后一行。尽管通常认为 import 应在使用前,由于实例化先执行,输出不会因位置出错。

若把 config.js 改为导出 export let apiUrl; setTimeout(() => { apiUrl = '...' }, 0);,由于 config.js 的顶层代码同步执行完时 apiUrl 仅被声明但尚未被 setTimeout 回调赋值,app.js 顶层立即访问会得到 undefined,不会触发 TDZ。这显示绑定已存在但值尚未就绪。若要获得最终值,需要等待异步回调完成后再访问。若存在循环依赖且导出模块尚未执行到 let apiUrl 的初始化语句,则访问会触发 TDZ。

边界:模块解析失败与 TDZ

提升有一个硬边界:模块必须能成功解析。如果 import 目标路径无效或语法错误,整个模块图不会进入执行阶段,因此任何代码都不会运行。所以不能用条件或动态路径写 import,它是静态的。此外 import 只能出现在顶层,不能嵌套在块或函数中。

若在循环依赖中,实例化顺序可能导致某个绑定在初始化阶段不可用,此时访问会触发 ReferenceError,因为导出模块尚未初始化某些 let 绑定。但这不在题目范围。处理办法是避免在模块顶层读取依赖值,而是通过函数调用等延迟到依赖完成。

回答前,多想一步

容易答错的地方

把 import 提升与 var 提升混淆
一些人认为 import 也会像 var 一样被初始化为 undefined,允许在模块执行早期读取并得到 undefined。实际上 import 绑定是导出绑定的别名,没有自身初始化过程。依赖模块中的代码决定了值何时可读。若导出的是 let x;(未赋值),读取得到 undefined;只有在导出模块尚未执行到该声明(如循环依赖)时,读取才会抛出 ReferenceError。
认为 import 可放在块内
有开发者误以为 import 声明可以像局部变量一样放在 if 或 try 块中。但 ES 模块是静态结构,必须在模块顶层。即使 import 写在块内,解析阶段就会失败。正确做法是用动态 import 来有条件导入。
试着用自己的话回答

面试官还会怎么问?

如果 import 语句写在模块底部,前面就使用导入值,总是安全吗?

在无循环依赖的常规模块加载中,导出模块先于导入模块完成执行,绑定总有值(若为 let 未赋值则为 undefined),不会出现 TDZ。若存在循环依赖,导出方可能尚未初始化对应绑定,此时访问会抛出 ReferenceError(暂时性死区)。为代码清晰起见,仍建议将 import 写在文件顶部,但这是风格建议而非必要性。

import * as ns 与 import { name } 在提升机制上有区别吗?

没有本质区别,两者在实例化阶段都会建立绑定,绑定到 namespace 对象或具体导出。区别在于实时绑定访问方式:具名导入直接绑定到导出变量,namespace 则通过属性反映。

动态 import() 是否也有提升?

没有。动态 import() 返回 Promise,在调用时才开始加载依赖,不属于模块的静态实例化阶段。因此它可以出现在函数或条件等任意位置。动态导入不会预创建绑定。

从一道题,走向一组知识

把知识连起来

模块与语言机制

纯副作用导入 import './module' 有什么用途,它会执行模块代码吗?

同属「模块与语言机制」专题,接着看 ESM 副作用导入 import 模块 在具体场景中的处理方式。

模块与语言机制

如何使用 import 语法进行重命名导入,并且仍然绑定原模块的实时值?

同属「模块与语言机制」专题,接着看 ESM import 重命名导入 实时绑定 在具体场景中的处理方式。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • JavaScript modules

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 机制:链接先于执行
  3. 具体场景:工具模块与调用顺序
  4. 边界:模块解析失败与 TDZ
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑