seven's blog | (干货!!)js遍历:for..in,for..of,forEach

(干货!!)js遍历:for..in,for..of,forEach
2018年11月06日

前言

日常开发中,数组或是对象的遍历是一个经常会遇到的事情,关于遍历,其实Js提供了很多方法,但是对于他们之间的区别却理解的不是很透彻,通常情况就是抓起来一个forEach就用。为了提高代码质量,我认为有必要深入了解一下不同遍历的特点。当然,本文不作太多深入讲解,只是就各种的特点进行整理展示。

一、for..infor..offorEach

1、遍历对象

for..in:输出 索引
let obj = { 
    a:1,
    b:2,
    c:3
}
for(let i in obj){
    console.log(i)
}
//a
//b
//c
for..of && forEach:不支持

2、遍历数组

let arr = [1,'参数2',3]
for..in:输出 索引
for(let i in arr){
    console.log(i)
}
//0
//1
//2

for..in 也可以通过 arr[i]的方式输出值

for..of:输出 值
for(let i of arr){
    console.log(i)
}
//1
//参数2
//3
forEach:输出 值&索引
arr.forEach((item,index)=>{
    console.log(item,index)
}}
//1 0
//参数2 1
//3 2

3、遍历的数组或对象包含原型属性或者自定义属性时

let arr = [1,'参数2',3]
arr.attr='attr'
Array.prototype.arrCustom = function() {};
for..in:全部输出
for(let i in arr){
    console.log(i)
}
//0
//1
//2
//attr
//arrCustom

当然也可以通过 hasOwnProperty进行一些优化,但是自定义的属性依旧会输出

for(let i in arr){
    if (arr.hasOwnProperty(i)) {
        console.log(i);
    }
}
//0
//1
//2
//attr
for..of & forEach:不受影响,输出正常

4、遍历时能否中断循环

let arr = [1,3,5,7,9]
for..in:无法中断
for..of:可以使用break中断
for(let i of arr){
    console.log(i);
    if (i==5) {
        break;
    }
}
//1
//3
//5
forEach:无法中断
arr.forEach((item,i)=>{
    console.log(item);
    if (item==5) {
        return false;
    }
})
//1
//3
//5
//7
//9

二、一些for..of特有的功能

1、迭代字符串

let str = 'mystr'

for(let i of str){
    console.log(i);
}
//m
//y
//s
//t
//r

2、迭代arguments类数组对象

3、迭代类DOM集合

无需[].slice.call(),也不需要Array.from()进行数组转化

4、迭代类型数组

let typeArr = new Uint8Array([0x00, 0xff]);

for (let value of typeArr) {
  console.log(value);
}
// 0
// 255

5、迭代Map

let mapData = new Map([['a', 1], ['b', 2], ['c', 3]]);

for (let [key, value] of mapData) {
  console.log(value);
}
// 1
// 2
// 3

6、迭代Set

let setData = new Set([1, 1, 2, 2, 3, 3]);

for (let value of setData) {
  console.log(value);
}
// 1
// 2
// 3

等等。

总结:

for..of是ES6中新增的语法,相对来说应用场景会更丰富一些,但是兼容性的话想对弱一些,使用的时候应酌情考虑 不考虑兼容性的情况下:

  1. 纯对象的遍历======for..in
  2. 数组遍历,不需要知道索引时======for..of
  3. 需要索引=====forEach

因为for..of可以中断,所以数组遍历时更推荐使用。

番外:

除了以上三个重点列出的遍历方法外,其实JS中还有很多其他的遍历方法,只是应用场景稍有不同。

对象遍历

除了以上的for..in,还有下面的一些方法也可以实现

var mySymbol = Symbol();

let myObj = {
    name:'seven',
    age:18,
    [mySymbol]:'Hello!'//Symbol属性
}
Object.prototype.hobby = 'music';
myObj.sex='female'
  1. Obeject.keys(obj)

    可以返回一个数组,包括对象自身的所有可枚举属性,不包括继承的属性和Symbol属性

    Object.keys(myObj)
    //["name", "age", "sex"]
    
  2. Object.getOwnPropertyNames(obj)

    返回一个数组,包含对象自身的所有属性,不含Symbol属性,包括不可枚举属性

    Object.getOwnPropertyNames(myObj)
    //["name", "age", "sex"]
    
  3. Object.getOwnPropertySymbols(obj)

    返回一个数组,包含对象自身的所有Symbol属性

    Object.getOwnPropertySymbols(myObj)
    //[Symbol()]
    
  4. Reflect.ownKeys(obj)

    返回一个数组,包含对象自身的所有属性,不管属性名是Symbol或字符串,也不管是否可枚举

    Reflect.ownKeys(myObj)
    //["name", "age", "sex", Symbol()]
    

    数组遍历

  5. Array.every():测试数组的所有元素是否都通过了指定函数的测试 ``` function isBelowThreshold(currentValue) { return currentValue < 40; } var array1 = [1, 30, 39, 29, 10, 13];

console.log(array1.every(isBelowThreshold)) // true

2. `Array.some()`:检测数组中的元素是否满足指定条件
+ 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
+ 如果没有满足条件的元素,则返回false。
 > tip:不会改变原始数组

var ages = [3, 10, 18, 20];

function checkAdult(age) { return age >= 18; } ages.some(checkAdult) //true

3. `Array.filter()`:创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
> tip:不会改变原始数组

var ages = [32, 33, 16, 40];

function checkAdult(age) { return age >= 18; }

ages.filter(checkAdult) //[32, 33, 40]

4. `Array.map()`:返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组
> tip:不会改变原始数组

var ages = [32, 33, 16, 40];

function checkAdult(age) { return age + 1 }

ages.map(checkAdult) //[33, 34, 17, 41]

5. `Array.reduce()`:接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值
 > tip:不会改变原始数组

var numbers = [65, 44, 12, 4];

function getSum(total, num) { return total + num; } numbers.reduce(getSum); //125

numbers.reduce(getSum,2); //127 ```

本文关于for..infor..offorEach的对比参考自张鑫旭大神的文章,传送门在这里:看,for..in和for..of在那里吵架!,文风还是挺诙谐幽默的,推荐一看!

07 Nov 2018