# JS的数组方法汇总

举例讲解一些JS的数组方法,如reduce()map()

# reduce

  • 接受一个函数作为累加器,数组中的每个值开始缩减,最终计算为一个值。
  • 可以作为一个高阶函数,用于函数的compose
  • 对于空数组是不会执行回调函数的
array.reduce(function(total,current,currentIndex,arr), initialValue)
1

参数解析

  1. total: 函数传进来的初始值或上一次回调的返回值
  2. current:数组中当前处理的元素值
  3. currentIndex:当前元素索引
  4. arr: 当前元素所属的数组本身
  5. initialValue: 传给函数的初始值
  • 不同初始值下的情况
    • 初始值为数值:
    const arr = [1,2,3,4,5,6,7,8,9,10]
    const sum = arr.reduce(function(prev,current){
        return prev + current
    },4)
    console.log(sum) 
    /*
    输出59
    reduce根据函数传进来的初始值,不断回调叠加最终算出数组的和
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    • 初始值为对象:
    const arr = [1,2,3,4,5,6,7,8,9,10]
    const sum = arr.reduce(function(prev,current){
        prev.count = prev.count + current
        return prev
    },{count: 0})
    console.log(sum)
    //输出{count:55}
    
    1
    2
    3
    4
    5
    6
    7
    • 初始值为数组:
    const str = 'hello'
    const newstr = str.split('')
    const result = newstr.reduce(function(prev,current){
        const obj = {}
        obj.current = current;
        prev.push(obj)
        return prev
    },[])
    console.log(newstr)
    /* [ { current: 'h' },
    { current: 'e' },
    { current: 'l' },
    { current: 'l' },
    { current: 'o' } ]
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

TIP

  • 如果没有提供 initialValue,那么第一次调用 callback 函数时,accumulator 使用原数组中的第一个元素,currentValue 即是数组中的第二个元素。 在没有初始值的空数组上调用 reduce 将报错。
  • 如果提供了 initialValue,那么将作为第一次调用 callback 函数时的第一个参数的值,即 accumulator,currentValue 使用原数组中的第一个元素。
  • 如果提供了initialValue则起始索引号为0,否则为1
[1, 2, 3, 4].reduce((x, y) => console.log(x, y)); //1 2 undefined 3 undefined 4
1

reducer 函数接收4个参数:

  1. Accumulator (acc) (累计器)
  2. Current Value (cur) (当前值)
  3. Current Index (idx) (当前索引)
  4. Source Array (src) (源数组)

reducer 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。 reducer 函数还有一个可选参数 initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供 initialValue,则将使用数组中的第一个元素。 在上述例子, reduce方法接收的第一个参数(Accumulator)是 x, 第二个参数(Current Value)是 y

  • 在第一次调用时,累加器 x1,当前值 “y”2,打印出累加器和当前值:12
  • 例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回 undefined。在下一次调用时,累加器为 undefined,当前值为3, 因此 undefined3被打印出。
  • 在第四次调用时,回调函数依然没有返回值。累加器再次为 undefined ,当前值为4undefined4被打印出。

# forEach

forEach()方法用于调用数组的每个元素,并将元素传递给回调函数。对于空数组是不会执行回调函数的

没有返回值,所以不支持链式调用

array.forEach(function(currentValue, index, arr), thisValue)
1
参数 描述
function(currentValue, index, arr) 必需 (currentValue当前元素;index可选,当前元素的索引值;arr可选,当前元素所属的数组对象。
thisValue 可选,传递给函数的值一般用this值,如果这个值为空,则undefinednull会传递给this的值

例子1:

function foo(el,index) {
    console.log(el, this.id, index);
}
var obj = {
    id: 'awesome'
}
var arr = [1,2,3]
arr.forEach(foo,obj); 
// 1 'awesome' 0
// 2 'awesome' 1
// 3 'awesome' 2
1
2
3
4
5
6
7
8
9
10
11

例子2:

如果使用箭头函数来传入函数参数,thisValue会被忽略:

function a(el) { 
    console.log(this.id + el) // 5 6 7
}
let arr = [1, 2, 3]
let obj = {
    id: 4
}
arr.forEach((el) => {
    console.log(this.id + el) // NaN NaN NaN
}, obj)
arr.forEach(a, obj)
1
2
3
4
5
6
7
8
9
10
11

例子3: 对象复制器函数

function copy(obj) {
  const copy = Object.create(Object.getPrototypeOf(obj));
  const propNames = Object.getOwnPropertyNames(obj);

  propNames.forEach(function(name) {
    const desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });

  return copy;
}

const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1); // 现在 obj2 看起来和 obj1 一模一样了
1
2
3
4
5
6
7
8
9
10
11
12
13
14

例子4: 如果数组在迭代时被修改了,则其他元素会被跳过

var words = ['one', 'two', 'three', 'four'];
words.forEach(function(word) {
  console.log(word);
  if (word === 'two') {
    words.shift();
  }
});
// one
// two
// four
1
2
3
4
5
6
7
8
9
10

例子5: 扁平化数组(也可以利用Array.prototype.flat()方法)

/**
 * Flattens passed array in one dimensional array
 *
 * @params {array} arr
 * @returns {array}
 */
function flatten(arr) {
  const result = [];
  arr.forEach((i) => {
    if (Array.isArray(i))
      result.push(...flatten(i));
    else
      result.push(i);
  })
  return result;
}
// Usage
const problem = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
flatten(problem); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# for...of

TIP

var a = ['A','B','C']
a.name = "Hello"
for(var x in a) {
  console.log(x) // '0','1','2','name'
}
// for...in循环把name包括在内,但Array的length属性却不在内。
// for...of修复这个问题
var a = ['A','B','C']
a.name = 'Hello'
for(var x of a) {
  console.log(x) // A,B,C
}
1
2
3
4
5
6
7
8
9
10
11
12
  • for...inJavaScript 最早的遍历语法,用来遍历对象的可枚举属性。for...in 遍历对象的顺序是不确定的,因此不能保证属性的顺序。此外,由于遍历的是属性名,所以遍历出来的是字符串类型。在遍历数组时,for...in 会将数组的索引遍历出来(字符串类型,普通for/forEach循环为数值类型),但是会遍历原型链上的属性,因此需要使用 Object.prototype.hasOwnProperty 方法判断是否是对象自身的属性。for...in 也无法使用 breakreturn 终止循环。
  • for...ofES6 新引入的遍历语法,可以遍历具有 Iterable 接口的数据结构(如数组、MapSet 等),以及实现了自定义迭代器的对象。for...of 通过迭代器对象的 next 方法来遍历每个元素,并且可以使用 breakreturn 终止循环。for...of 无法遍历对象。
  • forEachArray 类型的一个方法,它接受一个函数作为参数,对数组的每个元素都调用一次该函数,并传入当前元素、当前索引和整个数组。forEach 无法使用 breakreturn 终止循环。forEach 无法遍历对象。

for...of的魅力

  1. 数组迭代
const products = ['oranges','apples']
for(const product of products) {
  console.log(product)
}

// 就地解构
const persons = [
  {name: 'John Smith'},
  {name: 'Jane Doe'}
]
for(const {name} of persons) {
  console.log(name)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 类数组迭代
function sum() {
  let sum = 0
  for(const number of arguments) {
    sum += number
  }
  return sum;
}
sum(1,2,3) // 6
1
2
3
4
5
6
7
8
  1. 遍历字符串
  2. 遍历对象,map/set
// Map
const names = new Map();
names.set(1, 'one');
names.set(2, 'two');

for (const [number, name] of names) {
  console.log(number, name);
}
// logs 1, 'one'
// logs 2, 'two'

// Set
const colors = new Set(['white', 'blue', 'red', 'white']);

for (color of colors) {
  console.log(color);
}
// 'white'
// 'blue'
// 'red

// 对象
const person = {
  name: 'John Smith',
  job: 'agent'
};

for (const [prop, value] of Object.entries(person)) {
  console.log(prop, value);
}
// 'name', 'John Smith'
// 'job', 'agent'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# map()

map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。不会改变原数组,不会对空数组进行检测

array.map(function(currentValue, index, arr), thisIndex) // thisIndex: 可选。对象作为该执行回调时使用,传递给函数,用作"this"的值。

let array = [1, 2, 3, 4, 5];
let newArray = array.map((item) => {
  return item * item;
})
console.log(newArray)  // [1, 4, 9, 16, 25]
1
2
3
4
5
6
7