for of, for in, forEach比较

forEach

forEach无法中途跳出forEach循环,break、continue和return都不奏效。

for in

for...in 循环只遍历可枚举属性。
像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf() 方法或 Object的toString()方法。

循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。

for...in不应该用于迭代一个 Array,因为无法预计索引顺序。

for in 循环的输出顺序问题
先遍历出整数属性(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来。

var obj = {
  a:1,
  '2':1,
  '1':1,
  d:1,
  c:1,
  '3':1
}
obj.b=66;

for (var key in obj) { console.log(key ); }    //"1", "2", "3", "a", "d", "c", "b"
Object.keys(obj);                              //["1", "2", "3", "a", "d", "c", "b"]
Object.keys()和for in 具有相同的排列顺序

for of

有着同for...in一样的简洁语法,但是没有for...in那些缺点。
不同于forEach方法,它可以与break、continue和return配合使用。
提供了遍历所有数据结构的统一操作接口。

遍历对象时,推荐使用for in,使用for of会报错

Iterator

JavaScript原有的四种表示'集合'的数据结构,Object、Array、Set、Map。

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了Iterator接口,就可以完成遍历操作。

Iterator 的作用有三个:

  1. 为各种数据结构,提供一个统一的、简便的访问接口;
  2. 使得数据结构的成员能够按某种次序排列;
  3. ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

遍历器原理

遍历器提供了一个指针,指向当前对象的某个属性,使用next方法,就可以将指针移动到下一个属性。next方法返回一个包含value和done两个属性的对象。其中,value属性是当前遍历位置的值,done属性是一个布尔值,表示遍历是否结束。
原生具备 Iterator 接口的数据结构如下:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

对于原生部署 Iterator 接口的数据结构,不用自己写遍历器生成函数,for...of循环会自动遍历它们。其他数据结构(主要是对象)的 Iterator 接口,都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历。

class RangeIterator {
    constructor(start, stop){
        this.value = start;
        this.stop = stop;
    }
    
    [Symbol.iterator]{return this;}
    
    next(){
        let value = this.value;
        if(value < this.stop){
            this.value++;
            return {done : false, value : value};
        }
        return {done : true, value : undefined};
    }
}

function range(start, stop){
    return new RangeIterator(start, stop);
}

for(let v of range(0,3)) {
    console.log(value);  // 0 1 2
}
阅读 1.2k

推荐阅读