散修带你入门鸿蒙应用开发基础第八节:高阶函数核心解析与应用

ArkTS基础第八节:高阶函数核心解析与应用

炼气八重天

【学习目标】

  1. 理解高阶函数的核心定义,明确其与闭包、箭头函数的关联(衔接前两节知识点)。
  2. 掌握ArkTS中高阶函数的两类核心实现形式(函数作为参数、函数作为返回值),能编写基础自定义高阶函数。
  3. 熟练运用ArkTS内置高阶函数(map/filter/reduce)简化数组操作,提升集合处理效率。
  4. 了解高阶函数的基础应用场景,规避过度封装导致的可读性下降问题。

【学习重点】

  1. 高阶函数核心定义:满足“函数作为参数传入”或“函数作为返回值导出”其一的函数。
  2. 实现形式:函数作为参数(基础场景)、函数作为返回值(结合闭包);
  3. 内置工具map(数组映射)、filter(数组过滤)、reduce(数组归并)的基础用法;
  4. 实战场景:集合数据转换、条件筛选、数据聚合;
  5. 避坑要点:避免无意义的多层高阶函数嵌套,保障代码可读性。

【温馨提示】

高阶函数是闭包与箭头函数的“组合技能”,核心依赖前两节的基础语法,是ArkTS实现代码复用、简化集合操作的核心工具,基础阶段聚焦“会用”而非“深度封装”。

一、高阶函数的核心概念

1.1 高阶函数的定义

在ArkTS中,高阶函数是指满足以下任一条件的函数:

  1. 函数作为参数:接收一个或多个函数类型的参数;
  2. 函数作为返回值:执行后返回一个新的函数(通常结合闭包实现)。

核心关联:高阶函数常与闭包结合使用,返回函数的高阶函数本质是闭包的封装形式,二者共同实现“逻辑复用+状态保留”。

1.2 高阶函数与普通函数的对比

  • 普通函数:参数和返回值均为基础类型/对象(如number/string/interface实例);
  • 高阶函数:参数或返回值为函数类型,支持逻辑的动态注入和复用。
/**
 * 普通函数:固定逻辑的加法运算
 * 功能:仅能实现两个数字的加法,逻辑不可动态调整
 * @param a 第一个加数(number类型)
 * @param b 第二个加数(number类型)
 * @returns 两个数字的和(number类型)
 */
function add(a: number, b: number): number {
  return a + b;
}

/**
 * 高阶函数:通用计算工具
 * 功能:接收两个数字和自定义计算逻辑,动态执行不同运算
 * @param a 待计算的第一个数字(number类型)
 * @param b 待计算的第二个数字(number类型)
 * @param func 计算逻辑函数(接收两个number类型参数,返回number类型结果)
 * @returns 执行自定义计算逻辑后的结果(number类型)
 * 核心优势:无需修改函数本体,仅替换func参数即可实现加减乘除等不同运算
 */
function calculate(a: number, b: number, func: (x: number, y: number) => number): number {
  // 执行外部传入的自定义计算逻辑,实现逻辑解耦
  return func(a, b);
}

/**
 * 高阶函数调用示例
 * 场景1:传入加法逻辑,计算5+3
 * 场景2:传入乘法逻辑,计算5×3
 * 场景3:传入减法逻辑,计算5-3
 * 说明:通过动态注入不同逻辑,实现同一函数支持多类运算
 */
const sumResult = calculate(5, 3, (x, y) => x + y);
const mulResult = calculate(5, 3, (x, y) => x * y);
const subResult = calculate(5, 3, (x, y) => x - y);

console.log(`加法结果:${sumResult}`); // 输出:8
console.log(`乘法结果:${mulResult}`); // 输出:15
console.log(`减法结果:${subResult}`); // 输出:2

二、高阶函数的两大核心实现形式

2.1 形式一:函数作为参数(基础高频场景)

这是高阶函数最基础的实现形式,核心是“将逻辑作为参数传入”,实现动态的逻辑复用。

2.1.1 实战示例:通用数据处理器

/**
 * 高阶函数:通用数据处理器
 * 功能:接收任意数字和自定义处理逻辑,返回处理后的结果
 * @param data 待处理的原始数字(number类型)
 * @param handler 处理逻辑函数(接收number类型参数,返回number类型结果)
 * @returns 执行处理逻辑后的数字结果(number类型)
 * 应用场景:统一数据处理入口,支持翻倍、加值、取模、平方等多种操作
 */
function processData(data: number, handler: (num: number) => number): number {
  // 执行外部传入的处理逻辑,实现逻辑解耦和动态替换
  return handler(data);
}

/**
 * 通用数据处理器调用示例
 * 场景1:将数字10翻倍(注入翻倍逻辑)
 * 场景2:将数字10加5(注入加值逻辑)
 * 场景3:将数字10取模3(注入取模逻辑)
 * 场景4:将数字10平方(注入平方逻辑)
 */
const doubleResult = processData(10, (num) => num * 2);
const add5Result = processData(10, (num) => num + 5);
const mod3Result = processData(10, (num) => num % 3);
const squareResult = processData(10, (num) => num * num);

console.log(`翻倍结果:${doubleResult}`);  // 输出:20
console.log(`加5结果:${add5Result}`);    // 输出:15
console.log(`取模3结果:${mod3Result}`);  // 输出:1
console.log(`平方结果:${squareResult}`);  // 输出:100

2.2 形式二:函数作为返回值(结合闭包)

该形式本质是闭包的封装,外层高阶函数接收配置参数,返回的内层函数实现具体逻辑,兼顾“配置复用”和“状态保留”。

2.2.1 实战示例:带固定步长的累加器

/**
 * 高阶函数:创建带固定步长的累加器
 * 功能:接收累加步长,返回可复用的累加函数
 * @param step 累加步长(如3表示每次调用加3,number类型)
 * @returns 累加逻辑函数(接收number类型参数,返回number+step的结果)
 * 核心原理:内层箭头函数形成闭包,永久保留外层step参数的引用
 * 优势:一次配置步长,多次复用累加逻辑,无需重复传递step参数
 */
function createAdder(step: number): (num: number) => number {
  // 闭包特性:内层函数可访问外层函数的step参数,且step不会被垃圾回收
  return (num: number) => num + step;
}

/**
 * 累加器使用示例
 * 1. 创建步长为3的累加器,复用计算10+3、20+3
 * 2. 创建步长为10的累加器,复用计算5+10、15+10
 * 说明:不同累加器的step参数相互独立,闭包保证各自的状态隔离
 */
const add3 = createAdder(3);
console.log(`10+3 = ${add3(10)}`);  // 输出:13
console.log(`20+3 = ${add3(20)}`);  // 输出:23

const add10 = createAdder(10);
console.log(`5+10 = ${add10(5)}`);   // 输出:15
console.log(`15+10 = ${add10(15)}`); // 输出:25

三、ArkTS内置高阶函数(数组操作核心工具)

ArkTS为数组提供了map/filter/reduce等内置高阶函数,可快速实现数据映射、筛选、聚合,避免手动编写循环逻辑,提升代码简洁性。

3.1 map:数组映射(一对一转换)

核心功能:遍历数组,对每个元素执行指定转换逻辑,返回长度与原数组一致的新数组(原数组不修改)。

/**
 * map实战:成绩等级转换
 * 功能:将数字分数数组转换为对应的等级数组(A/B/C/D)
 * 核心逻辑:一对一映射,每个分数对应一个等级,新数组长度与原数组一致
 */
// 原始分数数组
const scores: number[] = [85, 92, 78, 66];

/**
 * map回调函数说明:
 * @param score 当前遍历的分数元素(number类型)
 * @returns 转换后的等级字符串(string类型)
 * 等级规则:≥90→A,≥80→B,≥70→C,其他→D
 */
const levels = scores.map((score) => {
  if (score >= 90) return "A";
  if (score >= 80) return "B";
  if (score >= 70) return "C";
  return "D";
});

console.log(`原始分数数组:${scores}`); // 输出:85,92,78,66(原数组不变)
console.log(`转换后等级数组:${levels}`); // 输出:B,A,C,D(新数组)

3.2 filter:数组过滤(按条件筛选)

核心功能:遍历数组,仅保留回调函数返回true的元素,返回长度≤原数组的新数组(原数组不修改)。

/**
 * filter实战:筛选及格成绩
 * 功能:从所有分数中筛选出≥60的及格分数
 * 核心逻辑:回调函数返回true则保留元素,false则剔除
 */
// 所有学生的分数数组
const allScores: number[] = [55, 88, 95, 58, 72];

/**
 * filter回调函数说明:
 * @param score 当前遍历的分数元素(number类型)
 * @returns 布尔值(true=保留,false=剔除)
 * 筛选规则:分数≥60为及格,保留;否则剔除
 */
const passScores = allScores.filter((score) => score >= 60);

console.log(`原始分数数组:${allScores}`); // 输出:55,88,95,58,72(原数组不变)
console.log(`及格分数数组:${passScores}`); // 输出:88,95,72(筛选后的新数组)

3.3 reduce:数组归并(聚合计算)

核心功能:遍历数组,将所有元素按回调逻辑聚合为单个值(如求和、求积、统计总数),是数组聚合的核心工具。

/**
 * reduce实战1:数组求和
 * 功能:计算数字数组中所有元素的总和
 * 核心逻辑:累加器(sum)保存上一轮结果,与当前元素(current)累加
 * @param sum 累加器(上一轮的求和结果,number类型)
 * @param current 当前遍历的元素(number类型)
 * @param 0 累加器初始值(必填,避免数组为空时出错)
 */
const nums: number[] = [1, 2, 3, 4, 5];
const total = nums.reduce((sum, current) => sum + current, 0);
console.log(`数组总和:${total}`); // 输出:15(1+2+3+4+5)

/**
 * reduce实战2:计算成绩平均分
 * 功能:先求和所有分数,再除以人数得到平均分
 * 步骤拆解:1. reduce求和 → 2. 总和 ÷ 数组长度 = 平均分
 */
const studentScores: number[] = [80, 90, 75, 85];
const averageScore = studentScores.reduce((sum, score) => sum + score, 0) / studentScores.length;
console.log(`成绩平均分:${averageScore}`); // 输出:82.5

3.4 内置高阶函数链式调用(基础进阶)

可将map/filter/reduce链式组合,实现复杂数据处理逻辑,简化多层循环嵌套。

/**
 * 链式调用实战:筛选及格成绩并转换为等级
 * 功能:先筛选出及格分数,再将及格分数转换为对应的等级
 * 优势:无需定义中间变量,代码简洁且逻辑连贯
 * 步骤拆解:1. filter筛选及格分数 → 2. map转换为等级
 */
const allStudentScores: number[] = [55, 88, 95, 58, 72, 83];

const passLevels = allStudentScores
  .filter((score) => score >= 60) // 第一步:筛选及格分数
  .map((score) => { // 第二步:转换为等级
    if (score >= 90) return "A";
    if (score >= 80) return "B";
    return "C";
  });

console.log(`及格成绩等级:${passLevels}`); // 输出:B,A,C,B

四、高阶函数的基础应用场景

4.1 场景1:集合数据格式转换

/**
 * 场景1:价格格式转换
 * 功能:将数字价格数组转换为带人民币单位的字符串数组(保留2位小数)
 * 应用场景:前端展示价格时的格式统一处理
 */
const prices: number[] = [19.9, 29.9, 39.9];

/**
 * map回调逻辑:
 * @param price 当前遍历的价格数字(number类型)
 * @returns 格式化后的价格字符串(如¥19.90)
 * 格式化规则:拼接¥符号 + 保留2位小数
 */
const priceStrs = prices.map((price) => `¥${price.toFixed(2)}`);

console.log(`格式化后价格数组:${priceStrs}`); // 输出:¥19.90,¥29.90,¥39.90

4.2 场景2:条件筛选+数据聚合

/**
 * 场景2:筛选偶数并求和
 * 功能:先筛选数组中的偶数,再计算偶数的总和
 * 步骤拆解:1. filter筛选偶数 → 2. reduce求和
 * 应用场景:数据统计类业务(如统计偶数金额、偶数数量等)
 */
const numList: number[] = [1, 2, 3, 4, 5, 6, 7, 8];

const evenTotal = numList
  .filter((num) => num % 2 === 0) // 筛选偶数
  .reduce((sum, num) => sum + num, 0); // 对偶数求和

console.log(`偶数总和:${evenTotal}`); // 输出:20(2+4+6+8)

4.3 场景3:带配置的逻辑复用(结合闭包)

/**
 * 高阶函数:创建按阈值筛选的通用筛选器
 * 功能:接收筛选阈值,返回可复用的筛选函数
 * @param threshold 筛选阈值(number类型,如5表示筛选大于5的数字)
 * @returns 筛选逻辑函数(接收number,返回boolean)
 * 核心原理:闭包保留threshold参数,实现筛选逻辑的定制化复用
 */
function createThresholdFilter(threshold: number): (num: number) => boolean {
  return (num) => num > threshold;
}

/**
 * 通用筛选器使用示例
 * 场景1:创建阈值为5的筛选器,筛选大于5的数字
 * 场景2:创建阈值为7的筛选器,筛选大于7的数字
 * 说明:通过闭包保留不同阈值,实现筛选逻辑的复用
 */
const filterGreaterThan5 = createThresholdFilter(5);
const result1 = [3, 6, 8, 2, 9].filter(filterGreaterThan5);
console.log(`大于5的数字:${result1}`); // 输出:6,8,9

const filterGreaterThan7 = createThresholdFilter(7);
const result2 = [3, 6, 8, 2, 9].filter(filterGreaterThan7);
console.log(`大于7的数字:${result2}`); // 输出:8,9

五、高阶函数避坑要点(基础版)

5.1 避免无意义的多层嵌套

/**
   * 避坑示例:避免多层高阶函数嵌套
   * 反面案例:嵌套层数过多,逻辑晦涩,可读性差
   * 正面案例:拆分逻辑为独立函数,结构清晰,易维护
   */
  // 不推荐:多层嵌套,逻辑难以理解
  const complexResult = [1,2,3].map((num: number): number => {
    return (() => num * 2)();
  }).filter((num): boolean => {
    return (() => num > 3)();
  });

// 推荐:拆分逻辑为独立函数,提升可读性
/**
 * 独立函数:数字翻倍逻辑
 * @param num 待翻倍的数字(number类型)
 * @returns 翻倍后的数字(number类型)
 */
const doubleNum = (num: number) => num * 2;

/**
 * 独立函数:判断数字是否大于3
 * @param num 待判断的数字(number类型)
 * @returns 布尔值(true=大于3,false=不大于3)
 */
const isGreaterThan3 = (num: number) => num > 3;

const simpleResult = [1,2,3].map(doubleNum).filter(isGreaterThan3);
console.log(`简化后结果:${simpleResult}`); // 输出:4,6

5.2 明确函数类型标注

/**
 * 避坑示例:明确函数类型标注
 * 错误案例:无类型标注,易引发隐式any类型错误(ArkTS强类型语言不推荐)
 * 正确案例:显式标注所有参数和返回值类型,提升代码健壮性
 */
// 错误示例(注释掉,避免编译报错)
// function badFunc(data, handler) {
//   return handler(data);
// }

// 正确示例:显式类型标注
/**
 * 规范的高阶函数写法
 * @param data 待处理数字(number类型)
 * @param handler 处理函数(接收number类型参数,返回string类型结果)
 * @returns 处理后的字符串结果(string类型)
 */
function goodFunc(data: number, handler: (num: number) => string): string {
  return handler(data);
}

5.3 避免过度封装

简单逻辑无需封装为高阶函数,例如单一的加法计算直接用普通函数实现即可,避免过度设计导致代码冗余。

六、课堂小结

  1. 核心定义:高阶函数是接收函数作为参数或返回函数的特殊函数,常与闭包结合实现逻辑复用。
  2. 实现形式:函数作为参数(基础场景)、函数作为返回值(结合闭包),二者可灵活组合。
  3. 内置工具map实现数组映射、filter实现条件筛选、reduce实现数据聚合,支持链式调用。
  4. 应用场景:集合数据转换、条件筛选、带配置的逻辑复用,是简化数组操作的核心工具。
  5. 避坑要点:控制嵌套层数、明确类型标注、避免过度封装,保障代码可读性。

七、课后练习

  1. 基础实现题:使用map将数组[1,2,3,4]转换为["1号","2号","3号","4号"]格式。
  2. 组合实战题:先通过filter筛选出数组[10,15,20,25,30]中能被5整除且大于15的数,再用reduce计算它们的乘积。
  3. 高阶封装题:实现高阶函数createMultiplier(factor: number),返回一个函数,该函数接收数字并返回其与factor的乘积(结合闭包)。

八、课后练习参考答案与解析

8.1 数组格式转换

/**
 * 练习1参考答案:数组格式转换
 * 功能:将数字数组[1,2,3,4]转换为["1号","2号","3号","4号"]
 * 核心逻辑:利用map的一对一映射特性,对每个元素做字符串拼接
 */
const nums: number[] = [1,2,3,4];
const numStrs = nums.map((num) => `${num}号`);
console.log(`转换结果:${numStrs}`); // 输出:1号,2号,3号,4号
  • 解析:利用map的一对一映射特性,直接对每个元素做字符串拼接,保留原数组长度。

8.2 筛选+聚合实战

/**
 * 练习2参考答案:筛选+聚合
 * 功能:筛选能被5整除且>15的数字,计算它们的乘积
 * 步骤拆解:1. filter筛选符合条件的数字 → 2. reduce累乘(初始值为1)
 */
const arr: number[] = [10,15,20,25,30];
const product = arr
  .filter((num) => num % 5 === 0 && num > 15) // 筛选条件:能被5整除且>15
  .reduce((prod, num) => prod * num, 1); // 累乘(初始值为1,避免乘积为0)

console.log(`筛选后乘积:${product}`); // 输出:20*25*30=15000
  • 解析filter先筛选出符合条件的元素[20,25,30]reduce从1开始累乘,最终得到乘积结果。

8.3 高阶函数封装

/**
 * 练习3参考答案:通用乘法器(高阶函数+闭包)
 * 功能:创建可复用的乘法器,支持自定义乘数
 * @param factor 乘数(number类型,如3表示乘以3)
 * @returns 乘法逻辑函数(接收number类型参数,返回num*factor的结果)
 * 核心原理:闭包保留factor参数,实现乘法逻辑的定制化复用
 */
function createMultiplier(factor: number): (num: number) => number {
  return (num) => num * factor;
}

/**
 * 乘法器使用示例
 * 创建乘数为3的乘法器,计算5×3的结果
 */
const multiplyBy3 = createMultiplier(3);
console.log(`5×3的结果:${multiplyBy3(5)}`); // 输出:15
  • 解析:外层函数接收乘数factor,内层箭头函数通过闭包保留该值,调用时只需传入待乘数字,即可复用乘法逻辑。

九、代码仓库

工程名称:HigherOrderFuncDemo本节代码已同步至:https://gitee.com/juhetianxia321/harmony-os-code-base.git

十、下节预告

下一节将进入第九节:模块的导入导出阶段(炼气九重天:高阶函数的工程化复用基础),重点学习:

  1. 理解ArkTS“文件即模块”的核心规范,明确模块导入导出与高阶函数复用的关联;
  2. 掌握export的两类核心方式(命名导出/默认导出),将本节的高阶函数拆分为独立工具模块;
  3. 掌握import的四类用法(命名导入/默认导入/批量导入/混合导入),灵活调用模块化的高阶函数;
  4. 吃透鸿蒙项目的模块路径规则,解决跨文件调用高阶函数的路径报错问题;
  5. 落地通用场景的高阶函数模块化实战,为后续接口、类的复用建立工程化基础。

十一、鸿蒙开发者学习与认证指引

(一)、官方学习班级报名(免费)

  1. 班级链接HarmonyOS赋能资源丰富度建设(第四期)
  2. 学号填写规则:填写个人手机号码即可完成班级信息登记

(二)、HarmonyOS应用开发者认证考试(免费)

  1. 考试链接HarmonyOS开发者能力认证入口

  2. 认证等级及适配人群

    • 基础认证:适配软件工程师、移动应用开发人员,需掌握HarmonyOS基础概念、DevEco Studio基础使用、ArkTS及ArkUI基础开发等能力;
    • 高级认证:适配项目经理、工程架构师,需掌握系统核心技术理念、应用架构设计、关键技术开发及应用上架运维等能力;
    • 专家认证:适配研发经理、解决方案专家,需掌握分布式技术原理、端云一体化开发、跨端迁移及性能优化等高级能力。
  3. 认证权益:通过认证可获得电子版证书以及其他专属权益。

posted @ 2025-12-12 16:16  鸿蒙-散修  阅读(0)  评论(0)    收藏  举报