1. ES模块
ES6模块是单例的,也就是说,无论你在代码中导入同一个模块多少次,实际上只会执行一次模块代码,然后所有的导入都会引用同一个实例。所以,如果模块导出的是一个对象或者数组这样的可变数据类型,任何地方对它的修改都会影响到所有导入该模块的地方。但如果导出的是原始类型,比如字符串或数字,因为它们是不可变的,修改的话可能需要重新赋值,这时候其他模块可能不会自动看到变化,除非使用响应式系统如Vue或React的状态管理。
在JavaScript的ES6模块系统中,当一个模块被多次import时,模块只会执行一次,所有import都会引用同一个模块实例。这意味着:
module.js
Apply
// 假设这是被import的模块
export const mutableObject = { count: 1 }; // 导出可变对象
export let primitiveValue = 10; // 导出原始值
// ...其他代码...
主要分为两种情况:
- 修改导出对象的属性(会相互影响):
javascript
Apply
// 模块A
import { mutableObject } from './module.js';
mutableObject.count = 2;
// 模块B
import { mutableObject } from './module.js';
console.log(mutableObject.count); // 也会变成2
- 重新赋值导出变量(不会影响):
javascript
Apply
// 模块C
import { primitiveValue } from './module.js';
primitiveValue = 20; // 会报错(ES6的const/let导出不能直接重新赋值)
// 正确做法应该通过修改对象属性,或使用可变容器对象
在项目中需要注意:
- 如果被import的js导出的是对象/数组等引用类型,修改其属性会影响所有import的地方
- 如果导出的是基础类型(number/string等),想要修改需要配合响应式系统(如Vue的ref/reactive)
- 推荐使用
const
导出不可变数据,用函数封装可变操作
2. CommonJS模块
首先,CommonJS的模块是动态加载的,每次require都会执行模块代码,但Node.js会缓存模块,所以多次require同一个模块只会执行一次,后续require返回缓存的结果。这意味着导出的对象是单例的,任何修改都会影响所有导入的地方。
不过,CommonJS导出的是值的拷贝,对于原始类型,导出的是值的副本,所以修改原始类型不会影响其他模块。但如果导出的是对象,因为拷贝的是引用,修改对象属性会影响所有使用该模块的地方。
在CommonJS模块系统中,多次require
同一个模块时的行为有所不同:
- 模块缓存机制
所有模块在第一次加载时会被缓存,后续require调用都返回同一个导出对象的引用 - 值拷贝与引用差异
commonjs-module.js
Apply
// 原始类型会被拷贝
exports.primitive = 100
// 对象类型共享引用
exports.mutableObject = { count: 1 }
- 修改行为示例:
javascript
Apply
// 模块A
const mod = require('./commonjs-module')
mod.primitive = 200 // 不影响其他模块
mod.mutableObject.count++ // 会影响所有模块
// 模块B
const mod = require('./commonjs-module')
console.log(mod.primitive) // 仍然显示100
console.log(mod.mutableObject) // 显示 { count: 2 }
关键差异总结表:
特性 | ES Modules | CommonJS |
---|---|---|
加载方式 | 静态 | 动态 |
导出类型 | 实时绑定 | 值拷贝/对象引用 |
循环依赖处理 | 安全 | 可能产生未完成引用 |
顶层this指向 | undefined | module.exports对象 |
对于项目的启示:
- 使用对象作为模块状态容器时,两种模块系统都会产生跨模块影响
- 修改原始类型时,CommonJS比ES Modules更安全(不会报错)
- 推荐在CommonJS中通过冻结对象避免意外修改:
javascript
Apply
// 安全写法
exports.config = Object.freeze({
apiUrl: 'https://api.example.com'
})