javascript函数是什么_如何编写可复用的代码块

JavaScript函数是一等公民,需明确输入、输出和边界;应主动防御异常输入,避免闭包陷阱,导出时用命名函数表达式以保障调试与稳定性。

JavaScript 函数就是可被多次调用的命名代码块

它不是语法糖,也不是“封装概念”的抽象说法——function 是 JavaScript 中的一等公民,能赋值给变量、作为参数传入、从函数里返回。所谓“可复用”,本质是:同一段逻辑,不靠复制粘贴,而是通过 callapply 触发执行,且每次可接收不同输入、产生对应输出。

写函数前先问三个问题:它要处理什么数据?返回什么?边界在哪?

跳过这步直接写,大概率写出带隐式依赖、副作用或类型假设的“脆函数”。比如:

function formatDate(date) {
  return new Date(date).toLocaleDateString();
}

这段代码在 date 是字符串时能跑,但传入 nullundefined 或无效格式字符串(如 "2025-13-01")就会静默失败或抛错。可复用函数得主动防御:

  • typeofinstanceof 检查输入类型,必要时用 String()Number() 做安全转换
  • 明确返回值类型,避免有时返回 string、有时返回 undefined
  • 对空值、NaN、非日期字符串等常见异常输入,统一返回 null 或抛出带上下文的 Error

避免闭包陷阱:别让函数意外捕获外部变量

闭包本身不是问题,但当函数被导出、传递到其他模块或异步回调中时,捕获的外部变量可能已变更或销毁。典型错误:

function createHandlers() {
  const items = ['a', 'b', 'c'];
  return items.map((item, i) => () => console.log(i, item));
}
const handlers = createHandlers();
handlers[0](); // 输出 "2 c" —— 因为循环结束时 i=2,item='c'

修复方式很简单:用参数绑定当前值,而不是依赖闭包捕获的循环变量:

  • 改用 for...of + let(块级作用域)
  • 或显式传参:() => handler(i, item)
  • 或用 bindhandler.bind(null, i, item)

导出函数时优先用命名函数表达式而非箭头函数

箭头函数没有 name 属性,堆栈追踪时只显示 ;调试、监控、性能分析都变困难。尤其当函数被框架或工具链重命名(如 Webpack 的 function-inlining)后,问题更隐蔽。

正确写法:

export const validateEmail = function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};

这样既保留了 validateEmail.name === "validateEmail",又支持具名递归和清晰的 devtools 显示。

真正难的从来不是“怎么写一个函数”,而是判断这个函数是否该存在、它的输入输出契约是否稳定、以及它被调用十次后,第十一次会不会因为某个没料到的参数而崩掉。