# 迭代器Iterator

一种遍历集合的机制,集合是指Array、Object、Map、Set等;
它是一种接口,为各种不同的数据结构提供统一的访问机制

作用:

1、 提供统一的、简便的访问接口;
2、 使数据结构成员按某种次序排列;
3、 供ES6 for...of消费

遍历过程

1、 创建指针对象,指向数据结构起始位置
2、 调用next方法,将指针移向下一个成员
3、 不断调用next方法,直到它指向的数据结构的结束位置

实现细节

提供next方法,总是返回包含value和done两个属性的对象,value为当前成员的值,done为boolean false,直到结束返回true;

什么数据结构可Iterable

只要部署了Iterator接口,这种数据结构就是“可遍历的”(Iterable),比如Array、String、Map、Set、TypedArray、函数的arguments对象、NodeList对象, 即原生部署了Iterator, for...of 天然可访问

Symbol.iterator

默认的Iterator接口部署在Symbol.iterator属性上,一个数据结构只要有Symbol.iterator属性,就是可Iterable;
Symbol.iterator本身是一个函数,执行该函数将返回一个遍历器对象, 即{next: fn},如下:

var a = [1,2,3];
var iterator = a[Symbol.iterator]();
iterator.next() // console {value: 1, done: false}
iterator.next() // console {value: 2, done: false}
iterator.next() // console {value: 3, done: false}
iterator.next() // console {value: undefined, done: true}

for...of原理

循环内部调用Symbol.iterator方法

遍历器对象的return和throw

遍历器对象必须具备next方法,return和throw可选。
return调用时机为for...of提前退出(出错或break语句)
throw一般用不到,主要是配合Generator函数使用

自己实现一个遍历器

function makeIterator(array){
    var nextIndex = 0;
    
    return {
        next: function(){
            return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
        }
    }
}

var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done);  // true

在对象上部署Iterator

// 简单例1, 无限迭代
const obj = {
    [Symbol.iterator] : function () {
        return {
            next: function () {
                return {
                    value: 1,
                    done: false
                };
            }
        };
    }
};
// 简单例2, 有限迭代
let obj = {
    data: ['hello', 'world', '!'],
    [Symbol.iterator]() {
        const self = this;
        let index = 0;
        return {
            next() {
                if (index < self.data.length) {
                    return {
                        value: self.data[index++],
                        done: false
                    };
                } else {
                    return { value: undefined, done: true };
                }
            },
            return() {
                console.log('returned');
                return {
                    value: undefined,
                    done: true
                };
            }
        };
    }
};