1、iterator迭代器是什么
js原有的表示数据的集合主要是数组和对象,而es6又新增了map和set用于表示新的数据结构,而用户又可以在内部自定义自己的数据结构,这是就需要一个接口来方便我们可以对他进行统一的遍历。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供 统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依 次处理该数据结构的所有成员)。部署了iterator的集合又叫做可遍历的集合
2、iterator的作用
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- 是ES6创造了一种新的遍历命令 for…of 循环,Iterator接口主要供 for…of 消费
3、遍历过程
- 创建一个指针对象,指向当前数据结构的起始位置,也就是说,遍历器对象事实上是一个指针对象
- 第一次调用next方法,可以将指针指向数据结构的第一个成员,以此类推
- 不断调用next方法,直到他指向数据结构的结束位置
每一次调用next方法,都会返回数据结构的当前成员信息。具体来说,就是返回以恶搞包含value和done两个属性的对象,其中value是当前成员值,done是表示是否结束
模拟:
let it = makeIterator([1, 2, 3])
function makeIterator(arr) {
let index = 0
return {
next() {
return {
value: index < arr.length ? arr[index++] : undefined,
done: index < arr.length ? false : true
}
}
}
}
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
4、默认iterator接口
Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制, 即 for…of 循环。当使用 for…of 循环遍历某种数据结构时, 该循环会自动去寻找 Iterator 接口。
es6规定,默认的iterator接口部署在数据结构的symbol.iterator属性上,有这个属性的数据结构称为可遍历的,symbol.iterator本身是一个函数,类似上面我们模拟的函数,执行这个函数会返回一个遍历器
原生部署了iterator接口的数据结构:
- Array
- Map
- Set
- String
- TypedArray
- 函数的arguments对象
- NodeList 对象
注意,对象没有
字符串是一个类数组对象,因此也原生具有iterator接口
对一个数据结构使用了iterator,事实上是将他变成了一个线性遍历的数据,因为iterator是一直以next的方法来遍历,无法乱序遍历
为类添加遍历器:
class rangeIterator {
constructor(start, stop) {
this.start = start
this.stop = stop
}
[Symbol.iterator]() {
return this
}
next() {
let cur = this.start
if (cur < this.stop) {
this.start++
return { value: cur }
} else {
return { done: true }
}
}
}
function range(start, stop) {
return new rangeIterator(start, stop)
}
for (const iterator of range(0, 3)) {
console.log(iterator)
}
为对象的prototype属性添加迭代器
function Obj(value) {
this.value = value
this.next = null
}
Obj.prototype[Symbol.iterator] = function() {
let iterator = { next: next }
let that = this
function next() {
if (that) {
let value = that.value
that = that.next
return { value: value }
} else {
return { done: true }
}
}
return iterator
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one) {
console.log(i); // 1, 2, 3
}
牢牢把握住,for-of方法是调用了[Symbol.ierator]这个属性的方法,然后返回了一个迭代器,然后调用迭代器的next方法来遍历
对于类数组对象,可以直接将数组的iterator方法给他
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.ite
rator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
5、调用iterator接口的场合
除了for…of外有些场合,会默认调用iterator接口
-
对set和array解构赋值时
-
使用扩展运算符时
等于说,只要有iterator接口,就可以使用扩展预算符将他变成数组
-
yield*
-
因为遍历数组会调用该接口,因此相当于只要使用了数组作为参数的场合,就是调用了
6、iterator与generator结合使用
7、遍历器对象的return(),throw()
如果是自己写的遍历器方法,可以省略这两个方法
return()方法一般用于以下情况:;
// 情况一
for (let line of readLinesSync(fileName)) {
console.log(line);
break;
}
// 情况二
for (let line of readLinesSync(fileName)) {
console.log(line);
continue;
}
// 情况三
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error();
}
而throw方法一般用于配合generator函数使用,一般遍历器用不到这个方法
8、for…of…方法
该方法调用的是iterator接口,具有该接口的对象都称为可遍历对象
js原有的for…in…方法只能遍历到数组的键,而不能直接遍历到数组的值,for…of…方法只能直接遍历到数组的值,而不能遍历到数组的键
要想for…of…能够遍历到数组的键,需要使用entries和keys方法
9、关于其他数据结构
计算生成的数据结构
有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6的数组、Set、 Map 都部署了以下三个方法,调用后都返回遍历器对象。
- entries() 返回一个遍历器对象,用来遍历 [键名, 键值] 组成的数组。对 于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用 entries 方法。
- keys() 返回一个遍历器对象,用来遍历所有的键名。
- values() 返回一个遍历器对象,用来遍历所有的键值。
这三个方法调用后生成的遍历器对象,所遍历的都是计算生成的数据结构。
计算生成的数据结构也可以进行遍历
类数组对象
类数组对象也可以使用for…of…进行遍历,最常见的是获取节点nodeList,字符串
// 字符串
let str = "hello";
for (let s of str) {
console.log(s); // h e l l o
}
但也不是所有的类数组对象都具有iterator接口,可以我们可以使用Array.from将该对象转成数组
对象
for…of…循环不能直接遍历对象,但是可以使用Object.keys来拿到键名的数组进行遍历然后取值,这种情况下for…in…可以直接拿到键名,会更加方便
10、与其他语法比较
原始for循环
优点:可以方便的拿到数组下标
缺点:写起来麻烦
forEach循环
优点:方便,可以拿到下标和对应值
缺点:无法中途跳出循环,break和return命令失效
forin循环
优点:可遍历数组的键名
缺点:
-
数组的键名是数字,但是 for…in 循环是以字符串作为键名“0”、“1”、“2”等 等。
-
for…in 循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原 型链上的键。
-
某些情况下, for…in 循环会以任意顺序遍历键名。
let obj = { '21': '21', '12': '12' } for (const key in obj) { console.log(obj[key]); } // 12 21
总之, for…in 循环主要是为遍历对象而设计的,不适用于遍历数组。
forof循环
优点:
- 有forin方法的简洁
- 没有forEach方法的缺点
- 提供了遍历所有书基本解构的统一操作接口