
今天我们来认真学习一下在ES6中出现的全新功能: 迭代器和生成器
为什么要加入这个新功能
我们之前使用for循环遍历时,如果多个嵌套需要追踪多个变量,代码复杂度也会增加。
迭代器的出现就是为了简化数据操作,消除这种复杂性并减少循环中的错误
什么是迭代器
迭代器是一个对象
对象有next()方法,调用该方法会返回一个对象,可接受参数用于代替生成器内部上一条yield语句的返回值,第一次调用传参无效
返回的对象有value和done属性,value表示下一个将要返回的值;done用于表示是否已迭代完,默认值为false,迭代完其值为true
来造一个迭代器
function createIterator(items) {
let i = 0;
return {
next: function (replaceValue) {
let done = i >= items.length;
let value = !done ? items[i++] : undefined;
return {
value,
done,
};
},
};
}
什么是生成器
- 生成器是返回迭代器的函数
- 通过function关键字后面的*符号定义,两者可贴在一起也可空一格
- 函数中会有yield关键字,通过它指定调用迭代器的next()方法时的返回值和返回顺序
- 每执行完一条yield语句后函数就会自动停止执行,可以返回任何值或表达式
- yield不能穿透函数边界
- 不能用箭头函数创建生成器
它大概长啥样
// 长这样
function* createIterator(){
yield 1
}
// 也可以长这样
let createIterator=function *(){
yield 1
}
// 还可以这样
let o={
createIterator: function *(){
yield
}
}
let iterator=createIterator()
console.log(iterator.next())
可迭代对象
- 可迭代对象有Symbol.iterator属性,该属性通过指定函数可以返回一个作用于附属对象的迭代器
- 在ES6中,所有的集合对象(数组、Set集合和Map集合)和字符串都是可迭代对象,都有默认的迭代器
- 所有通过生成器创建的迭代器都是可迭代对象
访问默认迭代器
let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next());
创建可迭代对象
默认情况下,开发者定义的对象都是不可迭代对象,但如果给Symbol.iterator属性添加一个生成器,则可以将其变成可迭代对象
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
},
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}
for-of
for-of通过调用可迭代对象的Symbol.iterator方法来获取迭代器,这一过程是由java引擎背后完成的
循环每执行一次都会调用得带起对象的next()方法,并将迭代器返回的结果对象的value属性存储在一个变量中,一直到done属性的值为true
如果for-of用于不可迭代对象、null、undefined将会导致程序抛出错误
内建迭代器
数组、Map集合、Set集合都内建了一下三种迭代器
- entries() 返回一个迭代器,其值为多个键值对,Map集合的默认迭代器
- 调用next()返回一个数组,其中两个元素分别是键值
可解构
let data=new Map() data.set('name','yqx') for(let [key,value] of data.entries()){ // do something }- values() 返回一个迭代器。其值为集合的值,数组和Set集合的默认迭代器
- keys() 返回一个迭代器,其值为集合中的所有键名
- entries() 返回一个迭代器,其值为多个键值对,Map集合的默认迭代器
字符串迭代器
我们知道可以通过类似数组下标的形式访问字符串中的某个字符,但由于方括号操作的是编码单元而非字符,因此无法正确访问双字节字符。
使用for-of可输出正确内容
NodeList迭代器
也拥有默认迭代器
展开运算符
展开运算符可以作用于任意可迭代对象,可将迭代对象转换为数组
let map = new Map([["name", "yqx"]]);
let set = new Set([1, 2, 3]);
console.log([...set]);
console.log([...map]);
在迭代器中抛出错误和处理
function* createIterator() {
let first = yield 1;
let second = yield first + 2;
}
let iterator = createIterator();
console.log(iterator.next());
console.log(iterator.throw(new Error('err')))
console.log(iterator.next(3));// 不会执行
处理
function* createIterator() {
let first
try{
yield 1;
}catch(ex){
first=1
}
let second = yield first + 2;
}
let iterator = createIterator();
console.log(iterator.next());
console.log(iterator.throw(new Error('err')))
console.log(iterator.next(3));
注意调用throw方法也会像掉哦那个next()方法一样返回一个结果对象
生成器返回值
在return语句可以指定一个返回值,该值将被赋值给返回对象的value属性
function* createIterator() {
let first = yield 1;
return 2
}
let iterator = createIterator();
console.log(iterator.next());
console.log(iterator.next());
委托生成器
可将两个迭代器合二为一,将生成数据的过程委托给其他迭代器
function* num() {
yield 1;
}
function* color() {
yield "red";
}
function* combined() {
yield* num();
yield* color();
}
let iterator = combined();
console.log(iterator.next());
console.log(iterator.next());
异步任务执行
let fs=require("fs")
function readFile(filename){
return (callback)=>{
fs.readFile(filename,callack)
}
}
function run(taskDef) {
let task= taskDef()
let result=task.next()
function step(){
if(!result.done){
if(typeof result.value==="function"){
result.value(function(err,data){
if(err){
result=task.throw(err)
return;
}
result=task.next(data)
step()
})
}else {
result=task.next(result.value)
step()
}
}
}
step()
}