函数命名应体现职责而非实现方式,如fetchUserProfile;组件化需隔离副作用与状态边界;复用性差常因参数耦合,宜用配置对象;模块化重在明确依赖流向与边界。

函数命名必须体现职责,而非实现方式
很多人写 getDataFromApi 这类名字,看似清晰,实则把调用方式锁死了——万一后续改成从 localStorage 读取,函数名就失效了。更合理的命名是 fetchUserProfile 或 loadCartItems,聚焦“做什么”,不暴露“怎么做”。
这类函数应满足:单一输入输出、无副作用、可预测。例如:
function validateEmail(email) { return /^[^s@]+@[^s@]+.[^s@]+$/.test(email); }
组件化不是套框架,而是隔离副作用与状态边界
哪怕不用 react/vue,纯 js 也能做组件化。关键在于把 dom 操作、事件绑定、数据更新三者拆开,用函数封装边界。比如一个下拉搜索框,不要写成“先查 DOM、再绑事件、再发请求”的大块逻辑。
推荐结构:
立即学习“Java免费学习笔记(深入)”;
function createSearchBox({ inputEl, listEl, onSearch }) { const debouncedSearch = debounce((q) => onSearch(q), 300); inputEl.addEventListener('input', (e) => { debouncedSearch(e.target.value); }); return { updateResults(items) { listEl.innerHTML = items.map(i => `${i.label} `).join(''); }, clear() { inputEl.value = ''; listEl.innerhtml = ''; } }; }
-
createSearchBox只负责初始化和事件绑定,不处理请求或渲染细节 -
onSearch是回调,由上层决定是调 API 还是查本地索引 - 返回的对象提供明确的控制接口,避免直接操作 DOM 的散点调用
复用性差往往因为参数耦合太紧
常见反模式:renderTable(data, columns, container, theme, loadingText) —— 参数超过 4 个,调用时极易出错,且每次新增需求都要改函数签名。
改用配置对象 + 默认值:
function renderTable(data, options = {}) { const { columns = [], container = document.body, theme = 'light', emptyMessage = 'No data', loading = false } = options; // ... }
- 新增字段不影响旧调用,比如以后加
sortable: true不会破坏现有代码 - 必填项少于 2 个时才考虑位置参数,其余一律进
options - 避免在函数内部判断
typeof options === 'String'来兼容老写法——这会让类型推导和 ide 提示失效
模块化不是文件拆分,而是明确依赖流向
把一堆函数塞进 utils.js 并不等于模块化。真正的问题是:谁依赖谁?能否单独测试?是否容易被误用?
例如日期处理,别写一个巨无霸 dateUtils.js,而是按能力切分:
-
parseDate(str):只负责字符串 →Date,不校验也不格式化 -
formatDate(date, pattern):只接受Date实例,不接受字符串或时间戳 -
isSameDay(a, b):纯比较,不涉及任何解析或格式化逻辑
这样每个函数都只有明确输入类型、确定行为、无隐藏依赖。测试时可以独立 mock 输入,上线后某部分出问题也不会牵连其他。
最常被忽略的一点:模块边界一旦划定,就别为了“方便”跨边界调用私有函数。比如 formatDate 内部用了 parseDate 是合理的,但业务代码里直接 import parseDate 然后手动拼接格式字符串,就破坏了抽象层级。