for循环
for in
for of
forEach
map
reduce

for循环

语雀内容

break\continue\return

语雀内容

for in

for in 和 for 循环的区别:
1、for-in 语句以原始插入顺序迭代对象的可枚举属性。for-in会把继承链的对象属性都会遍历一遍
2、for key in arr 是索引值,索引值不是数字类型,而是string。手动设置arr.name = ‘1’; for in 也可以打印出来
遍历的属性key 是迭代元素的属性值,而不是value值

  1. const arr = []; arr[0]=0; arr[2]=2; arr[4]=4;
  2. # for 循环: 打印结果: 0 undefined 2undefined4
  3. for (var i = 0, len = arr.length; i < len; i++){
  4. console.log(arr[i]);
  5. }
  6. # for in 只打印出遍历对象的可枚举属性
  7. # 打印结果: 0 24
  8. for (var key in arr){
  9. console.log(arr[i]);
  10. }

for in特点:

  • “索引值”是String类型
  • 循环出的值不一定是按顺序的
  • 遍历对象可枚举的属性
  • 可遍历出对象原型中的属性

结论:for in 更适合遍历对象,且遍历对象时配合 arr.hasOwnProperty(key) 使用


forEach

for of\ for in \ for \forEach

//语法 [].forEach(function(value, index, array){ // do some })

callback参数

  1. value(当前值)—-数组中正在处理的当前元素
  2. index(索引) —-数组中正在处理的当前元素的索引
  3. array —-正在应用forEach()数组

优点:
1、不用重新声明index和遍历的元素
2、已被删除 或 从未赋值的项 会被跳过 (注:从未被赋值 不包括值本身为undefined 、null的项

  1. var array = [1, 2, 3];
  2. delete array[1];
  3. console.log(array); //===>[1, undefined × 1, 3]
  4. array.forEach(function(value){
  5. console.log(value);
  6. }) //===>1, 3

优点:
3、不会遍历到数组原型链上的属性
4、index是Number 类型

  1. var arr = [];
  2. arr[0] = "a";
  3. arr[3] = "b";
  4. arr[10] = "c";
  5. arr.name = "Hello world";
  6. arr.forEach(function(data, index, array) {
  7. console.log(data);
  8. // 0 3 10
  9. });

弊端:
0、forEach 里面return ,不是整个函数的return; (因为forEach本质是回调函数);使用return时,效果和在for循环中使用continue一致
1、没有办法跳出或终止 forEach 循环,break,continue不会跳出循环,
2、如果在迭代中删除了元素,之后的元素会被跳过。

for of

for…of语句在可迭代对象(包括 Array, Map, Set, String, TypedArray,arguments 对象等等)上创建一个迭代循环,对每个不同属性的属性值,调用一个自定义的有执行语句的迭代挂钩

特点

  • 比普通for简洁
  • 比forEach 灵活,支持break, continue, return
  • 比for..in 作用更大, 除了可以遍历数组,还可以以遍历遍历其它集合,如字符串、Map 和 Set 对象、DO 集合等

for…of与for…in的区别

  • for…in 遍历每一个属性名称key,而 for…of遍历每一个属性值value:
  • for…in循环会遍历一个object所有的可枚举属性
  • for…of语法是为各种collection对象专门定制的,并不适用于所有的object.它会以这种方式迭代出任何拥有[Symbol.iterator] 属性的collection对象的每个元素


  1. Object.prototype.objCustom = function () {};
  2. Array.prototype.arrCustom = function () {};
  3. let iterable = [3, 5, 7];
  4. iterable.foo = "hello";
  5. for (let i in iterable) {
  6. console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
  7. }
  8. for (let i of iterable) {
  9. console.log(i); // logs 3, 5, 7
  10. }

map

map方法会给原数组中的每个元素都按顺序调用一次callback函数。callback每次执行后的返回值包括 undefined)组合起来形成一个新数组。callback函数只会在有值的索引上被调用;那些从来没被赋过值或者使用delete删除的索引则不会被调用

map一般用于,对原数组新增或修改某属性,然后得到新的数组

  1. const arr2 = arr.map(item => {
  2. item.total = item.price * item.count;
  3. return item;
  4. })

那些从来没被赋过值或者使用delete删除的索引则不会被调用:

  1. Array(10).map(function(){return "A"})
  2. // => [undefined * 10]

题外话:
一个经典的面试题:[1,2,3].map(parseInt)
请解释下面代码的执行结果

  1. console.log([10, 11, 12].map(parseInt)); // [1, NaN, NaN]
  2. console.log([1, 2, 3].map(parseFloat)); // [1, 2, 3]
  3. console.log([1.1, 2.2, 3.3].map(parseInt)); // [1, NaN, NaN]
  4. console.log([1.1, 2.2, 3.3].map(parseFloat)); // [1.1, 2.2, 3.3]

parseInt:parseInt(string [, radix]) 接受两个参数 将字符串解析成radix进制的数字 如 parseInt(‘110’, 2) // 2进制的110表示10进制的6注意的点:> 1、如果传入第一个参数非string会调用string转换为字符 2、该函数的返回值NAN的情况: radix < 2 or string转换为数字失败、 3、radix=0时,按十进制处理 parseFloat:parseFloat(value) 将字符串解析成浮点数 只接受一个参数

  1. [1, 2, 3].map(parseInt); // [1, 2, 3]
  2. // ==> 实际上解析为
  3. // parseInt(1, 0) == 1 // 无0进制 直接转为十进制
  4. // parseInt(2, 1) == NAN // 直接返回NAN
  5. // parseInt(3, 2) == NAN // 2进制只能用01表示 这里出现了3 所以NAN

reduce

reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值。 [].reduce(callback,[initialValue])

callback 被调用时传入四个参数: callback(accum, cur, index, arr)

  1. 上一次调用回调返回的值previousValue
  2. 当前迭代元素的值 currentValue
  3. 元素的索引
  4. 调用reduce的数组

initialValue ===>作为第一次调用 callback 的第一个参数。
回调函数第一次执行时,previousValue 和 currentValue 的取值有两种情况。

  • 如果 initialValue 在调用 reduce 时被提供,那么第一个 previousValue 等于 initialValue ,并且currentValue 等于数组中的第一个值;
  • 如果initialValue 未被提供,那么previousValue 等于数组中的第一个值,currentValue等于数组中的第二个值。
  • 如果数组为空并且没有提供initialValue, 会报错。
  • 如果数组仅有一个元素(无论位置如何)并且没有提供initialValue,或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。

reduce的精华所在是将累计器逐个作用于数组成员上,把上一次输出的值作为下一次输入的值

迭代应用:
1、reduce实现map、filter的功能:

  1. const arr = [0, 1, 2, 3];
  2. // 代替map:[0, 2, 4, 6]
  3. const a = arr.map(v => v * 2);
  4. const b = arr.reduce((accurmArr, v) => [...accurmArr, v * 2], []);
  5. // 代替filter:[2, 3]
  6. const c = arr.filter(v => v > 1);
  7. const d = arr.reduce((accurmArr, v) => v > 1 ? [...accurmArr, v] : accurmArr, []);

2、reduce数据统计

# 统计出各个数的出现频率
const arr = [0, 1, 1, 2, 2, 2];
console.log(getCount(arr)); // { 0: 1, 1: 2, 2: 3 }

# way1: 普通迭代
function getCount(arr) {
    let hash = {};
    arr.forEach(item => {
        hash[item] = (hash[item] || 0) + 1;
    });
    return hash;
}

# way2: reduce
function getCount(arr) {
    return arr.reduce((accurmHash, cur) => {
        accurmHash[cur] = (accurmHash[cur] || 0) + 1;
        return accurmHash;
    }, {});
}

3、reduce累加

# 计算数组的累加
const arr = [0, 1, 2, 3, 4, 5];
console.log(getAccumulation(arr)); // 15

# 普通迭代
function getAccumulation(arr) {
    let res = 0;
    arr.forEach(item => res += item);
    return res;
}

# reduce
function getAccumulation(arr) {
    return arr.reduce((acc, item) => acc + item, 0);
}

参考

JavaScript中数组的迭代方法详解(一)