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)
';