各位观众,各位朋友,早上好(或者下午好,或者晚上好,取决于你们什么时候看)。我是你们今天的JS数组专家,今天咱们来聊聊JS数组的非破坏性方法:map
、filter
和 reduce
。别害怕,虽然听起来有点学术,但保证让你们听得进去,学得会,用得上。
啥叫非破坏性方法?
首先,咱们得搞清楚啥叫“非破坏性”。简单来说,就是这些方法在处理数组的时候,不会直接修改原数组,而是返回一个新的数组。这就像你复印一份文件,然后在复印件上涂涂改改,原件还是干干净净的。
map
:变形金刚,数组元素的华丽变身
map
方法的作用是对数组中的每个元素应用一个函数,然后返回一个包含所有结果的新数组。你可以把它想象成一个变形金刚,每个元素进去,经过一番操作,变成另外一个样子出来。
-
基本用法:
const numbers = [1, 2, 3, 4, 5]; const doubledNumbers = numbers.map(number => number * 2); console.log(doubledNumbers); // 输出: [2, 4, 6, 8, 10] console.log(numbers); // 输出: [1, 2, 3, 4, 5] (原数组没变)
这里,我们把数组
numbers
里的每个数字都乘以 2,生成了一个新的数组doubledNumbers
。注意,numbers
数组本身并没有改变。 -
进阶用法:提取对象数组的特定属性
假设我们有一个对象数组,每个对象都有
name
和age
属性,我们想提取所有人的名字到一个新的数组里。const people = [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 35 } ]; const names = people.map(person => person.name); console.log(names); // 输出: ['Alice', 'Bob', 'Charlie']
这简直太方便了!不用写循环,一行代码搞定。
-
更高级的用法:结合索引进行转换
map
方法还可以接收第二个参数,表示当前元素的索引。这在某些情况下非常有用。const numbers = [10, 20, 30]; const indexedValues = numbers.map((number, index) => `Value at index ${index} is ${number}`); console.log(indexedValues); // 输出: ["Value at index 0 is 10", "Value at index 1 is 20", "Value at index 2 is 30"]
我们用索引和值,构建了一个更有意义的字符串数组。
filter
:筛选器,留下你想要的,剔除你不要的
filter
方法的作用是根据指定的条件,筛选数组中的元素,返回一个包含所有符合条件的元素的新数组。你可以把它想象成一个筛选器,只留下你想要的,剔除你不要的。
-
基本用法:
const numbers = [1, 2, 3, 4, 5, 6]; const evenNumbers = numbers.filter(number => number % 2 === 0); console.log(evenNumbers); // 输出: [2, 4, 6] console.log(numbers); // 输出: [1, 2, 3, 4, 5, 6] (原数组没变)
我们筛选出了数组
numbers
中的所有偶数。 -
进阶用法:筛选对象数组
继续用上面的
people
数组,这次我们想筛选出所有年龄大于 28 岁的人。const people = [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 35 } ]; const olderPeople = people.filter(person => person.age > 28); console.log(olderPeople); // 输出: [{ name: 'Alice', age: 30 }, { name: 'Charlie', age: 35 }]
筛选条件可以是任何你想要的逻辑。
-
更高级的用法:基于字符串的筛选
假设我们有一个字符串数组,我们想筛选出所有包含特定字符的字符串。
const fruits = ['apple', 'banana', 'orange', 'grape']; const fruitsWithA = fruits.filter(fruit => fruit.includes('a')); console.log(fruitsWithA); // 输出: ["apple", "banana", "orange", "grape"]
字符串的
includes
方法可以帮助我们判断字符串是否包含某个字符。
reduce
:累加器,化零为整的终极武器
reduce
方法的作用是对数组中的每个元素执行一个“reducer”函数(你可以理解为累加器),最终将数组归约为一个单一的值。你可以把它想象成一个累加器,把数组里的所有东西累加起来,得到一个最终的结果。
-
基本用法:求和
const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); console.log(sum); // 输出: 15 console.log(numbers); // 输出: [1, 2, 3, 4, 5] (原数组没变)
这里,
accumulator
是累加器,currentValue
是当前元素。0
是初始值。reduce
方法会遍历数组,每次都把accumulator
和currentValue
加起来,最终得到总和。 -
进阶用法:连接字符串
const words = ['Hello', ' ', 'World', '!']; const sentence = words.reduce((accumulator, currentValue) => accumulator + currentValue, ''); console.log(sentence); // 输出: "Hello World!"
我们把字符串数组连接成了一个完整的句子。
-
更高级的用法:统计元素出现次数
假设我们有一个数组,里面包含各种元素,我们想统计每个元素出现的次数。
const names = ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob', 'Bob']; const nameCounts = names.reduce((accumulator, currentValue) => { if (accumulator[currentValue]) { accumulator[currentValue]++; } else { accumulator[currentValue] = 1; } return accumulator; }, {}); // 初始值是一个空对象 console.log(nameCounts); // 输出: { Alice: 2, Bob: 3, Charlie: 1 }
这里,我们用一个对象来存储每个元素的计数。
accumulator
就是这个对象。每次遍历到一个元素,我们就判断它是否已经存在于accumulator
中。如果存在,就增加计数;如果不存在,就初始化计数为 1。 -
再高级一点:扁平化数组(二维数组变成一维数组)
const nestedArray = [[1, 2], [3, 4], [5]]; const flattenedArray = nestedArray.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5]
我们使用
concat
方法将每个子数组连接到累加器中。
map
, filter
, reduce
的组合拳:威力加倍
这三个方法可以组合起来使用,形成强大的数据处理流水线。
-
例子:筛选出年龄大于 28 岁的人的名字,并转换成大写
const people = [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 35 } ]; const olderPeopleNames = people .filter(person => person.age > 28) .map(person => person.name.toUpperCase()); console.log(olderPeopleNames); // 输出: ["ALICE", "CHARLIE"]
我们先用
filter
筛选出年龄大于 28 岁的人,然后用map
提取他们的名字,并转换成大写。
总结:表格对比
为了更好地理解这三个方法,我们用一个表格来总结一下:
方法 | 作用 | 返回值类型 | 是否修改原数组 | 适用场景 |
---|---|---|---|---|
map |
对数组中的每个元素应用一个函数,返回一个包含所有结果的新数组 | 数组 | 否 | 数组元素的转换和映射,提取对象数组的特定属性 |
filter |
根据指定的条件,筛选数组中的元素,返回一个包含所有符合条件的元素的新数组 | 数组 | 否 | 数组元素的筛选和过滤,根据特定条件选择元素 |
reduce |
对数组中的每个元素执行一个“reducer”函数,最终将数组归约为一个单一的值 | 单一值 | 否 | 数组元素的累加、统计、分组,以及将数组转换为其他数据结构(例如对象) |
注意事项:
- 这些方法都是非破坏性的,所以要记得把结果赋值给一个新的变量。
reduce
方法需要一个初始值,这个初始值的类型会影响最终结果的类型。- 组合使用这些方法的时候,要注意顺序,不同的顺序可能会导致不同的结果。
结束语:
map
、filter
和 reduce
是 JS 数组处理的利器。掌握它们,可以让你写出更简洁、更高效的代码。希望今天的讲解对大家有所帮助。记住,编程是一门实践的艺术,多写代码,多尝试,才能真正掌握这些方法。下次有机会,我们再聊点更高级的玩法! 祝大家编程愉快!