高阶函数是接受函数作为参数或返回函数作为结果的函数,体现JavaScript函数是一等公民;如map、filter、reduce及手写的once、memoize,核心在于运行时传入/返回函数值而非语法糖。
高阶函数不是“高级写法”,而是指**接受函数作为参数,或返回函数作为结果的函数**——这是 JavaScript 中函数是一等公民的直接体现,不是语法糖,也不是可选技巧。
typeof 为 "function" 的值能否被传入或返回只要满足以下任一条件,就是高阶函数:
Array.prototype.map 的第一个参数)return 返回了一个新函数(如 const add = (a) => (b) => a + b)常见误判:把闭包、箭头函数、异步回调本身当成高阶函数——它们只是函数,只有当被当作参数传给另一个函数,或被另一个函数返回时,才参与构成高阶函数调用链。
map、filter、reduce 是最常用的内置高阶函数它们本身不执行具体逻辑,而是把“怎么处理每个元素”的决策权交给传入的回调函数。这正是高阶函数的价值:解耦数据遍历与业务逻辑。
例如:
const numbers = [1, 2, 3]; const doubled = numbers.map(n => n * 2); //n => n * 2是传入的函数 const evens = numbers.filter(n => n % 2 === 0); //n => n % 2 === 0是传入的函数 const sum = numbers.reduce((acc, n) => acc + n, 0); //(acc, n) => acc + n是传入的函数
注意:map 和 filter 不修改原数组;reduce 的初始值(第二个参数)不能省略,否则第一次调用时 acc 会是数组第一个元素,容易出错。
once 和 memoize
这两个是典型场景:控制函数执行次数(once),或复用已有计算结果(memoize)。它们必须返回新函数,才能拦截原始调用。
const once = (fn) => {
let called = false;
let result;
return function(...args) {
if (!called) {
result = fn.apply(this, args);
called = true;
}
return result;
};
};
const memoize = (fn) => {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
};
关键点:
once 内部用闭包保存 called 和 result,确保多次调用返回同一结果memoize 用 JSON.stringify(args) 做简易键生成,仅适用于参数可序列化的场景;若含函数、undefined、Symbol,需改用更健壮的哈希方案...args 和 fn.apply(this, args) 保证 this 和参数透传,否则会丢失上下文this 绑定、参数数量、副作用时机高阶函数不是“套个壳就完事”。下面这些错误在真实项目中高频出现:
obj.method.map(...) 时,method 被提取后丢失 this,应写成 obj.method.bind(obj) 或 (...args) => obj.method(...args)
array.forEach(callback) 传了 3 个参数,但 callback 只声明了 1 个形参,剩余参数会被静默丢弃真正难的不是写出高阶函数,而是判断该在哪一层做抽象、哪些状态该由闭包捕获、哪些该由调用方传入——这需要对数据流和生命周期有清晰把握。