首先做一个粗体声明:循环经常是无用的,并且使得代码很难阅读。
当谈到迭代一个数组的时候,无论你想去查找元素,排序或者任何其他的事,都有可能存在一个数组的方法供你使用。
然而,尽管它们有用,但其中一些仍然不被人了解。我会努力为你展示一些有用的方法。把这篇文章当做对 JavaScript 数组方法的指引吧。
注意: 在开始之前,不得不了解一件事:我比较偏爱函数式编程。所以我倾向于使用的方法不会直接改变原来的数组。这种方法,我避免了副作用。我不是说不应该改变数组,但至少要了解那些方法会改变,那些会有副作用。副作用导致不想要的改变,而不想要的改变带来bugs!
了解到这里,我们可以开始正文了。
必不可少的
当跟数组打交道时,有四件事你应该清楚:map,filter,reduce 和 展开操作符。它们富有力量。
map
你可以在很多种情况下使用它。基本地,每次你需要修改数组的元素时,考虑使用 map。
它接受一个参数:一个方法,在每一个数组元素上调用。然后返回一个新的数组,所以没有副作用。
const numbers = [1, 2, 3, 4] const numbersPlusOne = numbers.map(n => n + 1) // 每个元素 +1 console.log(numbersPlusOne) // [2, 3, 4, 5]
你也能创建一个新数组,用于保留对象的一个特殊属性:
const allActivities = [ { title: 'My activity', coordinates: [50.123, 3.291] }, { title: 'Another activity', coordinates: [1.238, 4.292] }, // etc. ] const allCoordinates = allActivities.map(activity => activity.coordinates) console.log(allCoordinates) // [[50.123, 3.291], [1.238, 4.292]]
所以,请记住,当你需要去转换数组时,考虑使用map。
filter
这个方法的名字在这里十分准确的:当你想去过滤数组的时候使用它。
如同map所做,它接受一个函数作为它的唯一参数,在数组的每个元素上调用。这个方法返回一个布尔值:
true 如果你需要在数组中保留元素
false 如果你不想保留它
接着你会得到一个带有你想要保留的元素的新数组。
举个例子,你可以在数组中只保留奇数:
const numbers = [1, 2, 3, 4, 5, 6] const oddNumbers = numbers.filter(n => n % 2 !== 0) console.log(oddNumbers) // [1, 3, 5]
或者你可以在数组中移除特殊的项:
const participants = [ { id: 'a3f47', username: 'john' }, { id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }, ] function removeParticipant(participants, id) { return participants.filter(participant => participant.id !== id) } console.log(removeParticipant(participants, 'a3f47')) // [{ id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }];
reduce
个人认为是最难理解的方法。但是如果你一旦掌握它,很多疯狂的事情你都可以用它做到。
基本地, reduce 使用有值的数组然后组合成一个新的值。它接受两个参数,一个回调方法就是我们的 reducer 和一个可选的初始化的值(默认是数组的第一个项)。这个 reducer 自己使用四个参数:
累计:在你的 reducer 中累积的返回值
当前数组的值
当前索引
当前调用 reduce 的数组
大多数时候,你只需要使用前两个参数:累计值和当前值。
抛开这些理论。来看看常见的一个 reduce 的例子。
const numbers = [37, 12, 28, 4, 9] const total = numbers.reduce((total, n) => total + n) console.log(total) // 90
在第一个遍历时,这个累计值,也就是 total,使用了初始化为 37 的值。它返回的值是 37 + n 并且 n 等于 12,因此得到 49.在第二次遍历时,累加值是 49,返回值是 49 + 28 = 77。如此继续直到第四次。
reduce 是很强大的,你可以实际使用它去构建很多数组的方法,比如 map 或者 filter:
const map = (arr, fn) => { return arr.reduce((mappedArr, element) => { return [...mappedArr, fn(element)] }, []) } console.log(map([1, 2, 3, 4], n => n + 1)) // [2, 3, 4, 5] const filter = (arr, fn) => { return arr.reduce((filteredArr, element) => { return fn(element) ? [...filteredArr] : [...filteredArr, element] }, []) } console.log(filter([1, 2, 3, 4, 5, 6], n => n % 2 === 0)) // [1, 3, 5]
根本上看,我们给 reduce 一个初始默认值 []:我们的累计值。对于 map,我们运行一个方法,它的结果是累加到最后,多亏了 展开操作符(不必担心,后面讨论)。对于 filter,几乎是相似的,除了我们在元素上运行过滤函数。如果返回 true,我们返回前一个数组,否则在数组最后添加当前元素。
我们来看一个更高级的例子:深度展开数组,也就是说把 [1, 2, 3, [4, [[[5, [6, 7]]]], 8]] 样的数组转换成 [1, 2, 3, 4, 5, 6, 7, 8] 样的。