es modules 的导出绑定天然是只读的,因 export 创建的是实时绑定而非值拷贝,导入后对绑定标识符赋值会抛 typeerror;只读限制不阻止修改对象内部状态,需用 object.freeze 或私有字段等手段进一步保护。

ES Modules 中的导出绑定天然是只读的——你无法在导入方重新赋值或重新声明同名绑定,这是语言规范强制保证的行为,不需要额外实现。
为什么导出绑定默认就是只读的
ESM 的 export 创建的是**实时绑定(live binding)**,而非值的拷贝。这个绑定被设计为只读:导入模块中对导入名称的赋值(=)、递增(++)、解构重赋值等操作都会抛出 TypeError(在严格模式下,而 ESM 全局自动启用严格模式)。
例如:
// math.js export let count = 0; export const PI = 3.14;
// main.js
import { count, PI } from './math.js';
<p>count = 1; // ❌ TypeError: Assignment to constant variable.(若 export const)或
// ❌ TypeError: Cannot assign to read only property 'count' of object '#<object>'(若 export let)</object></p><p>PI = 3.1415; // ❌ 同样报错
</p>
关键点:只读的是“绑定”,不是“值”本身
只读限制仅作用于导入后的标识符绑定,不阻止修改可变对象的内部状态:
-
导出一个对象/数组,你可以修改它的属性或元素:
export const config = { theme: 'dark' };→ 导入后config.theme = 'light'✅ 允许 - 导出一个函数,你可以调用它,甚至让它内部改变闭包变量
-
但你不能让导入名指向新对象:
config = { theme: 'light' }❌ 报错
如何真正保护内部状态(进阶控制)
如果目标是防止导入方意外修改数据结构,需在导出端主动封装:
- 用
Object.freeze()冻结对象(浅冻结):export const config = Object.freeze({ theme: 'dark', fontSize: 14 }); - 用
export default配合私有变量 + getter-only 接口:const _state = { count: 0 };<br> export default { get count() { return _state.count; } }; - 使用
class并省略 setter,或把字段设为#private:
export class Counter {
#value = 0;
get value() { return this.#value; }
increment() { this.#value++; }
}
注意:动态导出(export *)和命名空间导入也遵循只读规则
import * as ns from './mod.js' 后,ns.x = ... 同样非法;export * from './other.js' 会透传原始模块的只读绑定,不会解除限制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











