这几天研究了一下迭代器模式,期间有一段时间经常搞不太懂一些概念与概念之间的关系,今天来整理一下。
迭代的一些基本概念
- 循环是迭代机制的基础
- 迭代在一个有序集合上进行
- 按照顺序反复多次执行一段程序,通常会有明确的终止条件
我的理解:
实际上就是为了优化循环,各种数据结构的循环。而且各种数据结构循环起来可以千奇百怪,为了合理的统一,就有了迭代器模式。
关键词
- 可迭代对象
- 可迭代协议(
Iterable
接口) - 迭代器
- 迭代器协议(
Iterator
接口) - 迭代器对象(
IteratorResult
)
关系图
QA式学习
Q:什么是可迭代对象?
A:满足可迭代协议(实现Iterable
接口)的对象。
Q:可迭代协议是啥?
A:就是Iterable
接口。满足可迭代协议的对象应该满足以下条件:
- 能够创建实现了
Iterator
接口的对象(实际上就是迭代器) - 且可以通过迭代器
Iterator
消费。
举个例子,数组,集合,映射等等都满足可迭代协议。他们都可以用for...of
遍历。
Q:什么是消费?
A:可以理解为遍历。有一个相关的概念“耗尽”,可以理解为遍历结束了。
Q:怎么满足可迭代协议?
A:你的对象需要有一个键名为Symbol.iterator
的属性,指向一个迭代器工厂函数。啥意思嘞?就是说
//jojo是一个可迭代对象(数组) console.log(jojo[Symbol.iterator]); // ƒ values() { [native code] }
JavaScript
Copy
作为一个工厂函数,我们调用它的话就应该可以生成一个迭代器
console.log(jojo[Symbol.iterator]()); //Array Iterator {}
JavaScript
Copy
Q:什么是迭代器?
A:首先,迭代器是用于迭代与其关联的对象的一次性使用对象,迭代器满足迭代器协议(实现Iterator
接口)。一次性是指其在完成一次遍历(消费)之后就会被“耗尽”,这个时候再想遍历只能通过重新生成一个迭代器。
Q:迭代器协议说了啥?
A:迭代器协议说,只要你实现了Iterator
接口,你就是个迭代器辣!你至少需要有一个next()
方法来返回一个迭代器对象IteratorResult
。这个对象包含两个属性:done
和value
。其中done
为一个布尔值,当done
不为真时表示还可以通过调用next()
取得下一个值,当done
为真时,就可以被称为“耗尽”了。当done
不为真时,value
对象表示可迭代对象的下一个值,而当done
不为真时,value
就自然为undefined
了。
模拟for...of
在MDN上有这么一段话
Thefor...of
statement creates a loop iterating over iterable objects, including: built-inString
,Array
, array-like objects (e.g.,arguments
orNodeList
),TypedArray
,Map
,Set
, and user-defined iterables.
大意
for...of
语句为可迭代对象创建循环迭代。这些可迭代对象包括内置的String
,Array
,和一些类数组对象(比如arguments
和NodeList
),TypedArray
,Map
,Set
和用户定义的可迭代对象。
也就是说实际上使用for...of
来遍历正是迭代器模式的一个应用。通过模拟一个数组的for...of
遍历来更好地理解一下迭代器。
当我们普普通通for..of
的时候发生了甚么事呢?
let arr = [1,2,3,4,5]; for(let item of arr) console.log(item);
JavaScript
Copy
实际上上面的代码可以等价于下面的代码。
let arr = [1,2,3,4,5]; let it = arr[Symbol.iterator]();//it->迭代器,通过调用arr[Symbol.iterator]()来生成 let item = it.next();//item->迭代器对象,包含着done和value两个属性 while(!item.done) {//当迭代器没有耗尽时 console.log(item.value);//可以通过item.value来访问当前迭代到的值 item = it.next();//继续迭代 }
JavaScript
Copy
自定义迭代器
刚刚提到了,for...of
同样可以用于遍历用户定义的可迭代对象。一般的可迭代对象是不可以用for...of
来遍历的,但是我们可以通过改造我们的对象,使其满足可迭代协议来使用for...of
let colors = { blue : "蓝色", green : "绿色", yellow : "黄色" }; for(let color of colors) console.log(color);
JavaScript
Copy
现在我们让color对象满足Iterable
接口
colors[Symbol.iterator] = function() { let keys = Object.keys(colors); let index = 0; return { next : function() { if (index < keys.length) { return { value : colors[keys[index++]], done : false } } return { done : true } } } }
JavaScript
Copy
然后再次for...of
循环,可以发现没有报错,并且正常循环输出了。