详解简单易懂的 ES6 Iterators 指南和示例


Posted in Javascript onSeptember 24, 2019

本文旨在分析理解 Iterators。 Iterators 是 JS中的新方法,可以用来循环任意集合。 在ES6中登场的Iterators。因其可被广泛使用,并且已在多处场景派上用场,我们将从概念上理解迭代器是什么,以及在何处使用它们和示例。我们还将看到它在JS 中的一些实现。

简介

假设有这样数组

const myFavouriteAuthors = [
 'Neal Stephenson',
 'Arthur Clarke',
 'Isaac Asimov', 
 'Robert Heinlein'
];

在某些情况下,希望返回数组中的所有单独值,以便在屏幕上打印它们、操作它们或对它们执行某些操作。

如何处理? 简单方法就是使用 for, while, for-of 方法。

如下:

详解简单易懂的 ES6 Iterators 指南和示例

现在,假设你拥有一个自定义数据结构来保存所有authors

详解简单易懂的 ES6 Iterators 指南和示例

myFavouriteAuthors 是一个对象,它包含另一个对象 allAuthorsallAuthors 包含三个数组,其中包含 fictionscienceFictionfantasy

现在,如果要求你循环遍历 myFavouriteAuthors 以获得所有的author,你的方法是什么? 你可能会尝试一些循环组合来获得所有数据。

但是,如果你这样做了 ——

for (let author of myFavouriteAuthors) { 
 console.log(author)
}
// TypeError: {} is not iterable

你将得到一个类型错误,说明该对象不可迭代。让我们看看什么是可迭代的,以及如何使对象可迭代。

可迭代对象与迭代器 (Iterables and Iterators)

在上一节中看到了问题,从我们的自定义对象中获取所有的author 是不容易的。我们需要某种方法,通过它我们可以有序地获取内部数据。

我们在 myFavouriteAuthors 中添加一个返回所有作者的方法 getAllAuthors。如:

详解简单易懂的 ES6 Iterators 指南和示例

这是一个简单的方法。它帮我们完成了获取所有author的功能。但是,这种实现可能会出现一些问题:

  • getAllAuthors 的名称非常具体。如果其他人正在创建自己的 myFavouriteAuthors,他们可能会将其命名为retrieveAllAuthors
  • 作为开发人员,我们总是需要知道返回所有数据的特定方法,在本例中,它被命名为getAllAuthors
  • getAllAuthors 返回的是字符串数组,如果另一个开发人员以这种格式返回一个对象数组,该怎么办:
[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]

开发人员必须知道返回所有数据的方法的确切名称和返回类型。

如果我们规定方法的名称和它的返回类型是固定不变的呢?

让我们将这个方法命名为 --- iteratorMethod

ECMA 也采取了类似的步骤来标准化在定制对象上循环的过程。但是,ECMA 没有使用名称 iteratorMethod,而是使用名称 Symbol.iterator。

Symbols 提供的名称是唯一的,不能与其他属性名称冲突。同时,Symbol.iterator 返回一个名为迭代器的对象,这个迭代器将拥有一个名为next的方法,该方法将返回一个具有键值为 valuedone 的对象。

值键 value 包含当前值,它可以是任何类型的,done 是布尔值,它表示是否获取了所有的值。

下图可以帮助建立可迭代对象、迭代器和next之间的关系,这种关系称为迭代协议。

详解简单易懂的 ES6 Iterators 指南和示例

根据Axel Rauschmayer博士的《探索JS》一书:

可迭代是一种数据结构,它希望使其元素对外部可访问,通过实现一个关键字是Symbol.iterator的方法来实现,该方法是迭代器的工厂,也就是说,它将创建迭代器。迭代器是一个指针,用于遍历数据结构的元素,我们将使用computed property语法来设置这个键,如下:

使用对象可迭代

因此,正如我们在上一节学到的,我们需要实现一个名为Symbol.iterator的方法

详解简单易懂的 ES6 Iterators 指南和示例

在第4行,我们创建迭代器。它是一个定义了next方法的对象。next方法根据step变量返回值。在第25行,我们检索iterator,27 行,我们调用next方法,直到 done的值为 true。

这正是for-of循环中发生的事情,for-of接受一个迭代器,并创建它的迭代器,它会一直调用next(),直到 done为 true。

JavaScript中可迭代对象(iterable)

JS 中的很多对象都是可迭代的。它们可能不是很好的察觉,但是如果仔细检查,就会发现迭代的特征:

  • Arrays and TypedArrays
  • Strings —— 遍历每个字符或Unicode代码点
  • Maps —— 遍历其键-值对
  • Sets —— 遍历元素
  • arguments  —— 函数中类似数组的特殊变量
  • DOM elements (Work in Progress)

JS中使用迭代的其他一些结构是:

for-of -- for-of 循环需要一个可迭代的对象,否则,它将抛出一个类型错误。

for (const value of iterable) { ... }

数组解构 -- 由于可迭代性,会发生析构。让我们来看看:

const array = ['a', 'b', 'c', 'd', 'e'];
const [first, ,third, ,last] = array;

等价于:

const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value

扩展操作符(…)

const array = ['a', 'b', 'c', 'd', 'e'];

const newArray = [1, ...array, 2, 3];

等价于:

const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next(); nextValue.done !== true; nextValue = iterator.next()) {
 newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)

Promise.allPromise.race 接受可迭代对象

Maps 和 Sets

让 myFavouriteAuthors 可迭代

下面是一个实现,它使myFavouriteAuthors 具有可迭代性:

const myFavouriteAuthors = {
 allAuthors: {
  fiction: [
   'Agatha Christie', 
   'J. K. Rowling',
   'Dr. Seuss'
  ],
  scienceFiction: [
   'Neal Stephenson',
   'Arthur Clarke',
   'Isaac Asimov', 
   'Robert Heinlein'
  ],
  fantasy: [
   'J. R. R. Tolkien',
   'J. K. Rowling',
   'Terry Pratchett'
  ],
 },
 [Symbol.iterator]() {
  // 获取数组中的所有作者
  const genres = Object.values(this.allAuthors);
  
  // 存储当前类型和索引
  let currentAuthorIndex = 0;
  let currentGenreIndex = 0;
  
  return {
   // Implementation of next()
   next() {
    // 根据当前的索引获取对应的作者信息
    const authors = genres[currentGenreIndex];
    
    // 当遍历完数组 authors是,oNotHaveMoreAuthors 为 true
    const doNothaveMoreAuthors = !(currentAuthorIndex < authors.length);
    if (doNothaveMoreAuthors) {
     // 加一继续访问下一个
     currentGenreIndex++;
     // 重置
     currentAuthorIndex = 0;
    }
    
    // 如果所有 genres 都遍历完了结,那么我们需要告诉迭代器不能提供更多的值。
    const doNotHaveMoreGenres = !(currentGenreIndex < genres.length);
    if (doNotHaveMoreGenres) {
     return {
      value: undefined,
      done: true
     };
    }
    
    // 如果一切正常,从当genre 返回 作者和当前作者索引,以便下次,下一个作者可以返回。
    return {
     value: genres[currentGenreIndex][currentAuthorIndex++],
     done: false
    }
   }
  };
 }
};

for (const author of myFavouriteAuthors) {
 console.log(author);
}

console.log(...myFavouriteAuthors)

通过本文获得的知识,你可以很容易地理解迭代器是如何工作的,这种逻辑可能有点难以理解。因此,理解这个概念的最佳方法是多多敲死代码,多多验证!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
获取页面高度,窗口高度,滚动条高度等参数值getPageSize,getPageScroll
Sep 22 Javascript
js有序数组的连接问题
Oct 01 Javascript
JS获取键盘上任意按键的值(实例代码)
Nov 12 Javascript
JS画线(实例代码)
Nov 20 Javascript
jQuery动态改变图片显示大小(修改版)的实现思路及代码
Dec 24 Javascript
浅析javascript中function 的 length 属性
May 27 Javascript
javascript绘制漂亮的心型线效果完整实例
Feb 02 Javascript
JS动态增删表格行的方法
Mar 03 Javascript
分享两段简单的JS代码防止SQL注入
Apr 12 Javascript
基于angular实现模拟微信小程序swiper组件
Jun 11 Javascript
微信小程序实现自上而下字幕滚动
Jul 14 Javascript
js中split()方法得到的数组长度问题
Jul 19 Javascript
layui实现下拉复选功能的例子(包括数据的回显与上传)
Sep 24 #Javascript
基于layui的下拉列表的数据回显方法
Sep 24 #Javascript
Layui动态生成select下拉选择框不显示的解决方法
Sep 24 #Javascript
layui动态渲染生成select的option值方法
Sep 23 #Javascript
微信小程序全局变量GLOBALDATA的定义和调用过程解析
Sep 23 #Javascript
layui-select动态选中值的例子
Sep 23 #Javascript
layui多图上传实现删除功能的例子
Sep 23 #Javascript
You might like
PHP中3种生成XML文件方法的速度效率比较
2012/10/06 PHP
基于php缓存的详解
2013/05/15 PHP
详解PHP中strlen和mb_strlen函数的区别
2014/03/07 PHP
PHP下载生成的csv文件及问题总结
2015/08/06 PHP
使用新的消息弹出框blackbirdjs
2008/10/16 Javascript
用jquery实现的模拟QQ邮箱里的收件人选取及其他效果(一)
2011/01/06 Javascript
用最通俗易懂的代码帮助新手理解javascript闭包 推荐
2012/03/01 Javascript
jquery 触发a链接点击事件解决方案
2013/05/02 Javascript
如何在JavaScript中实现私有属性的写类方式(二)
2013/12/04 Javascript
jQuery时间插件jquery.clock.js用法实例(5个示例)
2016/01/14 Javascript
Angular.js与Bootstrap相结合实现手风琴菜单代码
2016/04/13 Javascript
浅谈JavaScript 数据属性和访问器属性
2016/09/01 Javascript
vue组件间通信解析
2017/03/01 Javascript
axios向后台传递数组作为参数的方法
2018/08/11 Javascript
Python网站验证码识别
2016/01/25 Python
浅谈python字符串方法的简单使用
2016/07/18 Python
Python爬虫天气预报实例详解(小白入门)
2018/01/24 Python
Python Flask 搭建微信小程序后台详解
2019/05/06 Python
利用ImageAI库只需几行python代码实现目标检测
2019/08/09 Python
Django xadmin开启搜索功能的实现
2019/11/15 Python
Python获取二维数组的行列数的2种方法
2020/02/11 Python
基于HTML5的WebGL实现json和echarts图表展现在同一个界面
2017/10/26 HTML / CSS
HTML5页面直接调用百度地图API获取当前位置直接导航目的地的实现代码
2018/03/02 HTML / CSS
Bailey帽子官方商店:Bailey Hats
2018/09/25 全球购物
美国在线纱线商店:Darn Good Yarn
2019/03/20 全球购物
《美丽的小路》教学反思
2014/02/26 职场文书
《池塘边的叫声》教学反思
2014/04/12 职场文书
单位活动策划方案
2014/08/17 职场文书
小学优秀教师先进事迹材料
2014/12/16 职场文书
优秀员工事迹材料
2014/12/20 职场文书
一般纳税人申请报告
2015/05/18 职场文书
理想国读书笔记
2015/06/25 职场文书
简爱读书笔记
2015/06/26 职场文书
2015年学校医务室工作总结
2015/07/20 职场文书
《有余数的除法》教学反思
2016/02/22 职场文书
Django与数据库交互的实现
2021/06/03 Python