01 · NOTES / WebGIS 开发
第 13 节 · 数组高阶方法
2026 年 9 月 1 日
第 13 节 · 数组高阶方法
📌 版本信息:基于 ECMAScript 2025(map/filter/reduce 等为基础语义,多年稳定;2026-08-29 核对) 📚 来源:现代 JS 教程 · 数组方法 | MDN Array | 本地教材
resources/01-web-basics/modern-js-tutorial/1-js/
一、这一节的目标
- 精通三巨头:
map/filter/reduce,理解"回调函数当参数"的用法 - 掌握
find/some/every/includes的适用场景 - 会链式调用:像流水线一样处理一批数据
- 建立 GIS 核心工作流直觉:拿到一个数组(比如地震列表)→ 筛 → 变形 → 汇总
二、为什么需要高阶方法
第 12 节处理数据时你写的是裸 for 循环——能用,但"要什么"淹没在"怎么循环"里。高阶方法把意图直接写出来:
// 需求:从震级列表里拿到所有 ≥5 的
const mags = [3.2, 5.1, 4.8, 6.0, 2.9];
// 裸循环版:程序在说"怎么循环、怎么判断、怎么塞"
const result1 = [];
for (const m of mags) {
if (m >= 5) result1.push(m);
}
// filter 版:一句话说出"要什么"
const result2 = mags.filter((m) => m >= 5); // [5.1, 6.0]
本质:这些方法接收一个回调函数(第 11 节:函数是值,可以当参数传),数组自己负责循环,你只负责"判断/变形规则"。
三、三巨头详解
① map:一一变形(数量不变,样子变)
const quakes = [
{ place: '武汉', mag: 5.1 },
{ place: '西安', mag: 3.2 },
];
// 把对象数组变成"描述字符串"数组
const labels = quakes.map((q) => `${q.place} ${q.mag} 级`);
// ['武汉 5.1 级', '西安 3.2 级']
// 给每个对象加字段(返回新对象,不改原数据)
const labeled = quakes.map((q) => ({ ...q, level: q.mag >= 5 ? '大震' : '小震' }));
口诀:map 是"加工流水线",进去 N 个,出来 N 个,每个都被加工过。
② filter:筛选(样子不变,数量变少)
const strong = quakes.filter((q) => q.mag >= 5); // 只留大震
const notWuhan = quakes.filter((q) => q.place !== '武汉');
口诀:filter 是"安检门",回调返回 true 的放行,false 的拦下。
③ reduce:汇总(N 个进,1 个出)
const mags = [3.2, 5.1, 4.8, 6.0];
// 求平均震级
const total = mags.reduce((sum, m) => sum + m, 0); // 19.1(0 是初始值)
const avg = total / mags.length;
// 进阶:一次遍历同时统计(GIS 统计面板的雏形)
const stats = mags.reduce(
(acc, m) => {
acc.count += 1;
acc.total += m;
acc.max = Math.max(acc.max, m);
return acc; // ⚠️ 别忘了把 acc 返回给下一轮
},
{ count: 0, total: 0, max: 0 } // 初始值(汇总结果的形状)
);
// { count: 4, total: 19.1, max: 6 }
reduce 是三巨头里最抽象的——初始值 + 逐轮累积。读别人的 reduce 时先找这两个东西。
四、其余高频方法速查
const list = [{ id: 1, name: '武汉' }, { id: 2, name: '西安' }];
list.find((x) => x.id === 2); // 找第一个满足的元素(返回元素本身或 undefined)
list.findIndex((x) => x.id === 2); // 它的下标
list.some((x) => x.id > 10); // 有一个满足吗?false
list.every((x) => x.id > 0); // 全部满足吗?true
list.includes('x'); // 原始值精确查找(数组内是对象时用 find)
[1, 2, 3].at(-1); // 3(at(-1) 取最后一个,现代写法)
[[1, 2], [3, 4]].flat(); // [1,2,3,4](拍平一层)
五、链式调用:GIS 数据处理的流水线
三个方法都返回新数组,所以能一路点下去:
const quakes = [
{ place: '武汉', mag: 5.1, depth: 10 },
{ place: '西安', mag: 3.2, depth: 18 },
{ place: '成都', mag: 6.0, depth: 12 },
{ place: '拉萨', mag: 4.4, depth: 8 },
];
// 需求:3 级以上的 → 按震级降序 → 拿前两名 → 变成描述串
const report = quakes
.filter((q) => q.mag >= 3) // 筛
.sort((a, b) => b.mag - a.mag) // 排(⚠️ sort 会改原数组,链头是 filter 的新数组所以安全)
.slice(0, 2) // 取前 2
.map((q) => `${q.place}(${q.mag}级,深 ${q.depth}km)`); // 变形
// ['成都(6级,深 12km)', '武汉(5.1级,深 10km)']
读链式代码的技巧:从上往下一行行读,每行的结果就是下一行的输入——和车间流水线一模一样。
⚠️ 两个规范:①链式里优先 filter 在前(先筛后算,性能好);②map 里必须 return(箭头函数省略花括号才有隐式 return,写了
{}就必须显式return)——漏 return 拿到一串 undefined 是新手高频 bug。
六、动手跟练:13 · 数据表格渲染
配套文件:02-web-basics/examples/13-数据表格渲染.html
步骤:
- Live Server 打开,F12 对照输出读代码
- 数据是一份迷你"USGS 地震 GeoJSON"(真数据的形状!)
- 完成 6 个 TODO:filter 筛强震、map 生成表格行、reduce 统计面板、链式组合、按震级排序渲染、把表格真的渲染到页面
- 这一节是 C-02b(地震数据面板)的直接前奏——这里的 TODO 6 就是那个综合项目的核心代码
通关标准:
- 6 个 TODO 全部完成且表格正确渲染
- 不查资料写出"筛 → 排 → 取前三 → 拼字符串"的链
- 能解释 map 回调里
{}和()包住对象的区别(=> ({...})与=> {...})
七、自测题
- map 和 filter 的输入输出数量关系分别是什么?
- reduce 的两个关键组成是什么?漏写初始值会怎样?
xs.filter(...).sort(...)为什么不用担心 sort 改坏原数组?xs.map((x) => { name: x.name })有什么 bug?怎么修?- 链式处理中为什么建议 filter 放最前面?
参考答案
- map:N 进 N 出(逐个变形);filter:N 进 ≤N 出(筛掉一部分)。
- 初始值 + 累积回调(每轮返回累积结果)。漏初始值时第一轮把前两个元素当初始值和首项,遇空数组直接报错,遇对象数组几乎必错。
- filter 返回的是新数组,sort 改的是这个新数组,原件无恙。
{}被箭头函数当成"函数体"而非"对象字面量",里面的 name: 被当标签语句,返回 undefined。要写=> ({ name: x.name })用圆括号包住对象。- 先筛先减量,后续 sort/map/reduce 处理的元素更少,性能更好,结果也一致。
八、下一步
数据会处理了,但结果还只在 Console 里 → 第 14 节:DOM 操作,把数据真正画到页面上。
TAGSweb