ES6 数组遍历方法详解:forEach、map、filter、sort、reduce 与 every

作者:拾光

时间:2026-10-30

浏览:625

本文深入解析 ES6 中常见的数组遍历方法,包括 forEach、map、filter、sort、reduce 和 every。通过实际代码案例,详细说明各方法的语法结构、参数含义、返回值特性及适用场景,帮助开发者在业务开发中高效使用这些数组处理工具。

文章目录

在 ES6 语法规范中,数组类型新增了一系列与遍历操作相关的方法。尽管从理论层面看,这些方法属于语法层面的便利封装,不使用它们依然可以完成编码任务,但它们的引入极大地简化了业务逻辑的处理流程。因此,在实际开发中熟练掌握这些方法是必不可少的。对于初次接触这些方法的开发者而言,理解其工作原理可能存在一定难度,本文将通过具体的实际案例,详细解析它们的语法结构与使用方式。

所有数组遍历方法在参数接收上具有共同点:均接收一个回调函数作为核心参数。

在上述所有回调函数内部,所涉及的参数均为形参。以 forEach 为例,参数名不必严格固定为 element、index 或 array。在实际使用中,可以使用任意自定义参数名来代表这些值,只要保证参数传递的顺序正确即可。

1. forEach

基本语法如下:

forEach((element, index, array) => { /* … */ }) // return undefined

其中,element 代表数组中的每一个元素,index 代表对应元素的索引值,array 代表数组本身。但如果通过 arr.forEach() 这种方式调用,第三个参数 array 通常是不需要的。需要注意的是,forEach 方法本身没有返回值。

首先介绍的是最容易理解且使用频率极高的数组方法:forEach。它基本上可以视为 for 循环的替代方案,最适合用于遍历数组,同时也适用于遍历其他可迭代对象(如 nodelist、Map 和 Set)。该方法本身不返回任何值,仅根据数据数量执行循环操作。

forEach 的一个常见应用场景是遍历 nodeList(节点列表),以便对 DOM 中的多个对象执行统一操作。请参考以下示例代码。

         const inventors = [
            { first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
            { first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
            { first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
            { first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
            { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
            { first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
            { first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
            { first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
            { first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
            { first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
            { first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
            { first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
        ];
        // 选择dom元素
        const list = document.querySelector('.list')
        // 对数组进行遍历,数组中每有一个元素就添加一个dom对象
        inventors.forEach(function(inventor, index) {
            const listItem = document.createElement('li')
            listItem.textContent = `${index}: ${inventor.first},
            born ${inventor.year}`
            list.appendChild(listItem)
        })
        // 箭头函数写法:
        inventors.forEach((inventor, index) => {
            const listItem = document.createElement('li')
            listItem.textContent = `${index}: ${inventor.first},
            born ${inventor.year}`
            list.appendChild(listItem)
        })

以下是另外两个 forEach 的使用场景示例:

    <button class="div">click me</button>
    <button class="div">click me</button>
    <button class="div">click me</button>
    <button class="div">click me</button>
    <button class="div">click me</button>        
      <script>
        // 获取所有button,赋予他们新的内容,并且绑定事件
        const buttons = document.querySelectorAll('.div')
        buttons.forEach((button, index) => {
            button.textContent = '我是一个按钮'
            button.addEventListener('click', () => {
                console.log('我是一个按钮, 并且我的缩印是' + index)
            })
        })
     </script>
        // 根据刚才的inventors数组,计算所有科学家生命总长度    
        let totalYearsLived = 0
        inventors.forEach(function(inventor) {
            let yearLived = inventor.passed - inventor.year
            totalYearsLived += yearLived
        })
        console.log(totalYearsLived) // 861

2. map

基本语法如下:

let newArr = map((element, index, array) => { /* … */ })
// return new array

map 方法与 forEach 类似,但最大的区别在于它会返回一个全新的数组,且不会修改原数组。element、index 和 array 的含义与 forEach 中相同。

下面两个示例展示了 map 的基本用法:

        const inventors = [
            { first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
            { first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
            { first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
            { first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
            { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
            { first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
            { first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
            { first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
            { first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
            { first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
            { first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
            { first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
        ];
        // 得到所有inventor的全名,并将他们组成一个新的数组
        let fullnameArr = inventors.map(function(inventor) {
            return inventor.first + ' ' + inventor.last
        })
        // 箭头函数写法
        let fullnameArr = inventors.map(inventor => inventor.first + ' ' +   inventor.last)
        const numArr = [1, 4, 98, 170, 35, 87]
        // 得到numArr中每一个数字的2倍
        let doubleNumArr = numArr.map(num => num*2)
        console.log(doubleNumArr) //  [2, 8, 196, 340, 70, 174]

3. filter

基本语法如下:

filter((element, index, array) => { /* … */ } )
// return shallow copy

filter 是另一个语法与 map 和 forEach 相似的数组方法。filter 的中文含义是“过滤”,特别适用于从数组中提取符合特定条件的元素。每轮遍历返回一个布尔值,结果为 true 的元素会被添加到新数组中,最终 filter 返回这个新数组。

filter 是我认为第二常用且极其实用的数组方法。因为在很多场景中,我们需要根据条件筛选部分数组元素,此时使用 filter 会非常便捷。对于新手来说,最大的难点在于理解回调函数中的 return 值是一个布尔值,该布尔值通常以表达式的形式出现。然而,filter 最终返回的是一个数组的浅拷贝。简而言之,如果修改浅拷贝中元素的值,也会影响原数组中对应元素的值。

        const inventors = [
            { first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
            { first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
            { first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
            { first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
            { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
            { first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
            { first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
            { first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
            { first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
            { first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
            { first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
            { first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
        ];
        // 获取所有出生在1500年和1600年之间的科学家
        const fifteen = inventors.filter(function(inventor) {
            return inventor.year >= 1500 && inventor.year < 1600
        })
        // 箭头函数写法
        const fifteen = inventors.filter(inventor => inventor.year >= 1500 && inventor.year < 1600)
        console.log(fifteen)
        // 例子2:获取所有名字中有字母a的科学家
        const nameHasA = inventors.filter(function(inventor) {
           return inventor.last.includes('a')
        })
        // 箭头函数写法
        const nameHasA = inventors.filter(inventor => inventor.last.includes('a'))
        console.log(nameHasA)

4. sort

基本语法如下:

sort()
sort((a, b) => { /* … */ } ) 
// 返回sort后被改变的原数组

sort 的语法与前几种方法有所不同。你可以直接调用它而不传任何参数,也可以传入一个用于比较的回调函数。sort 的用途是对数组元素进行排序,其中的 a 和 b 分别指代用于比较的第一个和第二个元素。该方法返回排序后的原数组。

如果没有提供比较函数,sort 将会按照元素的 Unicode 编码位点进行排序。这意味着:'Banana' 会排在 'Cat' 之前,因为 'b' 的编码比 'c' 靠前;同样,110 会排在 20 之前,因为 '1' 的编码比 '2' 靠前。请参考以下两个示例。

        const numArr = [1, 4, 98, 170, 35, 87]
        // 纯数字排序
        const sortNoParams = numArr.sort()
        console.log(sortNoParams) // [1, 170, 35, 4, 87, 98]
        const nameArr = ['Hanna', 'Meitner', 'Blodgett', 'Nicolaus', 'Einstein']
        // 字符串排序
        const newNameArr = nameArr.sort()
        console.log(newNameArr) // ['Blodgett', 'Einstein', 'Hanna', 'Meitner', 'Nicolaus']

但是,如果你在 sort 中传入一个比较函数,那么 sort 将会按照 a 和 b 的实际大小进行排序,基本规则如下:

比较函数(a, b)返回值排序顺序
> 0a在b后
< 0a在b前
=== 0保持a和b的顺序

简而言之,比较函数每次执行需要返回一个数字。如果返回值大于 0,数组按升序排序;如果小于 0,则按降序排序;如果等于 0,则保持原有顺序不变。传统上,我们通常返回 1 和 -1 来分别代表大于 0 和小于 0 的情况。使用方式如下:

function compareFn(a, b) {
  if (在某些排序规则中,a 小于 b) {
    return -1;
  }
  if (在这一排序规则下,a 大于 b) {
    return 1;
  }
  // a 一定等于 b
  return 0;
}
通常我们把返回值写为1和-1来代表返回值是否大于0
但如果是纯数字之间进行比较,那么以上函数可以简写:
function compareFn(a, b){
    return a - b
}

这次,我们再次使用之前的数字数组进行排序,但这次使用回调比较函数。结果将按照数字的实际大小进行排序:

        const numArr = [1, 4, 98, 170, 35, 87]
        const sortedArr = numArr.sort(function(a, b) {
            return a - b
        })
        // 箭头函数写法
        const sortedArr = numArr.sort((a, b) => a - b)
        console.log(sortedArr) // [1, 4, 35, 87, 98, 170]
        const nameArr = ['Hanna', 'Meitner', 'Blodgett', 'Nicolaus', 'Einstein']
        const sortName = nameArr.sort((a, b) => {
            if(a < b) {
                return -1
            } else if(a > b) {
                return 1
            }
            return 0
        })
        console.log(sortName) //  ['Blodgett', 'Einstein', 'Hanna', 'Meitner', 'Nicolaus']

5. reduce

基本语法如下:

reduce((previousValue, currentValue, currentIndex, array) => { /* … */ }
, initialValue)
// 返回一个类型不一定的结果

reduce 可以说是数组方法中最难理解、也最特殊的一个函数。

reduce 同样接受一个回调函数和一个可选的初始值作为参数。该回调函数可以接收 4 个参数,分别是:上一个值(累加器总值)、当前值、当前索引和数组。reduce 有一个可选的第二个参数,用于指定初始值。如果不填写初始值,遍历将从索引 1 开始(即数组的第二个元素),因为第一个元素的初始值不存在,当前索引的值也将为 1。如果填写了初始值,那么第一轮遍历的当前索引为 0,当前元素为一个数组元素,而“上一个元素”的值即为初始值。最终返回回调函数遍历整个数组后的结果。

reduce 并不是一个特别常用的数组方法,但在某些特定情况下,它可以大大减少代码的复杂程度。我们将通过多个示例来展示 reduce 的一些实际用途。

        const numArr = [1, 4, 98, 170, 35, 87]
        // 得到numArr的元素的和
        let sum = numArr.reduce((previous, current) => {
            return previous + current
        })
        console.log(sum) // 395   
        // 如果最初值存在的话
        let withInitial = numArr.reduce((previous, current) => {
            return previous + current
        }, 100)
        console.log(withInitial) // 495

第一眼看到上述代码时,你可能会感到困惑。下面的表格可以帮助你理解其执行过程:

回调次数previousValuecurrentValuecurrentIndexreturn value
第一轮遍历1415
第二轮遍历5982103
第三轮遍历1031703273
第四轮遍历273354308

我们的数组共有六个元素,因此将一共遍历六次。上述示例展示了前四轮的过程及结果。下面有另一个示例,展示如何使用 reduce 来集合对象数组中的值:

        let shoppingCart = [
            {
                product: 'phone',
                qty: 1,
                price: 500,
            },
            {
                product: 'Screen Protector',
                qty: 1,
                price: 10,
            },
            {
                product: 'Memory Card',
                qty: 2,
                price: 20,
            },
        ];
        // 计算购物车内的价格总和
        let total = shoppingCart.reduce(function (previousValue, currentValue) {
            return previousValue + currentValue.qty * currentValue.price;
        }, 0);
        console.log(total) // 550

以上是 reduce 的两个比较基础的用法。你也可以将其应用在一些更复杂的场景中。还有一个比较常见的场景是将数组中相同种类的对象归为一类,形成一个新的数组。

const people = [
  { name: 'Kyle', age: 26 },
  { name: 'John', age: 31 },
  { name: 'Sally', age: 42 },
  { name: 'Jill', age: 42 },
]
// 把年龄相同的人分为一组
const peopleGroupedByAge = people.reduce((groupedPeople, person) => {
  const age = person.age
  // 首先给每个存在的年龄段创建一个空数组
  // 然后用push将每个元素放入相应的数组
  if (groupedPeople[age] == null) groupedPeople[age] = []
  groupedPeople[age].push(person)
  return groupedPeople
    }, {}) // 初始值是一个空对象,这样才能使用增加属性
    console.log(peopleGroupedByAge)
/*
  {
    26: [{ name: 'Kyle', age: 26 }],
    31: [{ name: 'John', age: 31 }],
    42: [
      { name: 'Sally', age: 42 },
      { name: 'Jill', age: 42 }
    ]
  }
*/

6. every

基本语法如下:

every((element, index, array) => { /* … */ } ) // return boolean

最后,我们以一个相对简单的函数收尾:every()。every 方法用于检查数组中是否所有元素都满足指定条件。如果有任何一个元素不满足条件,回调函数将返回 false。如果所有遍历结果均返回 true,那么 every 最终返回 true。

every 自身的概念很好理解,我们用一个示例来阐述其作用:

        const inventors = [
            { first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
            { first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
            { first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
            { first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
            { first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
            { first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
            { first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
            { first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
            { first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
            { first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
            { first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
            { first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
        ];
        // 检查是不是所有发明家的last name都至少4个字母并且出生于1400年以后
        let checkInventors = inventors.every(inventor => {
            return inventor.last.length >= 4 && inventor.year>1400
        })
        console.log(checkInventors) // true

最后只有一个需要额外注意的是,今天所讲解的所有数组方法,都不会遍历空的数组元素。

console.log([1, , 3].every((x) => x !== undefined)); // true
console.log([2, , 2].every((x) => x === 2)); // true

以上就是 ES6 中常见的数组遍历方式。掌握它们对业务开发至关重要,需要在合适的情况下多多练习。

到此这篇关于 JS 中的常见数组遍历方法详解(forEach, map, filter, sort, reduce, every)的文章就介绍到这了,更多相关 js 数组遍历内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章