every、some、filter、forEach、map、reduce方法异同

  every、some、filter、forEach、map、reduce方法都会对数组元素做遍历操作,但适用场景各有不同。

Array.prototype.every()

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

语法

1
arr.every(callback(element[, index[, array]])[, thisArg])

参数

callback
用来测试数组的每个元素的函数。它接收以下三个参数:
  element
  用于测试的当前值。
  index 可选
  当前值在数组中的索引。
  array 可选
  调用了方法的数组本身。
thisArg 可选
执行 callback 时,用于 this 的值。

返回值

如果回调函数的每一次返回都为 truthy 值,返回 true,否则返回 false。

示例

判断数组是否每个元素都大于10

1
2
3
4
5
6
let isBigEnough = element => {
return element > 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); //返回false
[12, 54, 18, 130, 44].every(isBigEnough); //返回true
//原数组不变

Array.prototype.some()

用法与参数与every()方法相同,只是判断条件不同。some() 方法测试数组中是否至少有1个元素通过了被提供的函数测试。它返回的是一个布尔值。
every()可理解为“与”,some()可理解为“或”。

示例

判断数组是否有大于10的元素

1
2
3
4
5
6
7
let isBigEnough = element => {
return element > 10;
}
[12, 5, 8, 130, 44].some(isBigEnough); //返回true
[12, 54, 18, 130, 44].some(isBigEnough); //返回true
[1, 5, 8, 3, 4].some(isBigEnough); //返回false
//原数组不变

将任意值转换为布尔值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const TRUTHY_VALUES = [true, 'true', 1];

let getBoolean = value => {
'use strict';

if (typeof value === 'string') {
value = value.toLowerCase().trim();
}

return TRUTHY_VALUES.some(t => {
return t === value;
});
}

getBoolean(false); // false
getBoolean('false'); // false
getBoolean(1); // true
getBoolean('true'); // true

Array.prototype.filter()

filter() 方法创建一个新数组, 过滤掉不符合条件的元素,返回通过所提供函数实现的测试方法的所有元素。

语法

1
var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

参数

callback
用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:
  element
  数组中当前正在处理的元素。
  index 可选
  正在处理的元素在数组中的索引。
  array 可选
  调用了 filter 的数组本身。
thisArg 可选
执行 callback 时,用于 this 的值。

返回值

一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

示例

用filter创建一个新数组,该数组元素由原数组中值大于10的元素组成。

1
2
3
4
5
6
7
let arr = [12, 5, 8, 130, 44];
let isBigEnough = element => {
return element >= 10;
}
let filtered = arr.filter(isBigEnough);
// filtered is [12, 130, 44]
// arr不变,还是 [12, 5, 8, 130, 44]

Array.prototype.forEach()

forEach() 方法对数组的每个元素执行一次给定的函数。

语法

1
arr.forEach(callback(currentValue [, index[, array]])[, thisArg])

参数

callback
为数组中每个元素执行的函数,该函数接收一至三个参数:
  currentValue
  数组中当前正在处理的元素。
  index 可选
  正在处理的元素在数组中的索引。
  array 可选
方法正在操作的数组。
thisArg 可选
执行 callback 时,用于 this 的值。

返回值

undefined.

1、forEach 不会直接改变调用它的对象,但是那个对象可能会被 callback 函数改变。
2、除了抛出异常以外,没有办法中止或跳出 forEach() 循环。如果你需要中止或跳出循环,forEach() 方法不是应当使用的工具。
3、forEach() 遍历的范围在第一次调用 callback 前就会确定。调用 forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach() 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()),之后的元素将被跳过。
4、forEach() 为每个数组元素执行一次 callback 函数;与 map() 或者 reduce() 不同的是,它总是返回 undefined 值,并且不可链式调用。

示例

1
2
3
4
5
6
7
8
9
let arraySparse = [1,3,,7];
let numCallbackRuns = 0;

arraySparse.forEach(function(element){
element + 1;
numCallbackRuns++;
});
//arraySparse === [1,3,empty,7],不变
//numCallbackRuns === 3

Array.prototype.map()

map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。

语法

1
2
3
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])

参数要求与forEach()相同。

返回值

一个由原数组每个元素执行回调函数的结果组成的新数组。

描述

1、因为map生成一个新数组,当你不打算使用返回的新数组却使用map是违背设计初衷的,请用forEach或者for-of替代。
2、根据规范中定义的算法,如果被map调用的数组是离散的,新数组将也是离散的保持相同的索引为空。

示例

下面的代码创建了一个新数组,值为原数组中对应数字的平方根。

1
2
3
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

下面的例子演示如何在一个 String 上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:

1
2
3
4
5
var map = Array.prototype.map
var a = map.call("Hello World", function(x) {
return x.charCodeAt(0);
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

Array.prototype.reduce()

reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Arrow function
reduce((previousValue, currentValue) => { /* ... */ } )
reduce((previousValue, currentValue, currentIndex) => { /* ... */ } )
reduce((previousValue, currentValue, currentIndex, array) => { /* ... */ } )
reduce((previousValue, currentValue, currentIndex, array) => { /* ... */ }, initialValue)

// Callback function
reduce(callback)
reduce(callback, initialValue)

// Inline callback function
reduce(function(previousValue, currentValue) { /* ... */ })
reduce(function(previousValue, currentValue, currentIndex) { /* ... */ })
reduce(function(previousValue, currentValue, currentIndex, array) { /* ... */ })
reduce(function(previousValue, currentValue, currentIndex, array) { /* ... */ }, initialValue)

参数

callback
一个 “reducer” 函数,包含四个参数:
  previousValue
  上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]。
  currentValue
  数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]。
  currentIndex 可选
  数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。
  array 可选
方法正在操作的数组。
initialValue 可选
作为第一次调用 callback 函数时参数 previousValue 的值。若指定了初始值 initialValue,则 currentValue 则将使用数组第一个元素;否则 previousValue 将使用数组第一个元素,而 currentValue 将使用数组第二个元素。

返回值

使用 “reducer” 回调函数遍历整个数组后的结果。

异常

TypeError
数组为空且初始值 initialValue 未提供。

示例

reducer 逐个遍历数组元素,每一步都将当前元素的值与上一步的计算结果相加(上一步的计算结果是当前元素之前所有元素的总和)——直到没有更多的元素被相加。

1
2
3
4
5
6
7
8
9
10
11
const array1 = [1, 2, 3, 4];

// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(previousValue, currentValue) => previousValue + currentValue,
initialValue
);

console.log(sumWithInitial);
//10

累加对象数组里的值

要累加对象数组中包含的值,必须提供 initialValue,以便各个 item 正确通过你的函数。

1
2
3
4
5
6
7
let initialValue = 0
let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(
(previousValue, currentValue) => previousValue + currentValue.x
, initialValue
)

console.log(sum) // logs 6

计算数组中每个元素出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']

let countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++
}
else {
allNames[name] = 1
}
return allNames
}, {})
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
// allNames初始值:{}

数组去重

注: 如果生产环境可以兼容Set 和 Array.from()方法,你可以使用let arrayWithNoDuplicates = Array.from(new Set(myArray)) 进行去重。

1
2
3
4
5
6
7
let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myArrayWithNoDuplicates = myArray.reduce(function (previousValue, currentValue) {
if (previousValue.indexOf(currentValue) === -1) {
previousValue.push(currentValue)
}
return previousValue
}, [])

使用 .reduce() 替换 .filter().map()

使用 Array.filter() 和 Array.map() 会遍历数组两次,而使用具有相同效果的 Array.reduce() 只需要遍历一次,这样做更加高效(当然也可以用for循环或者Array.forEach)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const numbers = [-5, 6, 2, 0];

const doubledPositiveNumbers = numbers.reduce((previousValue, currentValue) => {
if (currentValue > 0) {
const doubled = currentValue * 2;
previousValue.push(doubled);
}
return previousValue;
}, []);

console.log(doubledPositiveNumbers); // [12, 4]

//使用map直接执行一次结果会包含undefined:
let newArray = numbers.map(x => {
if(x > 0) {
return x * 2;
}
});
console.log(newArray); //[undefined, 12, 4, undefined]

浏览器兼容性

以上方法皆兼容IE9及以上

参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array