面试官 : “ 说一下 ES6 模块与 CommonJS 模块的差异 ? ”

先甩结论 :

它们有三个重大差异。

  • CommonJS 模块输出的是一个 值的拷贝 ,ES6 模块输出的是 值的引用
  • CommonJS 模块是 运行时 加载,ES6 模块是 编译时 输出接口。
  • CommonJS 模块的require()同步 加载模块,ES6 模块的import命令是 异步 加载,有一个独立的模块依赖的解析阶段。

一、核心定位与运行环境

  • CommonJS(CJS) :Node.js 早期的模块化规范,为服务端设计(同步加载),也被 Browserify 等工具适配到浏览器端。
  • ES6 模块(ESM) :ES6 官方定义的模块化规范,适配浏览器 + 服务端,是前端模块化的标准方案,Node.js v14.13+ 也全面支持。

二、差异对比表

对比维度 CommonJS 模块 ES6 模块
加载时机 运行时加载(动态):模块代码在 require 执行时才加载、执行 编译时加载(静态):编译阶段就确定模块依赖关系,提前解析导入导出
导入导出语法 导出:module.exports / exports;导入:require() 导出:export / export default;导入:import / import()
值的绑定方式 赋值传递:导入的是值的拷贝,模块内部值变化不影响导入值 引用传递:导入的是值的实时引用,模块内部值变化会同步到导入端
执行机制 模块代码同步执行,加载完成后才继续执行后续代码 模块代码异步执行(浏览器端),Node.js 中可同步 / 异步
顶层 this 指向 指向当前模块的 module.exports 对象 指向 undefined
是否支持按需导入 不支持(静态分析无法识别 require 动态路径) 支持(import() 动态导入、import { a } from 'xxx' 按需导入)
循环依赖处理 加载时生成未完成的模块对象,后续补全(可能拿到空对象) 编译时确定依赖关系,通过 "暂时性死区" 处理,更稳定
文件后缀 默认 .js,Node.js 中可省略后缀 浏览器端需 .mjs 或配置 type="module";Node.js 需 .mjspackage.json"type": "module"

三、关键差异详解(附代码示例)

1. 语法差异(最直观)

CommonJS 示例

javascript 复制代码
// 导出(module.js)
// 方式1:整体导出
module.exports = {
  name: 'CommonJS',
  sayHi: () => console.log('hi')
};
// 方式2:单个导出
exports.age = 18; // 等价于 module.exports.age = 18

// 导入(index.js)
const mod = require('./module.js');
console.log(mod.name); // CommonJS
console.log(mod.age); // 18

ES6 模块示例

javascript 复制代码
// 导出(module.js)
// 方式1:命名导出
export const name = 'ES6 Module';
export const age = 18;
// 方式2:默认导出
export default {
  sayHi: () => console.log('hi')
};

// 导入(index.js)
// 命名导入 + 默认导入
import { name, age } from './module.js';
import defaultMod from './module.js';
console.log(name); // ES6 Module
defaultMod.sayHi(); // hi

2. 值绑定方式差异(核心区别)

CommonJS(值拷贝)

javascript 复制代码
// module.js(CJS)
let count = 0;
setTimeout(() => count = 10, 1000);
module.exports = { count };

// index.js(CJS)
const { count } = require('./module.js');
console.log(count); // 0(初始值拷贝)
setTimeout(() => console.log(count), 1000); // 仍为 0(拷贝值不更新)

ES6 模块(引用传递)

javascript 复制代码
// module.js(ESM)
export let count = 0;
setTimeout(() => count = 10, 1000);

// index.js(ESM)
import { count } from './module.js';
console.log(count); // 0(初始值)
setTimeout(() => console.log(count), 1000); // 10(实时引用更新)

3. 加载时机差异

  • CommonJS:require('./a.js') 执行时才会读取 a.js 文件、执行代码、生成导出对象,支持动态路径(如 require('./' + fileName))。
  • ES6 模块:import 必须写在顶层(不能在 if 里),编译阶段就确定所有导入,不支持动态路径(需用 import('./a.js') 动态导入,返回 Promise)。

四、实战注意事项

  1. Node.js 中混用两种模块

    • 后缀 .cjs 强制为 CommonJS,.mjs 强制为 ES6 模块;
    • package.json 中设置 "type": "module",则 .js 文件默认是 ES6 模块;设为 "commonjs" 则默认是 CJS。
  2. 浏览器端使用 ES6 模块

    xml 复制代码
    <!-- 必须加 type="module" -->
    <script type="module" src="./index.js"></script>
  3. 循环依赖处理:ES6 模块的循环依赖更可靠,CommonJS 需注意模块加载顺序,避免拿到空对象。

总结

  1. 核心区别:CJS 是运行时动态加载(值拷贝),ESM 是编译时静态加载(值引用);
  2. 语法区别 :CJS 用 require/module.exports,ESM 用 import/export
  3. 使用场景:Node.js 老项目多用 CJS,现代前端 / 新版 Node.js 优先用 ESM(标准化、支持按需加载)。
相关推荐
学嵌入式的小杨同学5 小时前
从零打造 Linux 终端 MP3 播放器!用 C 语言实现音乐自由
linux·c语言·开发语言·前端·vscode·ci/cd·vim
芝士爱知识a5 小时前
2026年AI面试软件推荐
人工智能·面试·职场和发展·大模型·ai教育·考公·智蛙面试
weixin_425543735 小时前
TRAE CN3.3.25 构建的Electron简易DEMO应用
前端·typescript·electron·vite·nestjs
Mr Xu_6 小时前
【Vue3 + ECharts 实战】正确使用 showLoading、resize 与 dispose 避免内存泄漏
前端·信息可视化·vue·echarts
0思必得06 小时前
[Web自动化] Selenium设置相关执行文件路径
前端·爬虫·python·selenium·自动化
石去皿6 小时前
大模型面试通关指南:28道高频考题深度解析与实战要点
人工智能·python·面试·职场和发展
雯0609~6 小时前
hiprint:实现项目部署与打印1-官网提供普通html版本
前端·html
yuezhilangniao6 小时前
AI智能体全栈开发工程化规范 备忘 ~ fastAPI+Next.js
javascript·人工智能·fastapi
C雨后彩虹6 小时前
CAS与其他并发方案的对比及面试常见问题
java·面试·cas·同步·异步·
美团程序员7 小时前
80道经典常见测试面试题
软件测试·面试·职场和发展·软件测试面试