✨最全的JavaScript数组和对象遍历方法🔥

433 阅读9分钟

数组

for循环

最传统的方法,按下标存取,也是最高效的方法

let arr = [1,2,3,4]
for(let i = 0; i < arr.length; i++){
    console.log(arr[i])
}

//1,2,3,4

//不用每次计算len,更加高效
for(let i = 0, len = arr.length; i < len; i++){
    console.log(arr[i])
}
//1,2,3,4

for...of循环

for...of语句创建一个迭代器(ES6引入,迭代器只会便利可枚举属性)。每一次循环都会调用迭代器的next对象。并返回当前该迭代器的值。

let arr = [1,2,3,4]
for(let val of arr){
    console.log(val)
}

//1,2,3,4

另外只要实现了迭代器的数据才能使用for...of循环。具体有:ArrayMapsSetStringArguments Object参数对象Generators(生成器)

for...in循环

for...in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性。

let arr = [1,2,3,4]
for(let i in arr){
    console.log(i)
}

//0,1,2,3

注意:

  1. for ...in循环遍历得到的结果为数据的键(数组即为下标)。
  2. 数组最好不要用for...in,因为for...in循环是为遍历对象而且设计的。
  3. 该方法也可用于String遍历下标。
  4. for...in会顺着原型链向上遍历,原型链上所有的可遍历对象都会被遍历。比如(定义Array.prototype.max = () => {...}来获取数组中最大值,如果这么定义,那么这个属性也会被for...in纳入遍历)

Array.prototype.entries()

该方法一个新的 Array 迭代器对象。Array Iterator是对象,它的原型(proto:Array Iterator)上有一个next方法,可用用于遍历迭代器取得原数组的[key,value]。

var arr = ["a", "b", "c"];
var iterator = arr.entries();
console.log(iterator);

/*Array Iterator {}
         __proto__:Array Iterator
         next:ƒ next()
         Symbol(Symbol.toStringTag):"Array Iterator"
         __proto__:Object
*/

用法一(直接使用Iteratornext对象):

const array1 = ['a', 'b', 'c'];

const iterator1 = array1.entries();

console.log(iterator1.next().value);
// expected output: Array [0, "a"]

console.log(iterator1.next().value);
// expected output: Array [1, "b"]

用法二(二维数组按行排序):

function sortArr(arr) {
    var goNext = true;
    var entries = arr.entries();
    while (goNext) {
        var result = entries.next();
        if (result.done !== true) {
            result.value[1].sort((a, b) => a - b);
            goNext = true;
        } else {
            goNext = false;
        }
    }
    return arr;
}

var arr = [[1,34],[456,2,3,44,234],[4567,1,4,5,6],[34,78,23,1]];
sortArr(arr);

/*(4) [Array(2), Array(5), Array(5), Array(4)]
    0:(2) [1, 34]
    1:(5) [2, 3, 44, 234, 456]
    2:(5) [1, 4, 5, 6, 4567]
    3:(4) [1, 23, 34, 78]
    length:4
    __proto__:Array(0)
*/

法三(使用for…of 循环)最典型:

var arr = ["a", "b", "c"];
var iterator = arr.entries();

for (let v of iterator) {
    console.log(v);
}
// [0, "a"]
// [1, "b"]
// [2, "c"]

for (let [index, value] of iterator) {
    console.log(index+'---'+value);
}

// 0-"a"
// 1-"b"
// 2-"c"

Array.prototype.keys()

keys() 方法返回一个包含数组中每个索引键的Array Iterator对象。

与上面的Array.prototype.entries()相同,只是遍历的是索引键。用法与上面完全一致,本质也是迭代器。

典型用法(结合for...of):

let arr = [1,2,3,4]

for(let i of arr.keys()){
    console.log(i)
}

//0,1,2,3

Array.prototype.values()

values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值

与上面的Array.prototype.entries()相同,只是遍历的是索引键。用法与上面完全一致,本质也是迭代器。

典型用法(结合for...of):

const arr = ['a', 'b', 'c'];

for (const value of arr.values()) {
  console.log(value);
}

//a,b,c

Array.prototype.every()

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

其接收一个回调函数callbackcallback 在被调用时可传入三个参数:元素值,元素的索引,原数组。

every 不会改变原数组。

every 和数学中的"所有"类似,**当所有的元素都符合条件才会返回true。**正因如此,若传入一个空数组,无论如何都会返回 true。(这种情况属于无条件正确:正因为一个空集合没有元素,所以它其中的所有元素都符合给定的条件。)

注意:若收到一个空数组,此方法在一切情况下都会返回 true

典型用法:

let arr = [5,6,4,7,8]
arr.every((value, index, arr) => {
    return value > 3
})

//true(因为所有的value都大于3)

arr.every((value, index, arr) => {
    return value > 5
})

//false(其中4,5不满足条件)

every方法不会改变数组。

Pollyfill(最基础,下同)

      if (!Array.prototype.every) {
        Object.defineProperty(Array.prototype, every, {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.every ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            let res = true;
            for (let [i, v] of this.entries()) {
              if (!callback(v)) {
                res = false;
                break;
              }
            }
            return res;
          },
        });
      }

Array.prototype.some()

这个方法与上面的方法使用方法完全相同,但是所用相反,**some()**方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。

其接收一个回调函数callbackcallback 在被调用时可传入三个参数:元素值,元素的索引,原数组。

**注意:**如果用一个空数组进行测试,在任何情况下它返回的都是false

典型用法:

let arr = [6,9,3,4]

arr.some((value, index, arr) => {
    return value > 8
})

//true(因为存在一个9大于8)

arr.some((value, index, arr) => {
    return value > 12
})

//false(因为所有的value都小于12)

some方法不会改变数组。

Pollyfill

if(!Array.prototype.some){
          Object.defineProperty(Array.prototype, 'some', {
        enumerable: false,
        value: function (callbakc) {
          if (this === null) {
            throw new TypeError(
              'Array.prototype.some ' + 'called on null or undefined'
            );
          }
          if (typeof callback !== 'function') {
            throw new TypeError(callback + ' is not a function');
          }
          let res = false;
          for (let [i, v] of this.entries()) {
            if (callbakc(v, i, this)) {
              res = true;
              break;
            }
          }
          return res;
        },
      });
}

Array.prototype.filter()

filter() 方法创建一个新数组, 其包含通过所提供函数实现的过滤的所有元素的结果。

接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)

返回:一个数组,包含所有的元素的检测结果。

let arr = [1, 2, 3, 4]

arr.filter((value, index, arr) => {
    return value > 2
})

//[3, 4]

该方法可用于剔除不合法数据,比如

let arr = [1,2,3, undefined, null, 4, 5]

arr.filter((value, index, arr) => {
    return value != undefined && value != null
})

//[1, 2, 3, 4, 5]

flter方法不会改变数组。

Pollyfill

      if (!Array.prototype.filter) {
        Object.defineProperty(Array.prototype, 'filter', {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.filter ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            let res = [];
            for (let i = 0; i < this.length; i++) {
              if (callback(this[i])) {
                res.push(this[i]);
              }
            }
            return res;
          },
        });
      }

Array.prototype.find()

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)

返回:数组中满足提供的测试函数的第一个元素的值。

let arr = [1,2,3,4]

arr.find((value, index, arr) => {
    return value > 2
})

//2(由于3,4均大于2,但是3是第一个,所以返回3的下标2)

find方法不会改变数组。

Pollyfill

      if (!Array.prototype.find) {
        Object.defineProperty(Array.prototype, 'find', {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.find ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            let res = undefined;
            for (let i = 0; i < this.length; i++) {
              if (callback(this[i], i, this)) {
                res = this[i];
                break;
              }
            }
            return res;
          },
        });
      }

Array.prototype.findIndex()

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。

接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)

返回:数组中满足提供的测试函数的最后一个元素的值。

let arr = [1,2,3,4]

arr.find((value, index, arr) => {
    return value > 2
})

//3(由于3,4均大于2,但是4是最后一个,所以返回4的下标3)

Pollyfill


      if (!Array.prototype.findIndex) {
        Object.defineProperty(Array.prototype, 'findIndex', {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.findIndex ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            let res = -1;
            for (let i = 0; i < this.length; i++) {
              if (callback(this[i], i, this)) {
                res = i;
                break;
              }
            }
            return res;
          },
        });
      }

Array.prototype.forEach()

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

接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)

返回undefined

forEach() 为每个数组元素执行一次 callback 函数;与 map() 或者 reduce() 不同的是,它总是返回 undefined 值,并且不可链式调用。其典型用例是在一个调用链的最后执行副作用(side effects,函数式编程上,指函数进行 返回结果值 以外的操作)。

forEach 不会直接改变调用它的对象,但是那个对象可能会被 callback 函数改变。

示例:

let arr = [1, 2, 3, 4]
let res = []

arr.forEach((value, index, arr) => {
    res.push(value*2) 
})

console.log(res)		//[2,4,6,8]

Pollyfill

      if (!Array.prototype.forEach) {
        Object.defineProperty(Array.prototype, 'forEach', {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.forEach ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            for (let i = 0; i < this.length; i++) {
              callback(this[i], i, this);
            }
          },
        });
      }

Array.prototype.map()

**map()**方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。(forEach类似,区别是forEach不用返回,一起操作都在回调函数中进行)

接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)

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

上面的示例重写:

let arr = [1, 2, 3, 4]
let res = arr.map((value, index, arr) => {
    return value * 2
})
console.log(res)		//[2, 4, 6, 8]
  1. callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
  2. 因为map生成一个新数组,当你不打算使用返回的新数组却使用map是违背设计初衷的,请用forEach或者for-of替代。
  3. map 不修改调用它的原数组本身(当然可以在 callback 执行时改变原数组)
  4. 根据规范中定义的算法,如果被map调用的数组是离散的(如:arr = [emptyx2, 3]),新数组将也是离散的保持相同的索引为空。

Pollyfill

      if (!Array.prototype.map) {
        Object.defineProperty(Array.prototype, 'map', {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.map ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            let res = [];
            for (let i = 0; i < this.length; i++) {
              res.push(callback(this[i], i, this));
            }
            return res;
          },
        });
      }

Array.prototype.reduce()

reduce()方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

接受:函数累计处理的结果reducer 函数接收4个参数:

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

callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

返回值:函数累计处理的结果

示例:

  1. 数组里所有值的和
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);
// 和为 6
  1. 累加对象数组里的值

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

var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
    return accumulator + currentValue.x;
},initialValue)

console.log(sum) // logs 6

你也可以写成箭头函数的形式:

var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(
    (accumulator, currentValue) => accumulator + currentValue.x
    ,initialValue
);

console.log(sum) // logs 6
  1. 计算数组中每个元素出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var 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 }

Polyfill

reduce 被添加到 ECMA-262 标准第 5 版,因此它在某些实现环境中可能不被支持。把下面的代码添加到脚本开头可以解决此问题,从而允许在那些没有原生支持 reduceRight 的实现环境中使用它。

// Production steps of ECMA-262, Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
  Object.defineProperty(Array.prototype, 'reduce', {
    value: function(callback /*, initialValue*/) {
      if (this === null) {
        throw new TypeError( 'Array.prototype.reduce ' +
          'called on null or undefined' );
      }
      if (typeof callback !== 'function') {
        throw new TypeError( callback +
          ' is not a function');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // Steps 3, 4, 5, 6, 7
      var k = 0;
      var value;

      if (arguments.length >= 2) {
        value = arguments[1];
      } else {
        while (k < len && !(k in o)) {
          k++;
        }

        // 3. If len is 0 and initialValue is not present,
        //    throw a TypeError exception.
        if (k >= len) {
          throw new TypeError( 'Reduce of empty array ' +
            'with no initial value' );
        }
        value = o[k++];
      }

      // 8. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kPresent be ? HasProperty(O, Pk).
        // c. If kPresent is true, then
        //    i.  Let kValue be ? Get(O, Pk).
        //    ii. Let accumulator be ? Call(
        //          callbackfn, undefined,
        //          « accumulator, kValue, k, O »).
        if (k in o) {
          value = callback(value, o[k], k, o);
        }

        // d. Increase k by 1.
        k++;
      }

      // 9. Return accumulator.
      return value;
    }
  });
}
//concise method     
if (!Array.prototype.reduce) {
        Object.defineProperty(Array.prototype, 'reduce', {
          enumerable: false,
          value: function (callback) {
            if (this === null) {
              throw new TypeError(
                'Array.prototype.map ' + 'called on null or undefined'
              );
            }
            if (typeof callback !== 'function') {
              throw new TypeError(callback + ' is not a function');
            }
            let count = 0;
            for (let i = 0; i < this.length; i++) {
              count += callback(count, this[i], i, this);
            }
          },
        });
      }

Array.prototype.reduceRight()

这个方法与Array.prototype.reduce()只是执行顺序上相反(从右到左)。

**reduceRight()**方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。

接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)

求一个数组中所有值的和

var sum = [0, 1, 2, 3].reduceRight(function(a, b) {
  return a + b;
});
// sum is 6 (虽然结果相同,但是是从右到左加)

展示 reducereduceRight 之间的区别

var a = ['1', '2', '3', '4', '5'];
var left  = a.reduce(function(prev, cur)      { return prev + cur; });
var right = a.reduceRight(function(prev, cur) { return prev + cur; });

console.log(left);  // "12345"
console.log(right); // "54321"

Polyfill

reduceRight 被添加到 ECMA-262 标准第 5 版,因此它在某些实现环境中可能不被支持。把下面的代码添加到脚本开头可以解决此问题,从而允许在那些没有原生支持 reduceRight 的实现环境中使用它。

// Production steps of ECMA-262, Edition 5, 15.4.4.22
// Reference: http://es5.github.io/#x15.4.4.22
if ('function' !== typeof Array.prototype.reduceRight) {
  Array.prototype.reduceRight = function(callback /*, initialValue*/) {
    'use strict';
    if (null === this || 'undefined' === typeof this) {
      throw new TypeError('Array.prototype.reduceRight called on null or undefined');
    }
    if ('function' !== typeof callback) {
      throw new TypeError(callback + ' is not a function');
    }
    var t = Object(this), len = t.length >>> 0, k = len - 1, value;
    if (arguments.length >= 2) {
      value = arguments[1];
    } else {
      while (k >= 0 && !(k in t)) {
        k--;
      }
      if (k < 0) {
        throw new TypeError('reduceRight of empty array with no initial value');
      }
      value = t[k--];
    }
    for (; k >= 0; k--) {
      if (k in t) {
        value = callback(value, t[k], k, t);
      }
    }
    return value;
  };
}

下面是几种遍历方法的时间对比,可以看出传统for...耗时最少,;for...in最差。

循环遍历.png

对象

for...in方法

上面已经提到了for...in方法用于遍历数据的索引键。所以这个方法也可以用于遍历对象,具体方法如下:

let obj = {a : 1, b : 2, c : 3, d : 4}

for(let i in obj){
    console.log(`${i}---${obj[i]}`)
}

/*
*a---1
*b---2
*c---3
*d---4
*/

Object.entries()

**Object.entries()**方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

参数

  • obj

    可以返回其可枚举属性的键值对的对象。

返回值

Object.entries()返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。

示例

const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

// array like object with random key ordering
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

// getFoo is property which isn't enumerable
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]

// non-object argument will be coerced to an object
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]

// iterate through key-value gracefully
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}

// Or, using array extras
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});

Object.keys()

**Object.keys()**方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

参数

  • obj

    要返回其枚举自身属性的对象。

返回值

一个表示给定对象的所有可枚举属性(索引键)的字符串数组。

描述

Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性(索引键)。这些属性的顺序与手动遍历该对象属性时的一致。

示例

// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
  getFoo: {
    value: function () { return this.foo; }
  }
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']

Object.values()

**Object.values()**方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。

参数

  • obj

    被返回可枚举属性值的对象。

返回值

一个包含对象自身的所有可枚举属性值的数组。

描述

Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。

示例

var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']

// array like object with random key ordering
// when we use numeric keys, the value returned in a numerical order according to the keys
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']

// getFoo is property which isn't enumerable
var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } });
my_obj.foo = 'bar';
console.log(Object.values(my_obj)); // ['bar']

// non-object argument will be coerced to an object
console.log(Object.values('foo')); // ['f', 'o', 'o']

本文大部分参考了MDN