for in 、for of 和 forEach 的使用场景

2,481 阅读3分钟

for in 、for of 与 forEach三者到底有什么区别?

前言:for in,for of与forEach这三个都是循环时常会用到的,每一个的使用场景略微不同,通过三者一些对比来发现什么样的场景使用哪一种循环最优。

首先来看看for...in...的作用:

1.1 可枚举对象

        const star={
            name:'钢铁侠',
            gender:'男',
            age:35
        };

        for(const k in star){
            console.log(k);   //name  gender  age
            console.log(star[k]); //钢铁侠  男  35
        }
        

当使用for...in...循环对象,能遍历属性名和属性值,使用for...of...遍历对象会报错。

        const star={
            name:'钢铁侠',
            gender:'男',
            age:35
        };

        for (const k of star) {
            console.log(k);   // star is not iterable
        }

所以for...of...是不能循环对象的,那么forEach可以遍历对象吗?

        const star = {
            name: '钢铁侠',
            gender: '男',
            age: 35
        };

        star.foreach(function (i) {
            console.log(i);    //star.foreach is not a function
        })

可见forEach不能对对象进行遍历操作

1.2可枚举数组

        const arr = ['tom', 'jarry', 'jack']
        
        for (const k in arr) {
            console.log(k);    // 0 1 2
            console.log(arr[k]);   // tom jarry jack
        }

输出结果为:0 ‘tom’ 1 ‘jarry’ 2 ‘jack’

结果看出使用for…in…是输出索引值,通过索引值能拿到数组数据

那么用for...of...的结果就不同了

        const arr = ['tom', 'jarry', 'jack']

        for (const k of arr) {
            console.log(k);    // 0 1 2
            console.log(arr[k]);   // undefined undefined undefined
        }

输出结果为:0 undefined 1 undefined 2 undefined

结果看出使用for…of…是输出数组值

1.3 可枚举的原型对象

        Array.prototype.sayHello = function () {
            console.log("Hello");
        }
        Array.prototype.str = 'world'
        var myArray = [1, 2, 3]
        myArray.name = '数组'

        for (let k in myArray) {
            console.log(k);    //0 1 2 name sayHello str
        }

结果可以看出 for in 不仅返回的是数组的下标,而且将数组的原型对象以及数组对象本身属性值都会返回。但也存在一个问题,实际开发中,这些对象很可能是不需要的,全部列举出来可能会产生新的问题。

所以为了解决原型对象的这个问题,可以使用hasOwnProperty

        Array.prototype.sayHello = function () {
            console.log("Hello");
        }
        Array.prototype.str = 'world'
        var myArray = [1, 2, 3]
        myArray.name = '数组'

        for (let k in myArray) {
            if (myArray.hasOwnProperty(k)) {
                console.log(k);    //0 1 2 name
            }
        }

虽然使用hasOwnProperty,但是数组本身的属性还是会输出

forEach的作用

1.1可遍历数组

针对上面原型对象的问题,可以使用forEach进行处理

        Array.prototype.sayHello = function () {
            console.log("Hello");
        }
        Array.prototype.str = 'world'
        var myArray = ['a', 'b', 'c']
        myArray.name = '数组'

        myArray.forEach((value, i) => {
            console.log(value);
            console.log(i);
            // 输出  ‘a’ 0 ‘b’ 1 ‘c’ 2
        })

使用forEach可以输出索引值和数组值,而且不会输出数组的原型对象

1.2无法 break的问题

forEach有个问题就是不能中断执行

        var arr = [1, 2, 3];

        arr.forEach(function (value) {
            console.log(value);
            if (value === 2) {
                return false
            }
        })  //输出1,2,3

从结果可以看出,return false没有执行,他会一直运行到底

for in 也同样存在这个问题

        var arr = [1, 2, 3];

        for (let value in arr) {
            console.log(arr[value]);
            if (value == 5) {
                break;
            }
        }   //输出 1,2,3

从结果可以看出,break没有执行,他会一直运行到底

1.3 for...of...的作用

可遍历数组 原型对象除了可用forEach,还可以使用for of进行处理

        Array.prototype.sayHello = function () {
            console.log("Hello");
        }
        Array.prototype.str = 'world'
        var myArray = ['a', 'b', 'c']
        myArray.name = '数组'

        for (let index of myArray) {
            console.log(index);  // 输出  a b c
        }

使用for of 无法输出索引值,但也不会输出数组的原型对象

for of可以解决无法使用break退出的问题

        var arr = [1, 2, 3];
        for (let value of arr) {
            console.log(value);
            if (value == 2) {
                break
            }
        }   //输出1,2

可以看出,break中断了循环

1.4 可迭代字符串

        let str = 'hello';

        for (let value of str) {
            console.log(value);   // 'h' 'e' 'l' 'l' 'o'
        }

1.5 可迭代arguments类数组对象

        (function () {
            for (let k of arguments) {
                console.log(k);
            }
        })(1, 2, 3)

1.6 可迭代map和set

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

        for (let [key, value] of mapData) {
            console.log(value);   //1,2,3
        }
        let setData = new Set([['a', 1], ['b', 2], ['c', 3]])

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

结果输出都是1 2 3

总结:

for in 适用于纯对象的遍历,并且只能输出可枚举属性

forEach 适用于需要知道索引值的数组遍历,但是不能中断

for of 适用于无需知道索引值的数组遍历,因为可以中断。另外对于其他字符串,类数组,类型数组的迭代,for of也更适用