es6模块导出对象时导出的是活引用而非深拷贝,导入方与导出方共享同一内存地址,属性修改实时可见,但不可重新赋值变量;原型链完整保留,instanceof等判断依然有效。

ES6 模块中导出对象时,导出的是该对象的活引用(live reference),不是深拷贝或快照。这意味着模块内部对象的属性变化,会在其他导入该对象的模块中实时可见;但原型链本身不会因导出/导入行为被破坏或重置。
对象导出本质是导出变量绑定,不是值拷贝
当你用 export const obj = { x: 1 } 或 export { obj } 导出一个对象,模块系统导出的是对这个变量内存地址的绑定。所有导入方拿到的都是同一份引用。
- 修改导出对象的属性(如
obj.x = 2),所有导入者都能立刻读到新值 - 但不能在导入端重新赋值整个变量(
obj = { y: 3 })——这只会改变本地绑定,不影响原始导出源 - 若导出的是
export default { x: 1 },那它仍是单个对象的引用,只是通过默认导入方式获取
原型链在导出导入过程中保持完整
对象的 __proto__ 或通过 class / Object.setPrototypeOf() 设置的原型关系,在模块间传递时完全保留。ES6 模块不干预对象的内部结构,只负责引用传递。
- 导出类实例、继承自某个父类的对象,其
instanceof判断在导入模块中依然有效 - 如果导出的是
class Person {...},导入后new Person()创建的实例,原型链指向原模块中的Person.prototype - 即使跨多个模块层层导入(A → B → C),只要没手动替换原型,原始原型链始终贯通
注意:默认导出对象字面量 ≠ 命名导出对象
虽然两者都传递引用,但语义和使用方式不同:
-
export default { a: 1 }导出的是一个匿名对象,导入时可任意命名:import cfg from './config.js' -
const conf = { a: 1 }; export { conf }是命名导出,导入必须用大括号:import { conf } from './config.js' - 两者都维持引用,但默认导出更易被误认为“值”,实际仍是同一对象引用
避免意外共享状态的实用建议
正因为对象是引用传递,多人协作或复杂依赖下容易引发隐式状态污染:
- 配置对象尽量用
export default Object.freeze({ ... })防止运行时篡改 - 需要可变状态时,明确封装为类或工厂函数,而非直接导出裸对象
- 若需隔离副本,导入后手动浅拷贝:
const localCopy = { ...sharedObj }(注意仅限一层) - 构建工具(如 Vite、Webpack)在生产环境下可能做 Tree-shaking 或常量内联,但原型和引用关系仍按规范保留











