为何通过import * as导入模块后可修改const导出值?
为什么修改ES模块对象的属性会改变原const导出的值,且Chrome控制台显示有差异?
这是个非常典型的ES模块绑定特性问题,咱们一步步拆解来搞明白:
一、为什么修改constants.APPLE会影响导入的APPLE?
ES模块的导出机制和CommonJS完全不同,核心在于它是绑定关系,而非值拷贝:
- 当你在
constants.js里写export const APPLE = 'apple'时,这个APPLE导出的不是一个固定的值,而是一个和模块内部变量绑定的引用。 - 用
import { APPLE } from './constants.js'导入时,你拿到的APPLE并不是导入时刻的快照,而是和模块内部原变量实时联动的绑定。 - 而
import * as constants得到的模块对象,它的所有属性都是这些绑定的“代理入口”。当你执行constants.APPLE = 'banana'时,本质是通过模块系统的底层机制修改了原模块中绑定的变量值——这里绕开了const的声明限制,因为你不是直接在原模块里重新赋值const APPLE,而是通过模块对象的属性操作修改了绑定的实际值。
直接修改APPLE = 'banana'会报错,是因为这个变量在当前模块的作用域里是const声明的,语法层面不允许重新赋值;但通过模块对象修改的是绑定的底层值,不属于当前作用域的const限制范畴。
二、为什么Chrome控制台的显示会有差异?
Chrome DevTools对模块对象的处理有个特殊的惰性求值机制:
- 当你
console.log(constants)时,控制台会先打印一个对象的“快照字符串”,这个字符串是当时对象的表层状态。 - 但模块对象的属性是动态绑定的,当你点击展开这个对象查看
APPLE属性时,控制台会实时重新读取绑定的当前值,这就导致展开后的属性值和初始打印的表层字符串不一致。 - 简单说就是:初始打印的是“静态快照”,展开时才会获取“动态实时值”,所以看起来和
console.log(APPLE)的直接输出有差异。
最后提醒
这种修改模块绑定值的操作属于非常规手段,严重违背了ES模块设计中“导出变量应保持不可变性”的初衷,会导致代码的可维护性急剧下降——所有导入这个变量的模块都会被意外影响,排查问题时会非常头疼,强烈不推荐使用!
内容的提问来源于stack exchange,提问作者Sumire
相关产品推荐
相关产品推荐

