es module 的 export 建立静态绑定而非值拷贝,仅限顶层声明六类标识符,导出为只读实时引用,值延迟至模块执行时初始化,默认导出是标记特殊入口的语法糖。

ES Module 的 export 声明机制不是简单地“把值拷出去”,而是在模块实例化阶段建立**静态绑定(live binding)**,并在模块执行上下文的环境记录中完成标识符与导出条目的映射。这个过程发生在代码运行前,由引擎依据 import 依赖图驱动,具有确定性、不可变性和延迟求值特性。
export 只能导出顶层声明的标识符
export 不是运行时语句,不能出现在函数、条件块或循环中,仅允许在模块顶层使用。它能导出的只有六类语法声明所创建的标识符:
-
const/let/var声明的变量(含初始化表达式) - 函数声明(
function foo() {})——必须有名字 - 类声明(
class Bar {})——同样必须具名 - 命名导出列表(
export { a, b as c })——本质是对已有声明的引用重映射 - 默认导出(
export default function() {}或export default obj)——不声明绑定名,但会生成一个隐式名称default - 重导出(
export { x } from './other')——不引入本地绑定,直接转发其他模块的导出项
导出绑定是只读、实时、非拷贝的
导出不是复制值,而是创建对原始绑定的**只读引用**。例如:
let count = 0;
export { count };
setTimeout(() => count++, 100);
其他模块通过 import { count } from './mod' 获取的始终是同一内存位置的当前值,修改 count 会影响所有导入方——这是 live binding 的体现。但导入方无法重新赋值(报错 Assignment to const),因为绑定是只读的。
模块环境记录决定导出何时真正初始化
虽然 export 语句在解析期就注册了导出项,但对应值的计算被推迟到模块执行阶段:
-
export const bigData = new ArrayBuffer(1GB);—— 内存分配发生在模块首次执行,而非加载或链接时 -
export let x = sideEffect();——sideEffect()在模块求值时才调用 - 未被任何 import 引用的模块,其导出绑定不会触发求值(Tree-shaking 基础)
这种延迟初始化由模块环境记录统一管理,确保导出值与其声明作用域保持一致,且不受外部干扰。
default 导出是一种特殊语法糖
export default 并不声明名为 default 的变量,而是在模块记录中设置一个特殊的 default 导出入口。它的等效形式是:
// export default 42 export var __default__ = 42; // 然后模块记录标记 __default__ 为 default 导出项
因此,import x from './m' 实际是解构该模块的 default 入口,x 可以是任意合法标识符,与源码中是否出现 default 名字无关。











