
ES 模块(ESM)不提供类似 CommonJS 的 module 对象来动态挂载属性;试图通过 module.bar = 1 或函数自身属性(如 foo.bar = 1)模拟模块作用域,既不符合规范,也易引发竞态、污染与可维护性问题——正确做法是显式声明模块级变量或使用闭包封装状态。
es 模块(esm)不提供类似 commonjs 的 `module` 对象来动态挂载属性;试图通过 `module.bar = 1` 或函数自身属性(如 `foo.bar = 1`)模拟模块作用域,既不符合规范,也易引发竞态、污染与可维护性问题——正确做法是显式声明模块级变量或使用闭包封装状态。
在 ES 模块环境中,不存在可从函数体内直接访问的 module 绑定。这与 CommonJS 中可通过 module.exports 或 module 对象动态扩展导出的行为有本质区别。ESM 的模块作用域是静态、只读且不可反射的:所有导出必须通过 export 声明提前确定,运行时无法通过类似 module.bar = 1 的方式向模块对象注入属性。
❌ 错误示例:混淆作用域与对象
以下写法不仅无效,而且具有误导性和潜在风险:
// ❌ 语法错误:module 在 ESM 中未定义
export function foo() {
module.bar = 1; // ReferenceError: module is not defined
}
// ❌ 危险变通:滥用函数自身属性(尤其在异步场景下)
export async function foo() {
foo.bar = 1; // 不安全!foo 可能被重命名、覆盖或多次导入(不同绑定)
}
foo.bar = 1 表面看似“模拟模块状态”,实则将状态耦合到函数标识符上。一旦该函数被解构、重命名或通过 import * as ns 引入,foo.bar 将丢失;若模块被多次导入(如通过不同路径),还可能因模块实例化差异导致状态不一致。
✅ 推荐方案:显式、可控、可维护
1. 模块级变量(最简洁直接)
适用于简单、同步、共享的状态:
// utils.js
let bar = 1;
export function foo() {
console.log(bar); // ✅ 正确访问模块私有状态
}
export function setBar(value) {
bar = value;
}
export const getBar = () => bar;
2. 闭包封装(推荐用于复杂逻辑或需初始化的状态)
增强封装性与防篡改能力:
// counter.js
const createModuleState = () => {
let count = 0;
return {
increment: () => ++count,
reset: () => { count = 0; },
getCount: () => count
};
};
const state = createModuleState();
export const increment = () => state.increment();
export const reset = () => state.reset();
export const getCount = () => state.getCount();
3. 类封装(适合需要多状态、生命周期或类型约束的场景)
提升可测试性与扩展性:
// config.js
class ModuleConfig {
#data = { bar: 1 };
get bar() { return this.#data.bar; }
set bar(value) { this.#data.bar = value; }
static #instance = new ModuleConfig();
static get instance() { return this.#instance; }
}
export const foo = () => {
console.log(ModuleConfig.instance.bar);
};
⚠️ 关键注意事项
- 避免全局污染:globalThis(或 global)是全局作用域,与模块无关,不应作为模块状态的替代方案。
- 警惕异步竞态:若状态被多个异步调用修改(如并发 await foo()),需配合 Mutex 或 Promise 队列等机制保障一致性。
- Tree-shaking 友好性:模块级 const/let 变量在静态分析中更易被优化;而函数属性方式会阻碍摇树优化。
- 调试与可观测性:显式变量名(如 bar)比 foo.bar 更易被 DevTools 检查、断点和 IDE 自动补全支持。
总结
ESM 的设计哲学强调显式性、静态性与确定性。放弃对 module 对象的执念,转而采用声明式变量、闭包或类来组织模块状态,不仅能确保代码符合标准、行为可预测,更能提升协作效率与长期可维护性。记住:好的模块设计,从来不是“绕过限制”,而是“用对工具”。










