# JS的数组方法汇总
举例讲解一些JS的数组方法,如reduce()、map()等
# reduce
 - 接受一个函数作为累加器,数组中的每个值开始缩减,最终计算为一个值。
- 可以作为一个高阶函数,用于函数的compose
- 对于空数组是不会执行回调函数的
array.reduce(function(total,current,currentIndex,arr), initialValue)
1
参数解析
- total: 函数传进来的初始值或上一次回调的返回值
- current:数组中当前处理的元素值
- currentIndex:当前元素索引
- arr: 当前元素所属的数组本身
- 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个参数:
- Accumulator (acc)(累计器)
- Current Value (cur)(当前值)
- Current Index (idx)(当前索引)
- Source Array (src)(源数组)
reducer 函数的返回值将会分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
reducer 函数还有一个可选参数 initialValue, 该参数将作为第一次调用回调函数时的第一个参数的值。如果没有提供 initialValue,则将使用数组中的第一个元素。
在上述例子, reduce方法接收的第一个参数(Accumulator)是 x, 第二个参数(Current Value)是 y。
- 在第一次调用时,累加器 x为1,当前值“y”为2,打印出累加器和当前值:1和2。
- 例子中我们的回调函数没有返回任何值,只是打印累加器的值和当前值。如果函数没有返回值,则默认返回 undefined。在下一次调用时,累加器为undefined,当前值为3, 因此undefined和3被打印出。
- 在第四次调用时,回调函数依然没有返回值。累加器再次为 undefined,当前值为4。undefined和4被打印出。
# forEach
 
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数。对于空数组是不会执行回调函数的
没有返回值,所以不支持链式调用
array.forEach(function(currentValue, index, arr), thisValue)
1
| 参数 | 描述 | 
|---|---|
| function(currentValue, index, arr) | 必需 ( currentValue当前元素;index可选,当前元素的索引值;arr可选,当前元素所属的数组对象。 | 
| thisValue | 可选,传递给函数的值一般用 this值,如果这个值为空,则undefined或null会传递给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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
- for...in是- JavaScript最早的遍历语法,用来遍历对象的可枚举属性。- for...in遍历对象的顺序是不确定的,因此不能保证属性的顺序。此外,由于遍历的是属性名,所以遍历出来的是字符串类型。在遍历数组时,- for...in会将数组的索引遍历出来(字符串类型,普通- for/forEach循环为数值类型),但是会遍历原型链上的属性,因此需要使用- Object.prototype.hasOwnProperty方法判断是否是对象自身的属性。- for...in也无法使用- break或- return终止循环。
- for...of是- ES6新引入的遍历语法,可以遍历具有- Iterable接口的数据结构(如数组、- Map、- Set等),以及实现了自定义迭代器的对象。- for...of通过迭代器对象的- next方法来遍历每个元素,并且可以使用- break或- return终止循环。- for...of无法遍历对象。
- forEach是- Array类型的一个方法,它接受一个函数作为参数,对数组的每个元素都调用一次该函数,并传入当前元素、当前索引和整个数组。- forEach无法使用- break或- return终止循环。- forEach无法遍历对象。
for...of的魅力
- 数组迭代
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
2
3
4
5
6
7
8
9
10
11
12
13
- 类数组迭代
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
2
3
4
5
6
7
8
- 遍历字符串
- 遍历对象,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
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
2
3
4
5
6
7
