默认情况下,如果是数组和Set集合,会逐一返回集合中所有的值。如果是Map集合,则按照Map构造函数参数的格式返回相同的数组内容。而WeakSet集合与WeakMap集合就没有内建的迭代器,由于要管理弱引用,因而无法确切地知道集合中存在的值,也就无法迭代这些集合了
【字符串迭代器】
自ES5发布以后,JS字符串慢慢变得更像数组了,例如,ES5正式规定可以通过方括号访问字符串中的字符(也就是说,text[0]可以获取字符串text的第一个字符,并以此类推)。由于方括号操作的是编码单元而非字符,因此无法正确访问双字节字符
var message = "A 𠮷 B" ; for (let i=0; i < message.length; i++) { console.log(message[i]); }
在这段代码中,访问message的length属性获取索引值,并通过方括号访问来迭代并打印一个单字符字符串,但是输出的结果却与预期不符
A
B
由于双字节字符被视作两个独立的编码单元,从而最终在A与B之间打印出4个空行
所幸,ES6的目标是全面支持Unicode,并且我们可以通过改变字符串的默认迭代器来解决这个问题,使其操作字符而不是编码单元。现在,修改前一个示例中字符串的默认迭代器,让for-of循环输出正确的内容
var message = "A 𠮷 B" ; for (let c of message) { console.log(c); }
这段代码输出以下内容
A
𠮷
B
这个结果更符合预期,通过循环语句可以直接操作字符并成功打印出Unicode字符
【NodeList迭代器】
DOM标准中有一个NodeList类型,document对象中的所有元素都用这个类型来表示。对于编写Web浏览器环境中的JS开发者来说,需要花点儿功夫去理解NodeList对象和数组之间的差异。二者都使用length属性来表示集合中元素的数量,都可以通过方括号来访问集合中的独立元素。而在内部实现中,二者的表现非常不一致,因而会造成很多困扰
自从ES6添加了默认迭代器后,DOM定义中的NodeList类型(定义在HTML标准而不是ES6标准中)也拥有了默认迭代器,其行为与数组的默认迭代器完全一致。所以可以将NodeList应用于for-of循环及其他支持对象默认迭代器的地方
var divs = document.getElementsByTagName("div"); for (let div of divs) { console.log(div.id); }
在这段代码中,通过调用getElementsByTagName()方法获取到document对象中所有div元素的列表,在for-of循环中遍历列表中的每一个元素并输出元素ID,实际上是按照处理数组的方式来处理NodeList的
高级迭代器
迭代器的基础功能可以辅助完成很多任务,通过生成器创建迭代器的过程也很便捷,除了这些简单的集合遍历任务之外,迭代器也可以被用于完成一些复杂的任务
【给迭代器传递参数】
迭代器既可以用迭代器的next()方法返回值,也可以在生成器内部使用yield关键字来生成值。如果给迭代器的next()方法传递参数,则这个参数的值就会替代生成器内部上条yield语句的返回值。而如果要实现更多像异步编程这样的高级功能,那么这种给迭代器传值的能力就变得至关重要
function *createIterator() { let first = yield 1; let second = yield first + 2; // 4 + 2 yield second + 3; // 5 + 3 } let iterator = createIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next(4)); // "{ value: 6, done: false }" console.log(iterator.next(5)); // "{ value: 8, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
第一次调用next()方法时无论传入什么参数都会被丢弃。由于传给next()方法的参数会替代上一次yield的返回值,而在第一次调用next()方法前不会执行任何yield语句,因此在第一次调用next()方法时传递参数是毫无意义的
第二次调用next()方法传入数值4作为参数,它最后被赋值给生成器函数内部的变量first。在一个含参yield语句中,表达式右侧等价于第一次调用next()方法后的下一个返回值,表达式左侧等价于第二次调用next()方法后,在函数继续执行前得到的返回值。第二次调用next()方法传入的值为4,它会被赋值给变量first,函数则继续执行。第二条yield语句在第一次yield的结果上加了2,最终的返回值为6
第三次调用next()方法时,传入数值5,这个值被赋值给second,最后用于第三条yield语句并最终返回数值8
【在迭代器中抛出错误】