导入同一个JS文件后,修改其中一个模块的数据会不会影响另一个导入的同一个JS模块的数据

54 阅读3分钟

1. ES模块

ES6模块是单例的,也就是说,无论你在代码中导入同一个模块多少次,实际上只会执行一次模块代码,然后所有的导入都会引用同一个实例。所以,如果模块导出的是一个对象或者数组这样的可变数据类型,任何地方对它的修改都会影响到所有导入该模块的地方。但如果导出的是原始类型,比如字符串或数字,因为它们是不可变的,修改的话可能需要重新赋值,这时候其他模块可能不会自动看到变化,除非使用响应式系统如Vue或React的状态管理。

在JavaScript的ES6模块系统中,当一个模块被多次import时,模块只会执行一次,所有import都会引用同一个模块实例。这意味着:

module.js
Apply
// 假设这是被import的模块
export const mutableObject = { count: 1 }; // 导出可变对象
export let primitiveValue = 10;           // 导出原始值

// ...其他代码...

主要分为两种情况:

  1. 修改导出对象的属性(会相互影响):
javascript
Apply
// 模块A
import { mutableObject } from './module.js';
mutableObject.count = 2;

// 模块B
import { mutableObject } from './module.js';
console.log(mutableObject.count); // 也会变成2
  1. 重新赋值导出变量(不会影响):
javascript
Apply
// 模块C
import { primitiveValue } from './module.js';
primitiveValue = 20;  // 会报错(ES6的const/let导出不能直接重新赋值)

// 正确做法应该通过修改对象属性,或使用可变容器对象

在项目中需要注意:

  1. 如果被import的js导出的是对象/数组等引用类型,修改其属性会影响所有import的地方
  2. 如果导出的是基础类型(number/string等),想要修改需要配合响应式系统(如Vue的ref/reactive)
  3. 推荐使用const导出不可变数据,用函数封装可变操作

2. CommonJS模块

首先,CommonJS的模块是动态加载的,每次require都会执行模块代码,但Node.js会缓存模块,所以多次require同一个模块只会执行一次,后续require返回缓存的结果。这意味着导出的对象是单例的,任何修改都会影响所有导入的地方。

不过,CommonJS导出的是值的拷贝,对于原始类型,导出的是值的副本,所以修改原始类型不会影响其他模块。但如果导出的是对象,因为拷贝的是引用,修改对象属性会影响所有使用该模块的地方。

在CommonJS模块系统中,多次require同一个模块时的行为有所不同:

  1. 模块缓存机制
    所有模块在第一次加载时会被缓存,后续require调用都返回同一个导出对象的引用
  2. 值拷贝与引用差异
commonjs-module.js
Apply
// 原始类型会被拷贝
exports.primitive = 100 

// 对象类型共享引用
exports.mutableObject = { count: 1 }
  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 ModulesCommonJS
加载方式静态动态
导出类型实时绑定值拷贝/对象引用
循环依赖处理安全可能产生未完成引用
顶层this指向undefinedmodule.exports对象

对于项目的启示:

  1. 使用对象作为模块状态容器时,两种模块系统都会产生跨模块影响
  2. 修改原始类型时,CommonJS比ES Modules更安全(不会报错)
  3. 推荐在CommonJS中通过冻结对象避免意外修改:
javascript
Apply
// 安全写法
exports.config = Object.freeze({
  apiUrl: 'https://api.example.com'
})