JavaScript模块是语言级机制,需显式声明type="module"或满足Node.js条件;export/import非语法糖,有命名/默认导出之分;路径须带扩展名或为绝对/相对URL;动态import()返回Promise;循环依赖时未执行完的导出为undefined。

JavaScript 模块就是把代码按功能拆成独立、可复用的文件,每个文件默认拥有自己的作用域,import 和 export 是声明依赖与暴露接口的唯一标准方式——不是语法糖,是语言级机制,不加 type="module" 或没配对的 export/import 会直接报错。
模块文件必须显式声明为 module 才生效
浏览器中, 标签默认是传统脚本,import/export 会报 Uncaught SyntaxError: Cannot use import statement outside a module。必须写成:
Node.js 中则需满足任一条件:.mjs 后缀、package.json 中设置 "type": "module",或用 node --input-type=module 运行字符串。混用 require() 和 import 会导致 ERR_REQUIRE_ESM 错误。
export 有命名导出和默认导出两种形式
命名导出适合暴露多个值(函数、类、常量),导入时名称必须一致;默认导出只能有一个,导入时可自定义名称。两者不能混在同一个 export 语句里。
立即学习“Java免费学习笔记(深入)”;
- 命名导出:
export const PI = 3.14;、export function add(a, b) { return a + b; } - 默认导出:
export default function multiply(a, b) { return a * b; }(注意:不能用function multiply() {}单独声明再export default multiply,除非是变量赋值形式) - 批量重导出:
export { add as sum, PI } from './math.js';,但不会提升原始模块的执行时机
import 路径必须带扩展名或为绝对/相对 URL
ES 模块的 import 不支持 Node.js 那样的自动扩展名解析(如省略 .js)。以下写法在浏览器中全部非法:
import { add } from './math'; // ❌ 缺少扩展名
import { add } from 'lodash'; // ❌ 不是相对/绝对路径,也不是 bare specifier(需构建工具或导入映射支持)合法写法包括:
import { add } from './math.js';import utils from './utils.mjs';-
import { render } from 'https://cdn.skypack.dev/react@18';(需环境支持 bare specifier)
动态 import() 是函数调用,返回 Promise,可传入变量路径,且不受静态分析限制,但无法在顶层用 await 替代(需包在 async 函数内)。
循环依赖时模块只执行一次,未执行完的部分是 undefined
如果 a.js 导入 b.js,而 b.js 又导入 a.js,V8 会缓存已初始化但未执行完的模块对象。此时 b.js 中访问 a.js 的命名导出,可能得到 undefined,尤其是导出的是 let 或 const 声明的值(尚未初始化)。
规避方法:
- 把相互依赖的逻辑提到第三个模块中统一导出
- 用默认导出函数代替直接导出变量(函数体延迟执行)
- 避免在模块顶层直接读取对方导出的非函数值
模块加载顺序由依赖图决定,不是文件书写顺序;import 语句位置不影响执行时机——所有 import 都在模块执行前完成解析和预加载。











