详解ES6语法之可迭代协议和迭代器协议


Posted in Javascript onJanuary 13, 2018

ECMAScript 2015的几个补充,并不是新的内置或语法,而是协议。这些协议可以被任何遵循某些约定的对象来实现。
有两个协议:可迭代协议和迭代器协议。

可迭代协议

可迭代协议允许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of 结构中什么值可以被循环(得到)。一些内置类型都是内置的可迭代对象并且有默认的迭代行为, 比如 Array or Map, 另一些类型则不是 (比如Object) 。

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环(详见下文)。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口,调用Symbol.iterator方法,返回该对象的默认遍历器。

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可迭代的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。

为了变成可迭代对象, 一个对象必须实现(或者它原型链的某个对象)必须有一个名字是 Symbol.iterator 的属性:

迭代器协议

该迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。

JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

迭代器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

Iterator 的遍历过程是这样的。

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

var someString = "hi";
typeof someString[Symbol.iterator]; // "function"
var iterator = someString[Symbol.iterator]();
iterator + "";  // "[object String Iterator]"
iterator.next()    // { value: "h", done: false }
iterator.next();   // { value: "i", done: false }
iterator.next();   // { value: undefined, done: true }

原生具备 Iterator 接口的数据结构如下。

  1. Array
  2. Map
  3. Set
  4. String
  5. TypedArray
  6. 函数的 arguments 对象
  7. NodeList 对象

注意对象是不具备 Iterator 接口的,一个对象如果要具备可被for...of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。

调用 Iterator 接口的场合

有一些场合会默认调用 Iterator 接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,解构赋值, 扩展运算符其实也会调用默认的Iterator 接口。

实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

  1. for...of
  2. Array.from()
  3. Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
  4. Promise.all()
  5. Promise.race()

for...of

for...of 循环是最新添加到 JavaScript 循环系列中的循环。

它结合了其兄弟循环形式 for 循环和 for...in 循环的优势,可以循环任何可迭代(也就是遵守可迭代协议)类型的数据。默认情况下,包含以下数据类型:String、Array、Map 和 Set,注意不包含 Object 数据类型(即 {})。默认情况下,对象不可迭代。

在研究 for...of 循环之前,先快速了解下其他 for 循环,看看它们有哪些不足之处。

for 循环

for 循环的最大缺点是需要跟踪计数器和退出条件。我们使用变量 i 作为计数器来跟踪循环并访问数组中的值。我们还使用 Array.length 来判断循环的退出条件。

虽然 for 循环在循环数组时的确具有优势,但是某些数据结构不是数组,因此并非始终适合使用 loop 循环。

for...in 循环

for...in 循环改善了 for 循环的不足之处,它消除了计数器逻辑和退出条件。但是依然需要使用 index 来访问数组的值.

此外,当你需要向数组中添加额外的方法(或另一个对象)时,for...in 循环会带来很大的麻烦。因为 for...in 循环循环访问所有可枚举的属性,意味着如果向数组的原型中添加任何其他属性,这些属性也会出现在循环中。这就是为何在循环访问数组时,不建议使用 for...in 循环。

注意: forEach 循环 是另一种形式的 JavaScript 循环。但是,forEach() 实际上是数组方法,因此只能用在数组中。也无法停止或退出 forEach 循环。如果希望你的循环中出现这种行为,则需要使用基本的 for 循环。

for...of 循环

for...of 循环用于循环访问任何可迭代的数据类型。

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {
 console.log(digit);
}

可以随时停止或退出 for...of 循环。

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {
 if (digit % 2 === 0) {
  continue;
 }
 console.log(digit); //1,3,5,7,9
}

不用担心向对象中添加新的属性。for...of 循环将只循环访问对象中的值。

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

Javascript 相关文章推荐
jquery 回车事件实现代码
Aug 23 Javascript
屏蔽网页右键复制和ctrl+c复制的js代码
Jan 04 Javascript
jquery插件之信息弹出框showInfoDialog(成功/错误/警告/通知/背景遮罩)
Jan 09 Javascript
jQuery UI 实现email输入提示实例
Aug 15 Javascript
table行随鼠标移动变色示例
May 07 Javascript
JavaScript编程中window的location与history对象详解
Oct 26 Javascript
Jquery uploadify上传插件使用详解
Jan 13 Javascript
javascript实现一个简单的弹出窗
Feb 22 Javascript
浅谈react-router HashRouter和BrowserRouter的使用
Dec 29 Javascript
解决vue-router中的query动态传参问题
Mar 20 Javascript
JavaScript实现与使用发布/订阅模式详解
Jan 19 Javascript
layUI的验证码功能及校验实例
Oct 25 Javascript
详解如何在React组件“外”使用父组件的Props
Jan 12 #Javascript
vue使用element-ui的el-input监听不了回车事件的解决方法
Jan 12 #Javascript
微信小程序实现的涂鸦功能示例【附源码下载】
Jan 12 #Javascript
js 索引下标之li集合绑定点击事件
Jan 12 #Javascript
简单的Vue SSR的示例代码
Jan 12 #Javascript
详解如何在react中搭建d3力导向图
Jan 12 #Javascript
关于axios不能使用Vue.use()浅析
Jan 12 #Javascript
You might like
让你的PHP同时支持GIF、png、JPEG
2006/10/09 PHP
php 小乘法表实现代码
2009/07/16 PHP
php将url地址转化为完整的a标签链接代码(php为url地址添加a标签)
2014/01/17 PHP
PHP数字字符串左侧补0、字符串填充和自动补齐的几种方法
2014/05/10 PHP
Laravel 5框架学习之数据库迁移(Migrations)
2015/04/08 PHP
php使用QueryList轻松采集js动态渲染页面方法
2018/09/11 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
JS 创建对象(常见的几种方法)
2008/11/03 Javascript
suggestion开发小结以及对键盘事件的总结(针对中文输入法状态)
2011/12/20 Javascript
js判断输入是否为数字的具体实例
2013/08/03 Javascript
多选列表框动态添加,移动,删除,全选等操作的简单实例
2014/01/13 Javascript
cookie的secure属性详解
2015/04/08 Javascript
轻松实现Bootstrap图片轮播
2020/04/20 Javascript
vue语法之拼接字符串的示例代码
2017/10/25 Javascript
在vue项目中引入vue-beauty操作方法
2019/02/11 Javascript
js计时事件实现圆形时钟
2020/03/25 Javascript
[02:30]DOTA2英雄基础教程 暗影恶魔
2013/12/17 DOTA
python之模拟鼠标键盘动作具体实现
2013/12/30 Python
python3实现抓取网页资源的 N 种方法
2017/05/02 Python
用Python抢火车票的简单小程序实现解析
2019/08/14 Python
OpenCV读取与写入图片的实现
2020/10/13 Python
基于Python制作一副扑克牌过程详解
2020/10/19 Python
性能服装:HYLETE
2018/08/14 全球购物
贝尔帐篷精品店:Bell Tent Boutique
2019/06/12 全球购物
微软马来西亚官方网站:Microsoft马来西亚
2019/11/22 全球购物
美国户外服装和装备购物网站:Outland USA
2020/03/22 全球购物
学校司机岗位职责
2013/11/14 职场文书
财务会计专业求职信范文
2013/12/31 职场文书
办加油卡单位介绍信
2014/01/09 职场文书
退休感言
2014/01/28 职场文书
护士的自我鉴定
2014/02/07 职场文书
初中生期末评语大全
2014/04/24 职场文书
数据保密承诺书
2014/06/03 职场文书
基层工作经验证明样本
2014/11/16 职场文书
法制教育主题班会
2015/08/13 职场文书
Windows11 Insider Preview Build 25206今日发布 更新内容汇总
2022/09/23 数码科技