Iterator
迭代器,es6引入的一种遍历机制,为我们遍历数据提供一个统一的接口。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
本质
Iterator是一个指针,指向当前数据结构的起始位置。当调用next()时,指针往后移,指向下一个对象;不断调用指针对象的next方法,直到它指向数据结构的结束位置。 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,是一个包含value和done两个属性的对象。value是当前成员的值,done是一个布尔值,表示遍历是否结束。
const items = ["1", "2", "3"];
const it = items[Symbol.iterator]();
it.next();
//{value: "1", done: false}
it.next();
//{value: "2", done: false}
it.next();
//{value: "3", done: false}
it.next();
//{value: undefined, done: true}
下面写一个类似的例子:
function myIterator(arr) {
let nextIdx = 0;
return {
next: function() {
return nextIdx < arr.length ?
{value: arr[nextIdx++], done: false} :
{value: undefined, done: true};
}
};
}
let it = myIterator(['a', 'b', 'c']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: "c", done: false }
it.next() // { value: undefined, done: true }
支持迭代的数据结构
es6规定默认的Iterator接口部署在Symbol.iterator属性,也就是说只要一个数据结构有Symbol.iterator属性的话,它就是可遍历的;支持for...of循环。 原生具有Symbol.iterator的数据结构有:
- Array
- Set
- Map
- String
- arguments(参数列表)
- NodeList(dom节点)
let str = 'str'
for(let i of str) {
console.log(i) // s t r
}
let iter = str[Symbol.iterator]();
iter.next() // { value: 's', done: false }
iter.next() // { value: 't', done: false }
iter.next() // { value: 'r', done: false }
iter.next() // { value: undefined, done: true }
可以利用Generator快速实现个Symbol.iterator
let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
[...myIterable] // [1, 2, 3]
注意:对象不具有Symbol.iterator属性,因为对象的哪个属性先遍历,哪个属性后遍历是不确定的。
让对象支持for...of遍历
let obj = {
'0': 's',
'1': 't',
length: '2',
[Symbol.iterator]: Array.prototype[Symbol.iterator],
}
for(let i of obj) {
console.log(i) // s t
}
上面的例子对象是有序列的、线性的,如果是一般对象的话可以支持吗
function* objIterate(obj) {
var keys = Object.keys(obj);
for (var i=0; i<keys.length; i++) {
yield keys[i]
}
}
let obj = {
1: '1',
b: 'b',
3: '3',
a: 'a',
c: 'c'
2: '2',
};
for (var key of objIterate(obj)) {
console.log(key);
}
// 1 2 3 b a c
我们发现打印的顺序乱了,数字属性按顺序排了起来,而常规的一些属性并没有。说明一般对象并不适合使用迭代器。