今天想说说ES6中一个新增的特性,生成器和迭代器。
为什么要引入这个特性呢?大概是因为在JS以前的方法中,如果我们想遍历循环数组类的数据类型,大概会利用for循环,但是在循环的过程中,我们不得不定义一个变量来跟踪遍历的位置。问题就在于有些时候我们用不到for循环里的位置变量,而是对数组里的数据更感兴趣。还有,有的时候循环的层次一旦变多,就容易出现位置变量弄混,重复覆盖等问题,这个时候,生成器和迭代器就出现了。
那么就开始瞎🐔8说说了。
啥是迭代器
当我们学习一个新的特性时,第一个疑问大概是它到底是啥,它的数据类型是什么。其实迭代器就是一种对象,一点都不神秘,一点也不新鲜,所以下面都是叫迭代器对象。
那么这种对象有什么特殊之处呢?
迭代器对象它有一个重要的API,就是next()方法,你可以直接在迭代器对象身上多次调用。而调用不是白调用的,调用next()方法会返回一个“结果对象”。这个结果对象呢有两个属性:
- value:表示下一个要返回的值.
- done :是一个boolean值,当数据遍历完就会返回false.
(当不停地调用next()方法把所有的数据都返回以后,再调用next()方法得到的结果对象的value:undefined,而done以后都是true)
从somewhere抄了一段用es5创建的迭代器的方法,大概可以理解一下迭代器。
function createIterator(items) {
var i = 0;
return {
next: function () {
var done: (i >= items.length);
var value: !done ? items[i++]: undefined;
return {
done: done,
value: value
};
}
};
}
var iterator = createIterator([1,2,3]);
console.log(iterator.next()) // "{value: 1, done: false}"
console.log(iterator.next()) // "{value: 2, done: false}"
console.log(iterator.next()) // "{value: 3, done: false}"
//下面以后的调用返回的都是一样的结果
console.log(iterator.next()) // "{value: undefined, done: true}"
console.log(iterator.next()) // "{value: undefined, done: true}"
上面就是利用createIterator函数返回了一个对象,这个对象有next()方法,next方法执行时会根据item.length来决定返回value和done的值,当i变成3的时候done变成false,而value也从此成为undefined。
啥是生成器
创建迭代器对象是很复杂的,ES6仁慈地引入了一个可以让我们coder轻松创建迭代器对象的特性,就是生成器。
同样地,我们还是要先知道生成器的类型,根据前面那个代码想必也能看出来,生成器就是一个函数,但是这个函数却跟一般的函数有一点点不同。
当声明生成器函数的时候,会在function关键字的后面加一个*,并且最好在function后面空一格,紧贴函数名。还会用到yield关键字,这样说比较。。。抽象,看代码吧:
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
//生成器被调用的方式和一般函数一样,得到迭代器对象
let iterator = createIterator();
console.log(iterator.next()) // "{value: 1, done: false}"
console.log(iterator.next()) // "{value: 2, done: false}"
console.log(iterator.next()) // "{value: 3, done: false}"
所以以后如果在函数名字的前面看到*就意味着这个函数是生成器函数,而yield起到的作用就是决定生成的迭代器对象调用next()方法的返回顺序。
而最神奇的地方是,yield能“暂缓执行语句”,什么意思?比如上面那个例子,当第一次调用iterator.next()时,yield 1被执行,而只有你再继续调用iterator.next(),yield 2才会被执行。
但是yield并不能在生成器函数外或者生成器函数内部的函数内使用
function *createIterator(items) {
items.forEach(function(item) {
yield item; //会报错
})
}
除了通过函数声明的方式定义生成器函数还可以通过函数表达式、对象方法等等方式生成。
// 函数表达式
let createIterator = function *() {
.....
}
//对象方法形式
let o = {
createIterator: function *() {
.....
}
}
//对象方法简写形式
let o = {
*createIterator() {
........
}
}