首页 > 开发 > JS > 正文

关于JavaScript 数组你应该知道的事情(推荐)

2024-05-06 16:49:46
字体:
来源:转载
供稿:网友

首先做一个粗体声明:循环经常是无用的,并且使得代码很难阅读。
当谈到迭代一个数组的时候,无论你想去查找元素,排序或者任何其他的事,都有可能存在一个数组的方法供你使用。

然而,尽管它们有用,但其中一些仍然不被人了解。我会努力为你展示一些有用的方法。把这篇文章当做对 JavaScript 数组方法的指引吧。

注意: 在开始之前,不得不了解一件事:我比较偏爱函数式编程。所以我倾向于使用的方法不会直接改变原来的数组。这种方法,我避免了副作用。我不是说不应该改变数组,但至少要了解那些方法会改变,那些会有副作用。副作用导致不想要的改变,而不想要的改变带来bugs!

了解到这里,我们可以开始正文了。

必不可少的
当跟数组打交道时,有四件事你应该清楚:map,filter,reduce 和 展开操作符。它们富有力量。

map

你可以在很多种情况下使用它。基本地,每次你需要修改数组的元素时,考虑使用 map。
它接受一个参数:一个方法,在每一个数组元素上调用。然后返回一个新的数组,所以没有副作用。

const numbers = [1, 2, 3, 4]const numbersPlusOne = numbers.map(n => n + 1) // 每个元素 +1console.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所做,它接受一个函数作为它的唯一参数,在数组的每个元素上调用。这个方法返回一个布尔值:

  1. true 如果你需要在数组中保留元素
  2. 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 自己使用四个参数:

  1. 累计:在你的 reducer 中累积的返回值
  2. 当前数组的值
  3. 当前索引
  4. 当前调用 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] 样的。

function flatDeep(arr) { return arr.reduce((flattenArray, element) => {  return Array.isArray(element)   ? [...flattenArray, ...flatDeep(element)]   : [...flattenArray, element] }, [])}console.log(flatDeep([1, 2, 3, [4, [[[5, [6, 7]]]], 8]])) // [1, 2, 3, 4, 5, 6, 7, 8]

这个例子有点像 map,除了我们用到了递归。我不想去解释这个用法,它超出了这篇文章的范围。但是,如果你想了解更多的关于递归的知识,请参考这篇优质的文章。

展开操作(ES2015)

我知道这不是一个方法。但是,在处理数组时,使用展开操作可以帮助你做很多事情。事实上,你可以在另一个数组中使用它展开一个数组的值。从这一点来说,你可以复制一个数组,或者连接多个数组。

const numbers = [1, 2, 3]const numbersCopy = [...numbers]console.log(numbersCopy) // [1, 2, 3]const otherNumbers = [4, 5, 6]const numbersConcatenated = [...numbers, ...otherNumbers]console.log(numbersConcatenated) // [1, 2, 3, 4, 5, 6]

注意::展开操作符对原数组做了一次浅拷贝。但什么是 浅拷贝?
注:相关教程知识阅读请移步到JavaScript/Ajax教程频道。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表