ES6 数组遍历方法详解:forEach、map、filter、sort、reduce 与 every
本文深入解析 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) // 8612. map
基本语法如下:
let newArr = map((element, index, array) => { /* … */ })
// return new arraymap 方法与 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 copyfilter 是另一个语法与 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)返回值 | 排序顺序 |
|---|---|
| > 0 | a在b后 |
| < 0 | a在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第一眼看到上述代码时,你可能会感到困惑。下面的表格可以帮助你理解其执行过程:
| 回调次数 | previousValue | currentValue | currentIndex | return value | |
|---|---|---|---|---|---|
| 第一轮遍历 | 1 | 4 | 1 | 5 | |
| 第二轮遍历 | 5 | 98 | 2 | 103 | |
| 第三轮遍历 | 103 | 170 | 3 | 273 | |
| 第四轮遍历 | 273 | 35 | 4 | 308 |
我们的数组共有六个元素,因此将一共遍历六次。上述示例展示了前四轮的过程及结果。下面有另一个示例,展示如何使用 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 数组遍历内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
