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

函数命名必须体现职责,而非实现方式
很多人写 getDataFromApi 这类名字,看似清晰,实则把调用方式锁死了——万一后续改成从 localStorage 读取,函数名就失效了。更合理的命名是 fetchUserProfile 或 loadCartItems,聚焦“做什么”,不暴露“怎么做”。
这类函数应满足:单一输入输出、无副作用、可预测。例如:
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
- 不修改全局状态或传入对象
- 不依赖外部变量(如
window.API_BASE),必要时通过参数注入 - 返回值类型稳定:总是
boolean,不有时返回null有时抛错
组件化不是套框架,而是隔离副作用与状态边界
哪怕不用 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 => `-
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 然后手动拼接格式字符串,就破坏了抽象层级。










