百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

前端面试-js高阶函数的介绍和使用

itomcoil 2025-04-05 19:24 10 浏览

一、什么是高阶函数?

高阶函数满足以下任一条件:

  1. 接受函数作为参数
  2. 返回一个新的函数

高阶函数的核心思想是将函数视为“一等公民”,像操作数据一样操作函数。

二、高阶函数的常见用途

1. 数组方法(接受函数作为参数)

JavaScript 数组的许多内置方法都是高阶函数,典型例子包括:

  • map():对数组每个元素执行操作,返回新数组。
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]

filter():筛选符合条件的元素。

const evens = numbers.filter(x => x % 2 === 0); // [2]

reduce():将数组元素累积为单个值。

const sum = numbers.reduce((acc, x) => acc + x, 0); // 6

find()some():查找元素或判断条件。

const firstEven = numbers.find(x => x % 2 === 0); // 2
const hasEven = numbers.some(x => x % 2 === 0); // true

高阶用法

一、函数组合与管道(Function Composition & Piping)

1.组合多个函数

将多个函数串联为一个新函数,实现数据处理流水线:

const compose = (...fns) => (initialValue) =>
  fns.reduceRight((acc, fn) => fn(acc), initialValue);

// 示例:字符串处理
const toUpperCase = str => str.toUpperCase();
const addExclamation = str => str + "!";
const reverse = str => str.split('').reverse().join('');

const processString = compose(reverse, addExclamation, toUpperCase);
console.log(processString("hello")); // "OLLEH! -> !OLLEH"

2.管道(从左到右执行)

与 compose 类似,但执行顺序相反:

const pipe = (...fns) => (initialValue) =>
  fns.reduce((acc, fn) => fn(acc), initialValue);

const processStringPipe = pipe(toUpperCase, addExclamation, reverse);
console.log(processStringPipe("hello")); // "HELLO! -> !OLLEH"

二、高阶函数的抽象模式

1.中间件模式(Middleware Pattern)

用于 Express、Redux 等框架的中间件系统:

const createMiddleware = (...middlewares) => {
  return (initialHandler) => {
    return middlewares.reduceRight(
      (next, middleware) => middleware(next),
      initialHandler
    );
  };
};

// 中间件示例
const logger = (next) => (action) => {
  console.log("Action:", action);
  return next(action);
};
const validator = (next) => (action) => {
  if (!action.type) throw new Error("Invalid action");
  return next(action);
};

// 组合中间件
const finalHandler = (action) => console.log("Handling:", action);
const applyMiddleware = createMiddleware(logger, validator);
const enhancedHandler = applyMiddleware(finalHandler);

enhancedHandler({ type: "TEST" }); // 输出日志并验证

2.依赖注入(Dependency Injection)

通过高阶函数实现解耦:

const createService = (dependencies) => (config) => {
  return {
    log: () => dependencies.logger.log(config.message)
  };
};

// 使用
const serviceFactory = createService({ logger: console });
const service = serviceFactory({ message: "Hello" });
service.log(); // 输出 "Hello"

三、高阶函数与异步编程

1.Promise 链的抽象

用高阶函数封装重复的异步逻辑:

const withRetry = (fn, retries = 3) => async (...args) => {
  for (let i = 0; i < retries i try return await fn...args catch error if i='== retries' - 1 throw error const fetchdata='()'> fetch("https://api.example.com/data");
const fetchWithRetry = withRetry(fetchData, 2);
fetchWithRetry().then(/* ... */);

2.异步函数组合

处理异步函数的流水线:

const asyncPipe = (...fns) => async (initial) => {
  return fns.reduce(async (accPromise, fn) => {
    const acc = await accPromise;
    return fn(acc);
  }, initial);
};

// 示例
const fetchUser = async (id) => ({ id, name: "John" });
const fetchPosts = async (user) => [{ userId: user.id, post: "Hello" }];
const processData = asyncPipe(fetchUser, fetchPosts);

processData(1).then(console.log); // 输出用户关联的帖子

四、惰性求值与高阶函数

通过高阶函数实现延迟计算:

const lazyMap = (fn) => function* (iterable) {
  for (const item of iterable) {
    yield fn(item);
  }
};

const lazyFilter = (predicate) => function* (iterable) {
  for (const item of iterable) {
    if (predicate(item)) yield item;
  }
};

// 使用生成器实现惰性计算
const numbers = [1, 2, 3, 4, 5];
const mapped = lazyMap(x => x * 2)(numbers);
const filtered = lazyFilter(x => x > 5)(mapped);

console.log([...filtered]); // [6, 8, 10]

五、状态管理与高阶函数

1.状态转换器

封装状态变更逻辑:

const withState = (initialState) => (reducer) => {
  let state = initialState;
  return (...args) => {
    state = reducer(state, ...args);
    return state;
  };
};

// 示例:计数器
const counterReducer = (state, action) => {
  switch (action) {
    case "INC": return state + 1;
    case "DEC": return state - 1;
    default: return state;
  }
};

const counter = withState(0)(counterReducer);
console.log(counter("INC")); // 1
console.log(counter("INC")); // 2

2.Redux 风格的 Store

模仿 Redux 的核心逻辑:

const createStore = (reducer, initialState) => {
  let state = initialState;
  const listeners = [];
  return {
    getState: () => state,
    dispatch: (action) => {
      state = reducer(state, action);
      listeners.forEach(listener => listener());
    },
    subscribe: (listener) => {
      listeners.push(listener);
      return () => listeners.splice(listeners.indexOf(listener), 1);
    }
  };
};

六、高级柯里化与部分应用

1.动态柯里化

支持任意数量参数的柯里化:

const curry = (fn) => {
  const curried = (...args) =>
    args.length >= fn.length
      ? fn(...args)
      : (...moreArgs) => curried(...args, ...moreArgs);
  return curried;
};

// 使用
const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3)); // 6
console.log(add(1, 2)(3)); // 6

2.占位符参数

实现灵活的部分应用(类似 Lodash 的 _):

const _ = Symbol("placeholder");
const partial = (fn, ...args) => (...newArgs) => {
  const finalArgs = args.map(arg => arg === _ ? newArgs.shift() : arg);
  return fn(...finalArgs, ...newArgs);
};

// 示例
const greet = (greeting, name) => `${greeting}, ${name}!`;
const sayHello = partial(greet, "Hello", _);
console.log(sayHello("Alice")); // "Hello, Alice!"

七、函数式编程库的底层实现

1.Functor(函子)

封装值并提供 map 方法:

class Functor {
  constructor(value) {
    this.value = value;
  }
  map(fn) {
    return new Functor(fn(this.value));
  }
}

// 使用
new Functor(2)
  .map(x => x * 3)
  .map(x => x + 1)
  .value; // 7

2.Monad(单子)

处理副作用或嵌套结构(如 Promise):

class Maybe {
  constructor(value) {
    this.value = value;
  }
  static of(value) {
    return new Maybe(value);
  }
  map(fn) {
    return this.value == null ? this : Maybe.of(fn(this.value));
  }
  flatMap(fn) {
    return this.value == null ? this : fn(this.value);
  }
}

// 使用
Maybe.of(null)
  .map(x => x.toUpperCase()) // 不会执行
  .flatMap(x => Maybe.of(x + "!")); // 安全跳过

八、性能优化技巧

1.记忆化(Memoization)

缓存函数结果:

const memoize = (fn) => {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
};

// 使用
const factorial = memoize(n => n <= 1 ? 1 : n * factorial(n - 1));
console.log(factorial(5)); // 120 (仅计算一次)

2.惰性链式调用

避免不必要的中间数组(类似 Lodash 的链式调用):

const lazyChain = (data) => ({
  map: (fn) => lazyChain({
    *[Symbol.iterator]() {
      for (const item of data) yield fn(item);
    }
  }),
  filter: (fn) => lazyChain({
    *[Symbol.iterator]() {
      for (const item of data) if (fn(item)) yield item;
    }
  }),
  value: () => [...data]
});

// 使用
const result = lazyChain([1, 2, 3])
  .map(x => x * 2)
  .filter(x => x > 3)
  .value(); // [4, 6]

九、总结:高阶函数的深度价值

  1. 抽象复杂性:将多步骤逻辑封装为可复用的单元。
  2. 声明式编程:用 what to do 代替 how to do。
  3. 函数式模式:实现 Functor、Monad、IO 等高级模式。
  4. 性能优化:通过惰性计算和记忆化提升效率。
  5. 框架设计:支撑中间件、状态管理等架构模式。

实际应用建议

  • 在数据处理、异步流程、工具库开发中优先使用高阶函数
  • 结合 TypeScript 强化类型推导
  • 避免过度抽象,保持代码可读性
  • 使用 Lodash/fp、Ramda 等函数式库加速开发

通过深度应用高阶函数,可以将 JavaScript 代码提升到接近函数式语言(如 Haskell)的表达能力,同时保持 JavaScript 的灵活性。

相关推荐

字节三面:MySQL数据同步ES的4种方法!你能想到几种?

如何进行数据同步MySQL是一种流行的关系型数据库,而Elasticsearch是一个强大的搜索引擎和分析平台。将MySQL数据同步到Elasticsearch中可以帮助我们更方便地搜索和分析数据。在...

Java 连接 MySQL 数据库(java连接mysql课设)

一、环境准备1.1依赖管理(Maven)在项目的pom.xml中添加MySQL驱动依赖:<dependency><groupId>mysql</gro...

Spring Boot 连接 MySQL 数据库(spring boot配置数据库连接)

一、环境准备1.1依赖管理(Maven)<!--方案1:JdbcTemplate--><dependency><groupId>org.sprin...

java连接mysql数据库达成数据查询详细教程

前言:本篇文章适用于所有前后端开发者众所周知,只要是编程,那肯定是需要存储数据的,无论是c语言还是java,都离不开数据的读写,数据之间传输不止,这也就形成了现代互联网的一种相互存在关系!而读写存储的...

既然有MySQL了,为什么还要有MongoDB?

大家好,我是哪吒,最近项目在使用MongoDB作为图片和文档的存储数据库,为啥不直接存MySQL里,还要搭个MongoDB集群,麻不麻烦?让我们一起,一探究竟,了解一下MongoDB的特点和基本用法,...

用 JSP 连接 MySQL 登入注册项目实践(JSP + HTML + CSS + MySQL)

目录一、写在前面二、效果图三、实现思路四、实现代码1、login总界面2、registercheck总代码3、logoutcheck总代码4、amendcheck总代码相关文章一、写在前面哈喽~大家好...

MySQL关联查询时,为什么建议小表驱动大表?这样做有什么好处

在SQL数据库中,小表驱动大表是一种常见的优化策略。这种策略在涉及多表关联查询的情况下尤其有效。这是因为数据库查询引擎会尽可能少的读取和处理数据,这样能极大地提高查询性能。"小表驱动大表&...

mysql8驱动兼容规则(mysql8.0驱动)

JDBC版本:Connector/J8.0支持JDBC4.2规范.如果Connector/J8.0依赖于更高版本的jdbclib,对于调用只有更高版本特定的方法会抛出SQLFea...

mysql数据表如何导入MSSQL中(mysql怎样导入数据)

本案例演示所用系统是windowsserver2012.其它版本windows操作系统类似。1,首先需要下载mysqlodbc安装包。http://dev.mysql.com/downloa...

MySQL 驱动中虚引用 GC 耗时优化与源码分析

本文要点:一种优雅解决MySQL驱动中虚引用导致GC耗时较长问题的解决方法虚引用的作用与使用场景MySQL驱动源码中的虚引用分析背景在之前文章中写过MySQLJDBC驱动中的虚引用导致...

ExcelVBA 连接 MySQL 数据库(vba 连接sqlserver)

上期分享了ExcelVBA连接sqlite3数据库,今天给大家分享ExcelVBA连接另一个非常流行的MySQL数据库。一、环境win10Microsoftoffice2010(...

QT 5.12.11 编译MySQL 8 驱动教程- 1.01版

安装编译环境:qt5.12.11mysql8.0.28修改mysql.pro工程文件,编译生成动态库mysql.pro文件位置:D:\Alantop_Dir\alantop_sde\Qt\Qt5....

「Qt入门第22篇」 数据库(二)编译MySQL数据库驱动

导语在上一节的末尾我们已经看到,现在可用的数据库驱动只有两类3种,那么怎样使用其他的数据库呢?在Qt中,我们需要自己编译其他数据库驱动的源码,然后当做插件来使用。下面就以现在比较流行的MySQL数据库...

(干货)一级注册计量师第五版——第四章第三节(三)

计量标准的建立、考核及使用(三)PS:内容都是经过个人学习而做的笔记。如有错误的地方,恳请帮忙指正!计量标准考核中有关技术问题1检定或校准结果的重复性重复性是指在一组重复性测量条件下的测量精密度。检定...

声学测量基础知识分享(声学测量pdf)

一、声学测量的分类和难点1.声学测量的分类声学测量按目的可分为:声学特性研究(声学特性研究、媒质特性研究、声波发射与接收的研究、测量方法与手段的研究、声学设备的研究),声学性能评价和改善(声学特性评价...