JavaScript中函数是一等公民,可赋值、传参、返回和存储;高阶函数如map/filter/reduce及pipe等是解决实际问题的工具,纯函数需满足相同输入得相同输出且无副作用。
因为 JavaScript 中的函数能像数字、字符串一样被赋值、传参、返回和存储——这是“一等公民”的全部定义,不是修辞,而是语言底层能力。
这不是语法糖,而是运行时真实行为:函数对象可随时被引用、重赋值、放进数组或对象里。一旦理解这点,就自然跳出“函数只能声明后调用”的思维定式。
function sayHi() { console.log('Hi') } 然后试图 typeof sayHi === 'function' 是对的,但若没赋值给变量,它只是全局作用域的一个绑定,无法动态传递const sayHi = () => console.log('Hi'); 或 const handlers = { click: sayHi, submit: () => alert('done') };
addEventListener 又想 later 移除?不行——没引用就无法 removeEventListener,必须先存成变量map、filter、reduce 都是高阶函数,它们接收函数作为参数;once、debounce、pipe 都返回函数——这些不是 FP 专属玩具,而是解决实际问题的工具。
filter 不改原数组,但你自己写的 filterByAge(arr, min) 若用 arr.splice() 就破坏了不可
变性,不再是纯函数pipe 的实现很短,但能让逻辑线性可读:const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
for (...) arr.push(() => {...})),闭包捕获的是同一份变量,容易引发意料外的引用问题所谓“相同输入 → 相同输出 + 无副作用”,本质是为了让函数行为不依赖外部状态,方便测试、复用和组合。
const now = () => Date.now() 每次调用结果不同,无法缓存、无法单元测试断言const formatTime = (timestamp, fmt) => ... 所有依赖都显式传入,哪怕 fmt 是默认值,也建议用参数而非闭包捕获obj.name = 'new')、发请求、console.log —— 这些都会让函数脱离“纯”的范畴,影响组合可靠性真正卡住人的往往不是“怎么写 pipe”,而是忘记函数作为值时的生命周期管理——比如事件监听器没清理、闭包长期持有大对象、setTimeout 回调里还引用着已卸载组件的 this。一等公民的权利越大,责任越具体。