三个方法都以回调函数为参数,都不改原数组。下面用一份商品数据逐个点按钮观察。
| 名称 | 价格 | 在售 | 库存 |
|---|
黄色行是 onSale: true 的在售商品——后面 filter 用它筛选。
products.map(p => p.name) // ['键盘', '鼠标', …] 每个元素换成另一个值
products.map(p => `${p.name} ¥${p.price}`) // 元素变换成字符串
// 回调的完整签名:map((元素, 下标, 原数组) => 新值)
products.map((p, i) => (i + 1) + '. ' + p.name)
products.filter(p => p.onSale) // 回调返回真,就保留 products.filter(p => p.price < 300) // 返回的永远是数组:没筛到就是 [],不是 null // 判断「有没有结果」用 filtered.length > 0
const total = products.reduce((acc, p) => acc + p.price, 0)
// 形态:reduce((累计值, 当前元素) => 新的累计值, 初始值)
// 初始值决定结果类型:0 → 数字;[] → 数组;{} → 对象
const grouped = products.reduce((acc, p) => {
const key = p.onSale ? 'on' : 'off'
acc[key] = (acc[key] ?? 0) + 1
return acc
}, {}) // { on: 4, off: 1 }
reduce 是最容易写晕的一个:把「累计值」想成「到目前为止的结果」,每个元素负责把它更新一步。始终传初始值——空数组不传初始值会直接报错,是真实事故来源。
products.some(p => p.price > 1000) // true —— 有没有「至少一个」满足 products.every(p => p.stock > 0) // false —— 是不是「全部」满足(空数组恒为 true!) products.find(p => p.price < 300) // 第一个满足的「元素」,没有则 undefined
const names = products
.filter(p => p.onSale) // ① 先缩小范围
.sort((a, b) => a.price - b.price) // ② 再排序(先拷贝再排,见正文)
.map(p => `${p.name}(¥${p.price})`) // ③ 最后变换成展示文本
// 每步返回新数组,可以继续接下一个方法 —— 「数据管道」