什么是高阶函数_如何在javascript中实现和使用它们【教程】

高阶函数是接受函数作为参数或返回函数作为结果的函数,体现JavaScript函数是一等公民;如map、filter、reduce及手写的once、memoize,核心在于运行时传入/返回函数值而非语法糖。

高阶函数不是“高级写法”,而是指**接受函数作为参数,或返回函数作为结果的函数**——这是 JavaScript 中函数是一等公民的直接体现,不是语法糖,也不是可选技巧。

高阶函数的核心判断标准:typeof"function" 的值能否被传入或返回

只要满足以下任一条件,就是高阶函数:

  • 参数中至少有一个是函数(如 Array.prototype.map 的第一个参数)
  • 函数体里用 return 返回了一个新函数(如 const add = (a) => (b) => a + b
  • 不关心函数名或是否具名,只看运行时行为:传入/返回的是函数值

常见误判:把闭包、箭头函数、异步回调本身当成高阶函数——它们只是函数,只有当被当作参数传给另一个函数,或被另一个函数返回时,才参与构成高阶函数调用链。

mapfilterreduce 是最常用的内置高阶函数

它们本身不执行具体逻辑,而是把“怎么处理每个元素”的决策权交给传入的回调函数。这正是高阶函数的价值:解耦数据遍历与业务逻辑。

例如:

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 是传入的函数

注意:mapfilter 不修改原数组;reduce 的初始值(第二个参数)不能省略,否则第一次调用时 acc 会是数组第一个元素,容易出错。

手写一个高阶函数:带缓存的 oncememoize

这两个是典型场景:控制函数执行次数(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 内部用闭包保存 calledresult,确保多次调用返回同一结果
  • memoizeJSON.stringify(args) 做简易键生成,仅适用于参数可序列化的场景;若含函数、undefinedSymbol,需改用更健壮的哈希方案
  • 两者都用 ...argsfn.apply(this, args) 保证 this 和参数透传,否则会丢失上下文

容易被忽略的陷阱:this 绑定、参数数量、副作用时机

高阶函数不是“套个壳就完事”。下面这些错误在真实项目中高频出现:

  • obj.method.map(...) 时,method 被提取后丢失 this,应写成 obj.method.bind(obj)(...args) => obj.method(...args)
  • 传入的回调函数参数数量不匹配:比如 array.forEach(callback) 传了 3 个参数,但 callback 只声明了 1 个形参,剩余参数会被静默丢弃
  • 在高阶函数内部提前执行了副作用(如发请求、改 DOM),而不是把副作用封装进返回的函数里,导致逻辑失控

真正难的不是写出高阶函数,而是判断该在哪一层做抽象、哪些状态该由闭包捕获、哪些该由调用方传入——这需要对数据流和生命周期有清晰把握。