详谈ES6中的迭代器(Iterator)和生成器(Generator)(3)

这段for-of循环的代码通过调用values数组的Symbol.iterator方法来获取迭代器,这一过程是在JS引擎背后完成的。随后迭代器的next()方法被多次调用,从其返回对象的value属性读取值并存储在变量num中,依次为1、2和3,当结果对象的done属性值为true时循环退出,所以num不会被赋值为undefined

如果只需迭代数组或集合中的值,用for-of循环代替for循环是个不错的选择。相比传统的for循环,for-of循环的控制条件更简单,不需要追踪复杂的条件,所以更少出错

[注意]如果将for-of语句用于不可迭代对象、null或undefined将会导致程序抛出错误

【访问默认迭代器】

可以通过Symbol.iterator来访问对象默认的迭代器

let values = [1, 2, 3]; let iterator = values[Symbol.iterator](); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"

在这段代码中,通过Symbol.iterator获取了数组values的默认迭代器,并用它遍历数组中的元素。在JS引擎中执行for-of循环语句时也会有类似的处理过程

由于具有Symbol.iterator属性的对象都有默认的迭代器,因此可以用它来检测对象是否为可迭代对象

function isIterable(object) { return typeof object[Symbol.iterator] === "function"; } console.log(isIterable([1, 2, 3])); // true console.log(isIterable("Hello")); // true console.log(isIterable(new Map())); // true console.log(isIterable(new Set())); // true console.log(isIterable(new WeakMap())); // false console.log(isIterable(new WeakSet())); // false

这里的islterable()函数可以检查指定对象中是否存在默认的函数类型迭代器,而for-of循环在执行前也会做相似的检查

除了使用内建的可迭代对象类型的Symbol.iterator,也可以使用Symbol.iterator来创建属于自己的迭代器

【创建可迭代对象】

默认情况下,开发者定义的对象都是不可迭代对象,但如果给Symbol.iterator属性添加一个生成器,则可以将其变为可迭代对象

let collection = { items: [], *[Symbol.iterator]() { for (let item of this.items) { yield item; } } }; collection.items.push(1); collection.items.push(2); collection.items.push(3); for (let x of collection) { //1 //2 //3 console.log(x); }

在这个示例中,先创建一个生成器(注意,星号仍然在属性名前)并将其赋值给对象的Symbol.iterator属性来创建默认的迭代器;而在生成器中,通过for-of循环迭代this.items并用yield返回每一个值。collection对象默认迭代器的返回值由迭代器this.items自动生成,而非手动遍历来定义返回值

【展开运算符和非数组可迭代对象】

通过展开运算符(...)可以把Set集合转换成一个数组

let set = new Set([1, 2, 3, 3, 3, 4, 5]), array = [...set]; console.log(array); // [1,2,3,4,5]

这段代码中的展开运算符把Set集合的所有值填充到了一个数组字面量里,它可以操作所有可迭代对象,并根据默认迭代器来选取要引用的值,从迭代器读取所有值。然后按照返回顺序将它们依次插入到数组中。Set集合是一个可迭代对象,展开运算符也可以用于其他可迭代对象

let map = new Map([ ["name", "huochai"], ["age", 25]]), array = [...map]; console.log(array); // [ ["name", "huochai"], ["age", 25]]

展开运算符把Map集合转换成包含多个数组的数组,Map集合的默认迭代器返回的是多组键值对,所以结果数组与执行new Map()时传入的数组看起来一样

在数组字面量中可以多次使用展开运算符,将可迭代对象中的多个元素依次插入新数组中,替换原先展开运算符所在的位置

let smallNumbers = [1, 2, 3], bigNumbers = [100, 101, 102], allNumbers = [0, ...smallNumbers, ...bigNumbers]; console.log(allNumbers.length); // 7 console.log(allNumbers); // [0, 1, 2, 3, 100, 101, 102]

创建一个变量allNumbers,用展开运算符将smallNumbers和bigNumbers里的值依次添加到allNumbers中。首先存入0,然后存入small中的值,最后存入bigNumbers中的值。当然,原始数组中的值只是被复制到allNumbers中,它们本身并未改变

由于展开运算符可以作用于任意可迭代对象,因此如果想将可迭代对象转换为数组,这是最简单的方法。既可以将字符串中的每一个字符(不是编码单元)存入新数组中,也可以将浏览器中NodeList对象中的每一个节点存入新的数组中

内建迭代器

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyjydf.html