首页 > 其他分享 >UES-08-迭代器和生成器

UES-08-迭代器和生成器

时间:2024-05-24 19:29:18浏览次数:18  
标签:迭代 08 生成器 yield next let value UES

循环问题

在一般循环结构中,如果需要跟踪多个变量,情况将变得复杂,容易出错。

迭代器

用于迭代对象的对象,一般提供 next() 方法用于迭代对象中的元素,该方法返回两个变量,value 表示返回的值,done 表示是否还存在没有遍历的元素。

function create(items) {
  var i = 0;
  return {
    next: function() {
      var done = i >= items.length;
      var value = !done ? items[i++] : undefined;
      return {
        value: value,
        done: done
      };
    }
  };
}

var ite = create([1,3,2]);
ite.next(); // {value: 1, done : false}
ite.next(); // {value: 3, done : false}
ite.next(); // {value: 2, done : false}
ite.next(); // {value: undefined, done : true}

生成器

生成器是能够返回迭代器的函数,function 关键字和函数标识符之间插入一个 '*' 表示该函数是生成器。在生成器函数内部,可以使用 yield 关键字,当生成器返回的迭代器使用 next() 方法时,生成器会执行到 yield 语句处,然后停止执行。再次执行 next() 方法时,从上一次停止的位置开始执行,依然是执行到 yield 语句处停止。
yield 语句只能在生成器内部作用域使用,不能在生成器内部函数中使用。

function *generator() {
  yield 1;
  yield 2;
}

可以使用函数表达式创建生成器,但是不能使用箭头函数创建。

let gene = function *(items) {
  for (let i = 0; i < items.length; i++) {
    yield items[i];
  }
}

生成器可以作为对象的属性

var obj = {
  *create() {
    yield 1;
    yield 2;
  }
};

let iterator = obj.create();

可迭代对象和 for-of 循环

具有 Symbol.iterator 属性的对象是可迭代对象,这个属性定义了一个函数,用于返回与对象关联的迭代器。集合对象(数组、Set、Map)和字符串都是可迭代对象,可迭代对象和 for-of 配合使用。生成器创建的迭代器是可迭代对象。

使用 for-of 循环时,被迭代对象的 Symbol.iterator 属性定义的方法返回迭代器。在每次迭代时,调用迭代器的 next() 方法,将该方法返回的 value 值赋给变量,当返回的 done 值为 true 时停止迭代。

不可迭代对象、null、undefined 使用 for-of 循环会抛出错误。

可以直接访问 Symbol.iterator 属性得到迭代器或者判断对象是否为可迭代对象。

let arr = [1,2,3];
let iterator = arr[Symbol.iterator](); // 返回迭代器
typeof arr[Symbol.iterator] === "function" // 判断该属性是否存储了函数

自定义对象默认是不可迭代的,但可以为 Symbol.iterator 属性定义一个生成器使得对象可迭代。

let collect = {
  items: [],
  *[Symbol.iterator]() {
    for (e of items) {
      yield e;
    }
  }
}
collect.items.push(1);
collect.items.push(2);
collect.items.push(3);

for (let e of collect)

内置迭代器

集合对象类型有三种:数组、Set 和 Map。每一种都有 3 个方法返回使用目的不同的迭代器:entries()values()keys() 方法。

entries() 方法返回的迭代器迭代的元素是键值对,使用大小为 2 的数组表示,第一个元素为键,第二个元素为值。对于数组对象,键值对是索引和对应的值;对于 Set,键和值相等;对于 Map,与存储时的键值对元素一致。values() 方法返回的迭代器迭代的元素是值。对于 Map,只包含值,没有对应的键。keys() 方法返回的迭代器迭代的元素是键。对于数组,是索引;对于 Set,是值,因为 Set 中键值是一样的。

数组和 Set 的默认迭代器是 values(),Map 的默认迭代器是 entries()

Map 迭代时可以使用数组解构

let m = new Map();
m.set("1", "v");
for (let [key, value] of m)

字符串使用下标访问字符时,使用的是码元,如果某个字符使用两个码元表示,使用下标访问会得到意料之外的结果。字符串的默认迭代器迭代的是单个字符而不是码元,使用 for-in 循环会使用默认迭代器遍历字符串数组。

DOM 中的 NodeList 类型表示页面文档中元素的集合。该类型的默认迭代器与数组类型的一致。

扩展运算符和非数组可迭代对象

扩展运算符作用于可迭代对象时,调用该对象的默认迭代器返回迭代的值。不同的可迭代对象,其默认迭代器不同,返回的值不同。在数组中可以使用任意多次扩展运算符。

let a = [1,2,3],
    b = [9,8,7];
let c = [...a, ...b]; // [1,2,3,9,8,7]

迭代器高级

生成器返回的迭代器调用 next() 方法时,如果向这个方法传入一个参数,则这个参数会取代上一次调用 next() 方法时的 yield 语句。

function *af() {
    let a = yield 1;
    let b = yield a + 2;
    yield b + 3;
}
let i = af();
i.next();  // value: 1, done: false
i.next(3); // value: 5, done: false       相当于使用 3 取代了 yield 1 变成了 let a = 3;
i.next(7); // value: 10, done: false      7 取代了 yield a + 2 变成了 let b = 7
i.next();  // value: undefined, done: true

可以向迭代器可选的 throw() 方法中传入一个错误,这个方法执行时,类似于 next(),会从上一次 yield 语句处恢复执行,然后执行到下一个 yield 语句为止。区别是在恢复执行时,会先抛出传入的错误对象,再执行。

function *ci() {
  let a = yield 1;
  let b;
  try {
    b = yield a + 2;
  } catch(ex) {
    b = 6;
  }
  yield b + 3;
}

let i = ci();
// 执行 yield 1, 得到 value: 1 done: false
i.next();
// 从 yield 1 处恢复,4 赋值给 a,最终执行 yield a + 2,得到 value: 6 done: false
i.next(4);
// 从 yield a + 2 处恢复,抛出错误,被捕获, 继续执行得到 value: 9 done: false
i.throw(new Error("error"));
i.next(); // value: undefined done: true

生成器中使用 return 语句时,表示生成器已经执行结束,即使后面仍然存在 yield 语句。生成器执行到 return 语句时,会将 done 设为 true,如果 return 语句有返回值,则该值设为 value 的值。
扩展运算符和 for-of 循环使用生成器时,如果发现 done 为 true 时,即使此时 value 中有值也不会使用该值,忽略该值。

可以在一个生成器中以 yield * generator() 的形式使用另一个生成器,这种方式可以将多个不同的生成器组合在一起使用。

function *a() {
  yield 1;
  yield 2;
  return 3;
}

function *b(count) {
  for (let i = 0; i < count; i++) {
    yield "b";
  }
}

function *c() {
  let r = yield *a();
  yield *b(r);
}

let i = c();
/*
  先执行 a(),最终返回 3 赋值给 r,然后传递给 b 并执行。
  value: 1     2     "b"   "b"   "b"   undefined
  done:  false false false false false true
*/
i.next();

yield * 'string' 这样直接在字符串上使用 yield 会调用字符串的默认迭代器。

异步任务

使用生成器和迭代器可以执行异步操作。如果有几个任务是连续执行的,并且这些任务执行不需要上一个任务的输出数据,此时可以在一个生成器中定义这些任务,并使用 'yield' 分隔这些任务。然后使用生成器返回的迭代器调用 next() 方法执行。此时每次调用 next() 方法后就执行完了一个任务。

当一个任务的执行需要上一个任务的输出作为输入的时候,此时需要将每个任务执行之后生成的数据保存在 value 中返回,迭代器调用 next() 方法时将其作为参数传入,让下一个任务可以使用该数据。

如果一个任务执行完毕后,返回的是一个参数为回调函数的函数时,这个回调函数的正常执行结果可以作为下一个任务执行时的输入数据;若返回的不是函数,则直接作为下一个任务执行的输入。

function run(generator) {
  let iterator = generator();
  // 执行第一个任务
  let result = iterator.next();
  function nextStep() {
    if (!result.done) { // 还有任务未执行
      if (typeof result.value === "function") {
        result.value(function (err, data) {
          if (err) {
            result = iterator.throw(err);
            return ;
          }
          result = iterator.next(data);
          nextStep();
        });
      } else {
        result = iterator.next(result.value);
        nextStep();
      }
    }
  }
  nextStep();
}

参考

[1] Zakas, Understanding ECMAScript 6, 2017.

标签:迭代,08,生成器,yield,next,let,value,UES
From: https://www.cnblogs.com/xdreamc/p/16548989.html

相关文章

  • Leetcode 力扣97. 交错字符串 (抖音号:708231408)
    给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:s=s1+s2+...+snt=t1+t2+...+tm|n-m|<=1交错 是 s1+t1+s2+t......
  • 二维码生成器,一个支持多文件在线转二维码的的工具,免费二维码生成器
    随着科技的飞速发展,二维码已成为我们生活中不可或缺的一部分。从支付到信息分享,再到活动报名,二维码以其独特的优势,为我们带来了极大的便利。在众多二维码生成器平台中,易易二维码凭借其丰富的功能和卓越的性能,逐渐崭露头角,成为众多用户的首选。本文将对易易二维码生成器平台工具的......
  • 盟军敢死队下载/迅雷BT下载[HD-1080P/2.16GB/AVI中字]4k高清版中字
    《盟军敢死队》是一部关于二战中的英勇士兵们的战争故事的电影。这部电影栩栩如生地描绘了盟军士兵的勇气、团队合作和无畏精神,以及他们为了自由和正义而奋战的决心。这篇文章将对电影进行细致入微的分析,探讨其中所传递的主题和情感。首先,电影的剧情十分紧凑且扣人......
  • 083、望月怀远
    083、望月怀远唐●张九龄海上生明月,天涯共此时。情人怨遥夜,竟夕起相思。灭烛怜光满,披衣觉露滋。不堪盈手赠,还寝梦佳期。 【现代诗意译】望月思念远方的你 皎洁的明月从海上冉冉升起,你我虽远隔天涯,却共同拥有此明月。 有情之人天各一方,同怨长夜之难挨,孤身彻夜无......
  • 《庆余年2》迅雷下载高清完整版[第二季1080p][全集BT下载]
    《庆余年2》是根据猫腻所撰写的同名小说改编的电视剧,于2020年12月7日在湖南卫视首播。作为继第一季《庆余年》取得巨大成功后的续作,第二季再次席卷荧屏,引起了广大观众的热议与关注。 故事发生在北宋年间,曹操统一北方后,开始对南方展开攻势。而这一切与主人公张小凡......
  • Pytorch-08 实战:手写数字识别
    手写数字识别项目在机器学习中经常被用作入门练习,因为它相对简单,但又涵盖了许多基本的概念。这个项目可以视为机器学习中的“HelloWorld”,因为它涉及到数据收集、特征提取、模型选择、训练和评估等机器学习中的基本步骤,所以手写数字识别项目是一个很好的起点。我们的要做......
  • 洛谷[普及]:P1149 [NOIP2008 提高组] 火柴棒等式
    [NOIP2008提高组]火柴棒等式感谢题目提供者CCF_NOI题目描述给你n 根火柴棍,你可以拼出多少个形如A+B=C 的等式?等式中的A、B、C 是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。用火柴棍拼数字 的拼法如图所示:注意:1.加号与等号各自需要两根火柴棍;2.如果,则......
  • 【Test 08】优先队列、滑动窗口、DFS
    文章目录1.单词搜索2.除2操作3.dd爱框框1.单词搜索题目链接解题思路:DFS(深度优先遍历),用一个pos记录要匹配单词word的位置,每次与pos进行匹配判断(这样做的好处是不用把答案存下来)注意细节❗:①没有用flag来记录的话,所有在DFS暴搜的时候需要......
  • LSI 3008 RAID卡硬盘定位
    前提:系统上需要准备sas3ircu工具1、获取硬盘SN号 2、通过硬盘SN找到对应Enclosure和slot.从这里看Enc:2slot:1(这里的slot就是实际硬盘位置,服务器硬盘一般是从0开始,这里的slot1就是前置面板第二块盘)如果硬盘支持点灯操作,也可以通过上面命令点灯 ......
  • 满足 5G 通信的带宽需求,1ST040EH2F35I2LG,1ST085ES3F50I3VG,1ST085ES3F50E3VG,1ST110E
    说明Stratix®10FPGA和SoCFPGA大幅提高了性能、功效、密度和系统集成度。Stratix10采用创新HyperflexFPGA架构,将嵌入式多芯片互连桥接器(EMIB)、高级接口总线(AIB)和芯片组等技术结合在一起。™因此,与上一代高性能FPGA相比,Stratix10器件的性能提高了2倍。Stratix®10......