在 ES 6 中规定,如果一个对象具有 Symbol.iterator 这个属性,并且属性值是一个迭代器创建函数,那么我们称这个对象是可迭代的,这就是迭代器协议。
在 JS 中,数组其实就是一个典型的可迭代对象,我们可以用下面的方式验证:
js
/**
* 遍历一个数组并打印每个元素的值。
* 该代码不作为函数执行,而是直接在全局作用域中运行。
* 没有参数和返回值,因为它直接操作全局变量。
*/
const arr = [1, 2, 3, 4, 5]; // 定义一个包含1到5的数组
const iterator = arr[Symbol.iterator](); // 获取数组的迭代器
let data = iterator.next(); // 获取迭代器的第一个元素
while (!data.done) { // 当数据未遍历完时继续循环
console.log(data.value); // 打印当前遍历到的元素的值
data = iterator.next(); // 获取迭代器的下一个元素
}除了数组以外,很多伪数组其实 JS 都给我们做成了一个可迭代的对象,例如我们通过 document.querySelectorAll() 得到的伪数组,就是可迭代的,这一点读者可以自行验证。
有了迭代器,我们就可以很方便地遍历一个可迭代对象,但是有一个问题,这个遍历的算法需要我们自己写,我们需要自己调用该对象上的迭代器创建函数创建一个迭代器,然后使用循环对这个迭代器进行遍历,我们会发现这个过程非常机械通用,只要我们按照这个流程可以遍历所有可遍历对象,但是每次写一遍又很麻烦,所以 ES 6 提供了 for...of 循环来帮助我们干这个事。
js
const arr = [1,2,3,4,5];
for(const item of arr){
console.log(item);
}这样就可以很方便的实现数组遍历。
注意
for...of和for...in的区别,for...in是遍历属性列表,是可以拿到原型链的,也就是说for...in本质上用的并不是迭代器,但是for...of本质上用的就是迭代器实现的,实际上就是一个语法糖。综上所述,for...of只能遍历可迭代对象,但是for...in可以遍历所有对象。当然,除此之外,两者最大的区别其实是一个拿key,一个拿value
属性描述符中的enumerable 是指可遍历,而非可迭代。
展开运算符补充
展开运算符可以作用于可迭代对象,只要一个对象是可迭代的,那么一定可以展开,通过这种方式,其实可以将可迭代对象很方便地转换为数组,实际上展开运算符用的就是迭代器。