javascript 高级编程系列 - 迭代器/生成器
1. 迭代器(Iterator)
- 特殊对象,具有迭代过程的接口next()方法
- 每次调用next()方法,返回一个结果对象
- 结果对象有两个属性value(任何类型)和done(布尔类型)
- 当集合的值返回完时,再次调用next()方法,结果对象的done属性值为true,value为迭代器最终返回的值。
使用javascript创建迭代器
function createIterator(items){
let i = 0;
return {
next: () => {
let done = (i >= items.length);
let value = !done ? items[i++] : undefined;
return { done, value };
}
};
}
const 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}
2. 生成器(Generator)
- 生成器是一种返回迭代器的函数, 通过function关键字后面的*来表示
- 函数中会用到yield关键字,并且只能在生成器内部使用,在嵌套函数中也不允许使用
- 每执行完一条yield语句后,函数就会自动停止执行,直到再次调用迭代器的next()方法
- 不能使用箭头函数来创建生成器
创建生成器
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
const 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}
console.log(iterator.next()); // {value: undefined, done: true}
创建生成器函数表达式
const createIterator = function *(items){
for (let i = 0; i < items.length; i++){
yield items[i];
}
}
const iterator = createIterator([1,2,3]);
for (let value of iterator){
console.log(value);
}
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}
3. 可迭代对象与for-of循环
- 可迭代对象具有Symbol.iterator属性,是一种与迭代器密切相关的对象。
- Symbol.iterator属性通过指定的函数可以返回一个作用与附属对象的迭代器。
- 所有集合对象(数组,Set集合,Map集合)以及字符串都是可迭代对象。
- for-of 循环每执行一次就会调用可迭代对象的next()方法,并将迭代器返回的结果对象的value属性存储在
一个变量中,循环持续这一过程,直到结果对象的done属性值为true - 生成器默认会为Symbol.iterator属性赋值,通过生成器创建的迭代器都是可迭代对象
生成器产生的迭代器是可迭代对象
const createIterator = function *(items){
for (let i = 0; i < items.length; i++){
yield items[i];
}
}
const iterator = createIterator([1,2,3]);
for (let value of iterator){
console.log(value); // 1, 2, 3
}
4. 访问默认迭代器
- 可以通过Symbol.iterator来访问对象默认的迭代器
const values = [1, 2, 3];
const iterator = values[Symbol.iterator]();
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}
- 检测对象是否为可迭代对象
function isIterable(object){
return typeof object[Symbol.iterator] === 'function';
}
const setObj = new Set([1,2,3]);
console.log(isIterable(setObj)); // true
5. 创建可迭代对象
- 通过给普通对象Symbol.iterator属性添加一个生成器,从而使其变为可迭代对象
const collection = {
items: [1,2,3],
*[Symbol.iterator]() {
for (let item of this.items){
yield item;
}
}
};
for (let item of collection){
console.log(item); // 1, 2, 3
}
6. 内建迭代器
ES6中有3种类型的集合对象:数组,Map集合,Set集合,它们都内建了以下三种迭代器:
-
keys() 返回一个迭代器,其值为集合中所有的键名
-
values() 返回一个迭代器,其值为集合的值
-
entries() 返回一个迭代器,其值为多个键值对
-
数组(默认迭代器为values())
let colors = ['red', 'green', 'blue'];
for (let entry of colors.entries()){
console.log(entry); // [0, 'red'] [1, 'green'] [2, 'blue']
}
for (let key of colors.keys()){
console.log(key); // 0, 1, 2
}
for (let value of colors.values()) {
console.log(value); // 'red', 'green', 'blue'
}
// 使用默认迭代器
for( let color of colors){
console.log(color); // 'red', 'green', 'blue'
}
- Set集合(默认迭代器为values())
let numbers = new Set([1, 2, 3]);
for (let entry of numbers.entries()){
console.log(entry); // [1, 1] [2, 2] [3, 3]
}
for (let key of numbers.keys()){
console.log(key); // 1, 2, 3
}
for (let value of numbers.values()) {
console.log(value); //1, 2, 3
}
// 使用默认迭代器
for (let number of numbers){
console.log(number); // 1, 2, 3
}
- Map集合(默认迭代器为entries())
let data = new Map([['name', 'mike'], ['age', '30']]);
for (let entry of data.entries()){
console.log(entry); // ['name', 'mike'] ['age', '30']
}
for (let key of data.keys()){
console.log(key); // 'name', 'age'
}
for (let value of data.values()) {
console.log(value); // 'mike', '30'
}
// 使用默认迭代器
for (let [key, value] of data){
console.log(key); // 'name', 'age'
console.log(value); // 'mike', '30'
}
- 字符串迭代器
const message = 'ab你好cd';
for (let c of message){
console.log(c); // 'a', 'b', '你', '好', 'c', 'd'
}
7. 高级迭代器功能
- 给迭代器传递参数
通过给迭代器的next()方法传递参数,则这个参数值回替代生成器内部上一条yield语句的返回值
function *createIterator() {
let first = yield 1;
let second = yield first + 2;
yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next(4)); // { value: 6, done: false }
console.log(iterator.next(5)); // { value: 8, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
注意:第一次调用next()方法无论传入什么参数都会被丢弃。由于传给next()方法的参数会替代上一次
yield的返回值,而在第一次调用next()方法前不会执行任何yield语句,因此在第一次调用next()方法
时传递参数是毫无意义的。
- 在迭代器中抛出错误
通过throw()方法,当迭代器恢复执行时可令其抛出一个错误
function *createIterator() {
let first = yield 1;
let second;
try {
second = yield first + 2;
} catch (ex){
second = 6;
}
yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next(4)); // { value: 6, done: false }
console.log(iterator.throw(new Error("error"))); // { value: 9, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
- 生成器返回语句
return 语句可以提前返回,也可以为最后一次next()方法调用指定一个返回值。
function *createIterator() {
yield 1;
return 42;
}
let iterator = createIterator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 422, done: true }
console.log(iterator.next()); // { value: undefined, done: false }
- 委托生成器
在某些情况下,我们需要将两个迭代器合二为一,这时可以创建一个生成器,再给yield语句添加
一个星号,就可以将生成的数据的过程委托给其他迭代器。
function *createIterator1() {
yield 1;
yield 2;
}
function *createIterator2() {
yield 3;
yield 4;
}
function *createCombinedIterator() {
yield *createIterator1();
yield *createIterator2();
yield true;
}
const iterator = createCombinedIterator();
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: 4, done: false }
console.log(iterator.next()); // { value: true, done: false }
console.log(iterator.next()); // { value: undefined, done: false }
8. 异步任务执行器
- 定义一个异步任务执行器
- 创建一个包装器,封装异步操作
- 定义一个生成器来编排包装的异步任务
- 将生成器作为任务执行器函数参数执行
// 定义任务执行器
function run(taskDef){
// 创建迭代器
let task = taskDef();
// 开始执行任务
let result = task.next();
// 循环调用next() 函数
function step(){
// 如果任务未完成,则继续执行
if(!result.done){
if(typeof result.value === 'function'){
result.value(function(err, data){
if(error){
result = task.throw(err);
return;
}
result = task.next(data);
step();
});
}
else{
result = task.next(result.value);
step();
}
}
}
// 开始迭代执行
step();
}
// 定义异步任务函数
function readFile(filename){
return function(callback){
fs.readFile(filename, callback);
};
}
// 执行任务
run(function *(){
let contents = yield readFile('config.json');
doSomething(contents);
console.log('done');
});
标签:
javascript
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战