详解简单易懂的 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 相关文章推荐
JQuery 表格操作(交替显示、拖动表格行、选择行等)
Jul 29 Javascript
javascript 客户端验证上传图片的大小(兼容IE和火狐)
Aug 15 Javascript
JQuery中getJSON的使用方法
Dec 13 Javascript
js判断样式className同时增加class或删除class
Jan 30 Javascript
JavaScript中的object转换函数toString()与valueOf()介绍
Dec 31 Javascript
表单验证正则表达式实例代码详解
Nov 09 Javascript
AngularJS基础 ng-href 指令用法
Aug 01 Javascript
JS实现标签页切换效果
May 04 Javascript
Vuejs中使用markdown服务器端渲染的示例
Nov 22 Javascript
详解Vue Elememt-UI构建管理后台
Feb 27 Javascript
JavaScript复制内容到剪贴板的两种常用方法
Feb 27 Javascript
vue axios封装及API统一管理的方法
Apr 18 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合并数组array_merge函数运算符加号与的区别
2008/10/31 PHP
将PHP的session数据存储到数据库中的代码实例
2016/06/24 PHP
tp5.0框架隐藏index.php入口文件及模块和控制器的方法分析
2020/02/11 PHP
input、button的不同type值在ajax提交表单时导致的陷阱
2009/02/24 Javascript
xml 封装与解析(javascript和C#中)
2009/07/26 Javascript
JavaScript 申明函数的三种方法 每个函数就是一个对象(一)
2009/12/04 Javascript
页面使用密码保护代码
2013/04/10 Javascript
JQuery为页面Dom元素绑定事件及解除绑定方法
2014/04/23 Javascript
angularjs创建弹出框实现拖动效果
2020/08/25 Javascript
jquery实现的回旋滚动效果完整实例【附demo源码下载】
2016/09/20 Javascript
js以及jquery实现手风琴效果
2020/04/17 Javascript
对于js垃圾回收机制的理解
2017/09/14 Javascript
JavaScript利用键盘码控制div移动
2020/03/19 Javascript
vue+element获取el-table某行的下标,根据下标操作数组对象方式
2020/08/07 Javascript
[14:50]2018DOTA2亚洲邀请赛开幕式
2018/04/03 DOTA
win7安装python生成随机数代码分享
2013/12/27 Python
python anaconda 安装 环境变量 升级 以及特殊库安装的方法
2017/06/21 Python
pandas 使用apply同时处理两列数据的方法
2018/04/20 Python
Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】
2019/03/30 Python
Flask框架单例模式实现方法详解
2019/07/31 Python
python字典排序的方法
2019/10/12 Python
python sorted方法和列表使用解析
2019/11/18 Python
对Tensorflow中tensorboard日志的生成与显示详解
2020/02/04 Python
Selenium向iframe富文本框输入内容过程图解
2020/04/10 Python
全网最全python库selenium自动化使用详细教程
2021/01/12 Python
纯CSS3实现8组超炫酷鼠标滑过图片动画
2016/03/16 HTML / CSS
html5构建触屏网站之touch事件介绍
2013/01/07 HTML / CSS
俄罗斯金苹果网上化妆品和香水商店:Goldapple
2019/12/01 全球购物
简述Linux文件系统通过i节点把文件的逻辑结构和物理结构转换的工作过程
2012/04/17 面试题
电脑专业个人求职信范文
2014/02/04 职场文书
开业典礼致辞
2015/07/29 职场文书
英语导游欢迎词
2015/09/30 职场文书
2016年世界艾滋病日宣传活动总结
2016/04/01 职场文书
Python OpenCV快速入门教程
2021/04/17 Python
解决Swagger2返回map复杂结构不能解析的问题
2021/07/02 Java/Android
Python中Schedule模块使用详解 周期任务神器
2022/04/19 Python