news 2026/10/3 2:31:52

用 reduce 计算对象数组平均年龄——Modern JavaScript Tutorial 数组方法实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 reduce 计算对象数组平均年龄——Modern JavaScript Tutorial 数组方法实战
  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

导读

本文围绕 Modern JavaScript Tutorial(javascript.info)array-methods 章节 中的经典练习题「Get average age」展开:编写getAverageAge(users)函数,对包含age属性的对象数组求平均年龄。通过这道题,你将掌握Array.prototype.reduce的完整用法——包括累加器语义、初始值参数、空数组陷阱,以及它与for..of、map等方法的取舍,最终能独立处理“对象数组聚合计算”这一类常见业务场景。

任务目标:对对象数组求平均年龄

练习任务定义在 10-average-age/task.md,要求如下:

  • 输入:一个由对象组成的数组,每个对象至少带有age数值属性;
  • 输出:所有age的算术平均值;
  • 计算公式:(age1 + age2 + ... + ageN) / N,其中N为数组长度。

题目给出的示例数据:

let john = { name: "John", age: 25 }; let pete = { name: "Pete", age: 30 }; let mary = { name: "Mary", age: 29 }; let arr = [ john, pete, mary ]; alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28

注意:这里的平均值就是普通算术平均数,不涉及加权、取整或去尾。示例结果为28,恰好是整数,但实际计算中25 + 30 + 29 = 84,84 / 3 = 28,分母是数组长度而非对象个数之外的任何额外因素。

参考答案:一行reduce搞定聚合

仓库中 10-average-age/solution.md 给出的标准解法极为简洁:

function getAverageAge(users) { return users.reduce((prev, user) => prev + user.age, 0) / users.length; } let john = { name: "John", age: 25 }; let pete = { name: "Pete", age: 30 }; let mary = { name: "Mary", age: 29 }; let arr = [ john, pete, mary ]; alert( getAverageAge(arr) ); // 28

这段代码的逻辑分两步:

  1. users.reduce((prev, user) => prev + user.age, 0)—— 把整个数组“折叠”成一个总和:0 + 25 + 30 + 29 = 84;
  2. 将总和除以users.length(即3),得到平均值28。

solution.md中的代码块带有js run标记,这是 javascript.info 教程源码特有的约定,表示该代码段可以被交互式运行验证——仓库中所有带此标记的练习解法都可以直接在浏览器控制台或任何支持 ES5+ 的环境执行。

深入原理:reduce的累加器机制

要真正理解上面的解法,需要先吃透Array.prototype.reduce。它在 array-methods/article.md 的「reduce/reduceRight」小节中有系统讲解,语法为:

let value = arr.reduce(function(accumulator, item, index, array) { // ... }, [initial]);

回调函数的四个参数:

参数含义
accumulator上一次调用的返回结果;首次调用时等于initial(若提供)
item当前数组元素
index当前元素的索引
array数组本身

reduce的核心思想是“携带结果前行”:每一次回调的返回值,都会作为下一次回调的第一个参数accumulator传入。全部元素遍历完后,最后一次回调的返回值就是整个reduce的结果。

在本题中,回调只用了两个参数(prev与user),这是最常见的用法:

  • 第一次调用:prev = 0(初始值),user = john,返回0 + 25 = 25;
  • 第二次调用:prev = 25,user = pete,返回25 + 30 = 55;
  • 第三次调用:prev = 55,user = mary,返回55 + 29 = 84。

最终reduce返回84。下面是同样的过程用表格呈现(各行的prev即上一行的结果):

调用次数prev(累加器)user(当前项)返回结果
第 1 次0{name:"John", age:25}25
第 2 次25{name:"Pete", age:30}55
第 3 次55{name:"Mary", age:29}84

上图(仓库中的 reduce.svg)展示了reduce在数组元素间传递累加结果的数据流,正是本文求和的执行过程。

为什么必须显式传入初始值0

solution.md中特意写了, 0这个第二参数,这并非可有可无。reduce允许省略初始值:

let result = arr.reduce((sum, current) => sum + current); // 同样返回 15

省略时,reduce会把数组第一个元素当作初始accumulator,并从第二个元素开始迭代。但原文档明确指出这种做法有隐患:如果数组为空,且没有提供初始值,reduce会直接抛出错误(TypeError: Reduce of empty array with no initial value)。

let arr = []; arr.reduce((sum, current) => sum + current); // TypeError!

而对于本题这种“可能传入空数组”的通用工具函数来说,显式指定0还能保证:空数组时reduce返回0,随后0 / 0 = NaN,至少不会抛异常。因此:

  • 在求和的聚合场景,永远显式传入初始值0;
  • 若希望空数组返回0而非NaN,可先判断长度,见下文“健壮性改造”。

reduceRight:从右向左的同门兄弟

reduce还有反向版本Array.prototype.reduceRight,回调参数含义完全相同,只是遍历方向从右到左。对于“求和”这类满足交换律的运算,两者结果一致;但在处理顺序敏感的累积(如从右往左拼接字符串、解析后缀表达式)时,reduceRight才有用武之地。

从求和到平均:一次reduce演算两个经典练习

reduce在 javascript.info 教程的练习体系中反复出现,是理解“对象集合聚合”的一把钥匙。除了本题,仓库中还有两个同族练习可以对照学习:

  • 12-reduce-object(12-reduce-object/_js.view/solution.js):用reduce把对象数组[{name:"John",salary:100}, ...]折叠成按name索引的映射对象,核心写法同样是array.reduce((obj, value) => { obj[value.name] = value.salary; return obj; }, {})——每次迭代都要显式返回累加器,这是对象聚合与数值求和的共同关键点;
  • Sum the properties(01-sum-salaries/task.md 与更早的 5-sum-object/task.md):对对象而非数组求和,需要先用Object.values取出数值序列再累加。

三者放在一起可以看出:聚合计算的本质是先“取数”(user.age或Object.values),再“折叠”(reduce)。平均年龄 = 年龄总和 ÷ 人数,正是这套思路的典型应用。

健壮性改造与常见边界情况

参考答案追求极简,作为生产级函数还可以补齐边界处理:

1. 空数组与除零

function getAverageAge(users) { if (users.length === 0) return 0; // 约定:空数组平均值为 0 return users.reduce((prev, user) => prev + user.age, 0) / users.length; }

0 / 0的结果是NaN,多数业务场景希望空数据返回中性值(0或null),因此前置守卫是常见做法。

2. 非整数结果与展示格式化

年龄均值常为小数(如三人的年龄是 25、30、31,均值为28.666...)。按需求保留位数:

// 保留一位小数 function getAverageAge(users) { const sum = users.reduce((prev, user) => prev + user.age, 0); return users.length ? Math.round((sum / users.length) * 10) / 10 : 0; }

3. 数据缺失防护(可选)

如果数组中可能存在没有age属性的对象,user.age会是undefined,参与加法后产生NaN。可在回调内兜底:(prev, user) => prev + (user.age || 0),但这属于对脏数据的宽容处理,需要依据业务确定语义。

替代实现与选型对比

reduce不是唯一答案,理解其它写法有助于在不同场景下选择:

// for..of 循环:可读性最高,适合需要中途 break 的场景 function getAverageAge(users) { let sum = 0; for (const user of users) sum += user.age; return users.length ? sum / users.length : 0; } // map + reduce:把“取数”与“折叠”显式分离 function getAverageAge(users) { const ages = users.map(user => user.age); // 先取出所有年龄 return ages.reduce((prev, age) => prev + age, 0) / users.length; } // forEach:需要额外维护外部变量 function getAverageAge(users) { let sum = 0; users.forEach(user => { sum += user.age; }); return sum / users.length; }

选型建议:

  • 数据量小、追求一行式表达:reduce最合适,仓库标准答案即此方案;
  • 需要提前退出(如检测到异常数据就停止):for..of配合break更自然,reduce无法中途中断;
  • 强调关注点分离、便于阅读:map+reduce把“抽取字段”和“聚合数值”拆成两步,语义更直白。

结语

「Get average age」这道题虽短,却完整覆盖了reduce的核心知识面:累加器参数、初始值语义、空数组陷阱、除零防护,以及与for..of、map的取舍。在 javascript.info 的练习体系中,它与 12-reduce-object、sum-salaries 共同构成了“数组/对象聚合计算”的完整训练闭环。掌握这一模式后,求和、平均值、最大值、分组、计数等日常数据处理任务都可以用同样简洁的reduce表达式完成。

  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载
上一篇:react-admin 接入 Create React App:从脚手架初始化到应用热更新检测的完整指南
下一篇:Teleport Operator `crdgen` 调试指南:从 dump `protoc` 请求到断点调试的完整方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 2:30:59

Go MongoDB 实战:go-mongo-driver 落地大型项目全攻略

Go MongoDB 实战:go-mongo-driver 落地大型项目全攻略MongoDB 是 Go 后端常用 NoSQL。本文从连接池、调优到 transaction 实战,再到企业生产规范,一次打包。一、driver 安装 go get go.mongodb.org/mongo-driver/mongo连接: clien…

作者头像 李华
网站建设 2026/10/3 2:30:32

【C++】初始化列表

目录 1. 含义 和 基本语法 2. 为什么使用初始化列表? 2.1 使用初始化列表,效率更高 2.2 有些成员必须使用初始化列表 3.初始化顺序 4. 给缺省值初始化 5. 与在函数体内赋值的区别 1. 含义 和 基本语法 含义: 之前学过的构造函数初始化…

作者头像 李华
网站建设 2026/10/3 2:29:53

千古前端图文教程:JavaScript 递归函数完全指南——概念、递归要素与三大经典案例(阶乘 / 喇叭花数 / 斐波那契)

文档教程前端 【免费下载链接】Web 千古前端图文教程,超详细的前端入门到进阶知识库。从零开始学前端,做一名精致优雅的前端工程师。 项目地址: https://gitcode.com/gh_mirrors/we/Web 点击查看 免费下载 导读 递归(Recursion&…

作者头像 李华