18、迭代器

最后更新于:2022-04-02 06:07:31

  ES6将迭代器和生成器内置到语言中,不仅简化了数据处理和集合操作,还弥补了for、while等普通循环的不足,例如难以遍历无穷集合或自定义的树结构等。   迭代器(Iterator)是一种用于迭代的对象,可有序的依次访问集合中的数据项。ES6制订了一套标准化的迭代器接口(包含3个方法,如表11所列),只要实现了这套接口都能成为迭代器。 :-: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/417263776d51dbf2ce8dc2fc1633e7e1_600x211.png) :-: 表11 迭代器接口   上表中的IteratorResult也叫迭代器结果,是一个特定形式的对象,它必须包含两个属性:value和done。value属性就是集合成员的值,done属性是一个布尔值,用于标记当前迭代是否结束。下面是一个简单的迭代器示例。 ~~~ function createIterator(items) { var index = 0; return { next() { var done = index >= items.length; return { value: items[index++], done: done }; } }; } var iterator = createIterator(["a", "b"]); iterator.next(); //{value: "a", done: false} iterator.next(); //{value: "b", done: false} iterator.next(); //{value: undefined, done: true} ~~~   当迭代结束时,done属性的值将会是true,而value属性在未提供返回值的时候就会设为undefined。 ## 一、可迭代对象   包含Symbol.iterator属性的对象被称为可迭代对象(Iterable),Symbol.iterator是一个特殊的内置符号(详见[第6篇](https://www.cnblogs.com/strick/p/10173774.html)),它的值是一个返回迭代器的方法。ES6内置了多种可迭代对象,例如集合(Array、TypedArray、Set和Map)、类数组对象(Arguments和NodeList)、字符串等,它们都含有各自默认的迭代器。下面的示例使用了数组的默认迭代器,通过next()方法依次遍历了它所包含的元素,返回结果与上一个示例类似。 ~~~ var arr = ["a", "b"], iterator = arr[Symbol.iterator](); iterator.next(); //{value: "a", done: false} iterator.next(); //{value: "b", done: false} iterator.next(); //{value: undefined, done: true} ~~~   可迭代对象的应用场景在前面的章节中已陆续介绍过,例如[第2篇](https://www.cnblogs.com/strick/p/10172721.html)的扩展运算符、[第3篇](https://www.cnblogs.com/strick/p/10172871.html)的解构赋值和[第12篇](https://www.cnblogs.com/strick/p/10225274.html)的Array.from()方法等。在接下来的章节中,还会将可迭代对象应用于for-of循环和yield\*。 ## 二、for-of   这是ES6新增的一种循环语句,当要遍历一个可迭代对象时,会先通过它的Symbol.iterator属性得到默认迭代器,再调用迭代器的next()方法,读取IteratorResult的value属性的值并赋给for-of语句中声明的变量,如此反复,直到done属性为ture时才终止遍历。而和其它循环语句一样,for-of循环也能通过跳转语句return、break和continue提前终止。下面分别对Set和Map两种数据结构进行for-of循环。 ~~~ var set = new Set(["strick", 28]), map = new Map([["name", "strick"], ["age", 28]]); /******************** "strick" 28 ********************/ for (var value of set) { console.log(value); } /******************** "name" "strick" "age" 28 ********************/ for (var [key, value] of map) { console.log(key, value); } ~~~   在前文中曾多次提到过3个迭代器方法,根据上面代码的输出结果可知,Set和Map的默认迭代器分别通过values()和entries()方法获得。 ## 三、字符串   字符串虽然是基础类型,但它能被隐式的封装成String对象,而String含有默认迭代器,因此可以进行for-of循环。在[第9篇](https://www.cnblogs.com/strick/p/10174360.html)字符串中曾提到过,JavaScript采用了UTF-16编码的Unicode字符集,在BMP中的字符可用一个编码单元表示,而在辅助平面中的字符则需要两个编码单元。以“向”和“𠮳”为例,前者存在于BMP中,后者存在于辅助平面中,当用for循环遍历下面的这段字符串(str)时,无法得到正确的字符。 ~~~ var str = "向𠮳"; //str.length的值为3 for (var i = 0; i < str.length; i++) { console.log(str[i]); } ~~~   而在改用for-of循环后(如下代码所示),就能得到预期的结果。由此可见,迭代器在处理字符串时更加安全可靠。 ~~~ for (var value of str) { console.log(value); } ~~~ ## 四、创建可迭代对象   普通的对象只要包含Symbol.iterator属性,并返回一个迭代器,就能摇身一变成为可迭代对象。在下面的代码中,iterable对象本身就是迭代器,因此Symbol.iterator属性的返回值可以是当前对象。注意,必须返回迭代器,否则会出现不可预料的错误。 ~~~ var iterable = { items: ["a", "b"], index: 0, [Symbol.iterator]() { return this; }, next() { var done = this.index >= this.items.length; return { value: this.items[this.index++], done: done }; }, return() { return { value: undefined, done: true }; } }; ~~~   在iterable对象中,包含return()方法,只有当迭代器终止遍历时,才会触发此方法。例如在for-of循环体中执行break、continue或return语句,甚至还能通过调用throw语句抛出自定义异常,提前结束代码的运行,以此实现触发条件,如下所示。 ~~~ for (var value of iterable) { throw new Error(); } ~~~ ***** > 原文出处: [博客园-ES6躬行记](https://www.cnblogs.com/strick/category/1372951.html) [知乎专栏-ES6躬行记](https://zhuanlan.zhihu.com/pwes6) 已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
';