commonjs的“值拷贝”实为对module.exports属性的一次性快照赋值:基本类型导出后完全隔离,引用类型则共享内存地址但仅原地修改生效,本质是导出时刻的引用快照而非实时绑定。

CommonJS 的「值拷贝」不是字面意义的深拷贝,而是指 require 时对 module.exports 对象属性的一次性快照式赋值。它对后续修改的影响,取决于导出值的类型和修改方式。
基本类型导出后彻底断开
数字、字符串、布尔值等原始类型,在 require 时被复制一份独立值。模块内部再改,导入方完全感知不到:
-
index.js:
let count = 1; module.exports = { count }; -
main.js:
const { count } = require('./index'); console.log(count); // 1;之后在 index.js 中count = 2,main.js 里的count仍是1
引用类型导出的是共享地址,但仅限原地操作生效
数组、对象、函数等导出的是内存地址的引用。导入方拿到的,是同一块内存的“钥匙”——只要不换锁,开门看到的内容变,双方都可见:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
-
index.js:
let list = []; module.exports = { list }; -
main.js:
const { list } = require('./index'); - 在 index.js 中调用
list.push('a')→ main.js 的list长度立刻变成1 - 但在 index.js 中写
list = ['b']→ main.js 的list仍指向原来的空数组,长度还是0
本质是「导出那一刻的引用快照」,不是实时绑定
module.exports 赋值那一行,决定了导出什么。之后你把内部变量重新赋值给别的对象,不会自动更新已导出的字段:
- 导出语句
module.exports = { list }相当于固定绑定了当时list所指的数组实例 - 后续
list = []只是让list这个局部变量指向新数组,老数组和它的引用关系没变 - importedList 和 exported.list 始终指向同一个初始数组,除非你主动改那个数组的内容(如 push、splice、obj.x =)
避免陷阱的实用做法
不要指望靠重赋值同步状态。推荐以下更可控的方式:
- 导出方法而非裸变量:
module.exports = { getList: () => list, add: (item) => list.push(item) } - 用 getter 封装读取:
Object.defineProperty(module.exports, 'list', { get() { return list; } }) - 状态统一由单例类管理,导出实例而非原始数据
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










