在es6的学习过程中,promise,generators,循环都遇到了Iterator,网上大部分的解释大部分都是后台语言,为了更好的理解es6系列知识,这里专门讲一下es6中的Iterator(迭代器)。
迭代器是一种接口,也可以说是一种规范。它提供了一种统一的遍历数据的方法。我们都知道数组、集合、对象都有自己的循环遍历方法。
比如数组的循环:
let ary = [1,2,3,4,5,6,7,8,9,10];
//for循环
for(let i = 0;i < ary.length;i++){
console.log(ary[i]);
}
//forEach循环
ary.forEach(function(ele){
console.log(ele);
});
//for-in循环
for(let i in ary){
console.log(ary[i]);
}
//for-of循环
for(let ele of ary){
console.log(ele);
}
集合的循环:
let list = new Set([1,2,3,4,5,6,7,8,9,10]);
for(let ele of list){
console.log(ele);
}
对象的循环:
let obj = {
name : 'tom',
age : 25,
gender : '男',
intro : function(){
console.log('my name is '+this.name);
}
}
for(let attr in obj){
console.log(attr);
}
从以上的代码可以看到,数组可以用for、forEach、for-in以及for-of来遍历。集合能用for-of。对象能用for-in。也就是说:
以上数据类型的遍历方式都各有不同,那么有没有统一的方式遍历这些数据呢?这就是迭代器存在的意义。它可以提供统一的遍历数据的方式,只要在想要遍历的数据结构中添加一个支持迭代器的属性即可。
这个属性写法是这样的:
const obj = {
[Symbol.iterator]:function(){}
}
[Symbol.iterator]
属性名是固定的写法,只要拥有了该属性的对象,就能够用迭代器的方式进行遍历。迭代器的遍历方法是首先获得一个迭代器的指针,初始时该指针指向第一条数据之前。接着通过调用next方法,改变指针的指向,让其指向下一条数据。每一次的next都会返回一个对象,该对象有两个属性。其中value代表想要获取的数据,done是个布尔值,false表示当前指针指向的数据有值。true表示遍历已经结束。
let ary = [{num:111},2,3];
let it = ary[Symbol.iterator](); // 获取数组中的迭代器
console.log(it.next()); // { value: Object { num: 111 }, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
数组是支持迭代器遍历的,所以可以直接获取其中的迭代器。集合也是一样。
let list = new Set([1,3,2,3]);
let it = list.entries(); // 获取set集合中自带的的迭代器
console.log(it.next()); // { value: [ 1, 1 ], done: false }
console.log(it.next()); // { value: [ 3, 3 ], done: false }
console.log(it.next()); // { value: [ 2, 2 ], done: false }
console.log(it.next()); // { value: undefined, done: true }
set集合中每次遍历出来的值是一个数组,里面的第一和第二个元素都是一样的。
由于数组和集合都支持迭代器,所以它们都可以用同一种方式来遍历。es6中提供了一种新的循环方法叫做for-of。它实际上就是使用迭代器来进行遍历,换句话说只有支持了迭代器的数据结构才能使用for-of循环。在JS中,默认支持迭代器的结构有:
这里面并没有包含自定义的对象,所以当我们创建一个自定义对象后,是无法通过for-of来循环遍历它。除非将iterator接口加入到该对象中:
let obj = {
name : 'tom',
age : 25,
gender : '男',
intro : function(){
console.log('my name is '+this.name);
},
[Symbol.iterator]:function(){
let i = 0;
let keys = Object.keys(this); // 获取当前对象的所有属性并形成一个数组
return {
next: function(){
return {
value:keys[i++], // 外部每次执行next都能得到数组中的第i个元素
done:i > keys.length // 如果数组的数据已经遍历完则返回true
}
}
}
}
}
for(let attr of obj){
console.log(attr);
}
通过自定义迭代器就能让自定义对象使用for-of循环。
有一些场合会默认调用 Iterator 接口(即Symbol.iterator方法)
let set = new Set().add('a').add('b').add('c');
let [x,y] = set; // x='a'; y='b'
let [first, ...rest] = set; // first='a'; rest=['b','c'];
var str = 'hello';
[...str] // ['h','e','l','l','o']
let arr = ['b', 'c'];
['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
Iterator 的作用有三个:
over 有问题留言
借鉴:https://zhuanlan.zhihu.com/p/66593213
http://es6.ruanyifeng.com/#docs/iterator