可维护的JavaScript代码需坚持模块化、单一职责、显式依赖、稳定接口、纯函数优先五大原则:模块聚焦一事,依赖明示可测,导出精简语义清,副作用集中管控,异步操作明确标识。

编写可维护的 JavaScript 代码,核心是让别人(包括未来的你)能快速看懂、安全修改、方便扩展。模块化设计不是加个 export 就完事,而是围绕“职责清晰、边界明确、依赖可控”来组织代码。
单一职责:一个模块只做一件事
每个模块应聚焦解决一个具体问题,比如“格式化日期”“处理本地存储”“发起特定 API 请求”。功能混杂的模块(如既管用户登录又管订单提交)会随业务增长迅速失控。
- 判断标准:模块名能用“动词 + 名词”准确描述,例如
validateEmail、saveToLocalStorage - 拆分信号:函数超过 20 行、参数多于 4 个、出现多个
if/else块处理不同场景,就该考虑拆成独立模块或工具函数 - 避免“工具箱模块”:像
utils.js堆砌几十个不相关函数,不如按领域建dateUtils.js、stringUtils.js
显式依赖:导入即可见,不隐藏调用链
模块对外部能力的使用必须通过 import 明确声明,禁止在内部动态拼接字符串引入、或直接访问全局变量(如 window.fetch 或 localStorage)。
- 好处:便于测试(可轻松 mock 依赖)、重构(删掉未被 import 的模块不会出错)、排查(从入口顺藤摸瓜)
- 实践建议:将浏览器 API 封装成可注入的服务,例如创建
ApiService模块,导出统一的request()方法,内部用fetch;业务模块只依赖ApiService,不直接调用fetch - 警惕“隐式上下文”:避免函数依赖外部状态(如某个全局配置对象),改为通过参数传入或由上层模块注入
稳定接口:导出内容精简且语义明确
模块对外暴露的 API 应尽量少,但每个都需有清晰意图和稳定行为。不要为了“方便”把所有内部函数都 export 出来。
立即学习“Java免费学习笔记(深入)”;
- 只导出使用者真正需要的:例如日期模块导出
formatDate和parseDate,不导出内部用的padZero - 命名体现用途而非实现:用
debounce而非throttleWithTimeout;用getUserProfile而非apiGetV1UserMe - 提供默认导出(default export)用于主功能,命名导出(named export)用于辅助能力,例如:
export default fetchUser; // 主流程
export { validateUserInput, normalizeUserData }; // 可选复用
可预测副作用:纯函数优先,副作用集中管控
模块中尽量让函数无副作用(输入相同,输出确定),把读写 DOM、发请求、改 localStorage 等操作隔离到少数可识别的位置。
- 计算类逻辑(如过滤数组、转换数据结构)写成纯函数,便于单元测试和复用
- 副作用操作统一收口:例如所有 API 调用走
apiClient.js,所有本地缓存操作走cacheManager.js,不散落在各处 - 异步操作明确标记:函数名含
Async(如loadConfigAsync)或返回 Promise,避免调用方误以为是同步执行











