默认导出是es6模块中每个模块仅允许一个的主出口声明,用于导出函数、类、对象或基本值,导入时可自定义名称且无需大括号;它不触发执行,仅静态指定核心导出项。

ES6 模块的默认导出机制不是“自动执行”或“隐式调用”,而是一种静态声明语法,用于指定模块对外暴露的主值。它不创建执行上下文,也不触发运行时逻辑;它只是告诉导入方:“这个模块最核心的东西是它”。理解这一点,就能避开常见误解。
默认导出的本质:一个模块一个主出口
每个 ES6 模块最多只能有一个 export default,这是语法硬性限制。它导出的可以是函数、类、对象、基本值,甚至是一个立即执行的表达式结果(但不是“执行上下文”本身):
-
export default function() { return 'hello'; }→ 导出函数定义,不执行 -
export default { version: '1.0' };→ 导出字面量对象 -
export default (function() { console.log('init'); return 42; })();→ 表达式在模块求值时执行一次,导出的是42,不是函数体
关键点:模块脚本本身会在首次被导入时同步求值一次(即执行顶层代码),但这属于模块加载过程,与 export default 语法无关——无论有没有默认导出,模块都会完整执行一遍。
导入时的名称自由与结构约束
默认导出的导入语法允许你任意命名,但必须使用“无大括号”形式:
-
import utils from './utils.js'✅ 合法:接收为变量utils -
import { utils } from './utils.js'❌ 错误:这是按需导入写法,会报“未找到命名导出 utils” -
import { default as utils } from './utils.js'✅ 合法但冗余:等价于上一条,仅在需要混用命名导出时才显式写出
如果你试图用解构方式从默认导入结果中取属性(如 import obj from './mod.js'; const { a } = obj;),那前提是导出的是一个对象——这取决于你 export default 的内容,而非语法本身赋予的结构。
默认导出 + 命名导出共存的实用场景
虽然混合导出不被强烈推荐,但在真实项目中很常见,比如工具库既提供主构造器,又附带辅助函数:
- 导出端:
export default class ApiClient { ... }+export const VERSION = '2.1'; export function retry(fn) { ... } - 导入端:
import ApiClient, { VERSION, retry } from './api.js'
此时模块求值仍只发生一次,VERSION 和 retry 是在该次求值中定义并导出的;ApiClient 类定义也在此刻完成,但类本身不会被实例化——除非你在模块顶层写了 new ApiClient(),那才是执行逻辑,和默认导出无关。
常见误区澄清
✘ “默认导出会自动初始化模块状态”
→ 模块状态初始化靠的是顶层语句(如 let count = 0; 或 init();),不是 export default 带来的。
✘ “导入默认值会重新执行模块”
→ ES6 模块是单例缓存的。无论多少个地方 import x from './mod',模块代码只执行一次,后续导入都复用首次求值后的导出值。
✘ “默认导出能绕过严格模式”
→ 所有 ES6 模块自动启用严格模式,export default 不改变这一点。











