模块顶层必须用const或let声明变量,禁用var;导出优先用const,必要时用let并封装为函数;避免跨模块共享可变变量,动态导入应限制在最窄作用域内。

在模块化开发中,变量声明不是语法练习,而是代码组织和依赖管理的起点。用错声明方式,轻则变量污染、值不可预期,重则模块间隐式耦合、热更新失效。
模块顶层必须用 const 或 let,禁用 var
ES6 模块(.mjs 或 import/export 语法)的顶层作用域是模块作用域,不是全局作用域。var 声明会意外泄漏到全局,破坏模块隔离性。
-
var count = 0→ 在模块里仍可能被其他模块通过window.count访问(浏览器环境),属于隐式全局污染 -
const API_BASE = 'https://api.example.com'→ 安全绑定,不可重赋值,语义清晰 -
let currentUser = null→ 明确表示该值后续会被初始化或更新,且仅限本模块内可见
导出变量优先用 const,只在必要时用 let
模块对外暴露的接口应尽量稳定。导出 const 表示“这个值你可放心使用,不会变”;导出 let 则暗示“它可能被本模块内部修改”,调用方需留意状态变化。
- ✅ 推荐:
export const DEFAULT_TIMEOUT = 5000、export const userSchema = { name: String } - ⚠️ 谨慎:
export let retryCount = 0—— 若必须导出可变状态,建议改用函数封装(如export function incRetry() { retryCount++ }) - ❌ 避免:
export var config = {...}—— var 不支持模块级块作用域,且重复导出无报错,易埋雷
避免跨模块共享可变变量,用函数或类封装状态
直接导出一个 let 变量,让多个模块去读写它,等于放弃状态控制权。模块化的核心是“明确输入输出”,不是“共享内存”。
- ❌ 危险模式:
export let sharedFlag = false,A 模块设为 true,B 模块读取后忘记重置,逻辑错乱 - ✅ 健康模式:
export const createCounter = () => { let count = 0; return { inc: () => ++count, get: () => count } } - ? 补充:若需响应式共享(如状态管理),应走专门机制(
store、Context、Observable),而非裸变量
动态导入与局部声明要匹配作用域需求
使用 import() 加载模块时,其返回的 Promise 解析值应在最窄作用域内使用,避免提升到外层污染。
- 不要:
let utils; import('./utils.js').then(m => utils = m)—— utils 成为模块顶层 let,违背按需原则 - 应该:
button.addEventListener('click', async () => { const { formatDate } = await import('./date-utils.js'); console.log(formatDate(new Date())); }); - 说明:内部声明 + 立即使用,既不污染作用域,又利于 tree-shaking 和懒加载
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











