本文github地址:JavaScript_Everything 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士
什么是迭代器
迭代器是一种特殊的对象,它具有一些为迭代过程设计的接口。迭代器在结构上具有如下特征:
- 有一个
next
方法,每次调用都会返回一个结果对象
- 结果对象有两个属性:
value
和done
。value
表示下一个将要返回的值,而done
是一个布尔状态,表达当前迭代是否已经结束。如果迭代还未完成,则done
的值为false
, 反之则为true
什么是可迭代对象
具有Symbol.iterator
属性的对象,即为可迭代对象。在 ES6 中,所有集合对象
,包括数组
,字符串
,Set集合
,Map 集合
。
既然说到了Symbol.iterator
属性,那它是什么呢?其实它是一个特殊的函数。我们可以直接来使用这个属性来进行迭代:
javascript
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}
console.log(iterator.next()) // {value: undefined, done: true}
可以看到,Symbol.iterator
其实就是一个函数,它的返回值是一个特殊的对象,这个对象有两个属性:value
和done
。当迭代完成时,done
的值为true
,而其值为undefined
。迭代完成后可以继续调用,没有次数的限制,但其返回的结果对象为{value: undefined, done: true}
。
如何模拟实现一个迭代器
首先Symbol.iterator
是一个函数,它应该接收一个可迭代对象。因此我们可以创建一个函数:
javascript
function createIterator(items){}
其次该函数调用的结果会返回一个拥有next
方法的函数:
javascript
function createIterator(items){
return {
next: function(){}
}
}
而调用该方法会返回一个包含了value
和done
属性的结果对象。且该结果对象的value
是迭代
的,而done
是表示当前迭代是否完成。因此我们需要一个能够记录当前迭代位置的指针,并且每一次的调用都去移动该指针。
javascript
function createIterator(items){
let i = 0;
return {
next: function(){
return i < items.length ?
{value: items[i++], done: false} :
{value: undefined, done: true};
}
}
}
我们来测试一下该函数:
javascript
let iterator = createIterator([1,2,3]);
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}
console.log(iterator.next()) // {value: undefined, done: true}
判断对象是否为可迭代对象
既然可迭代对象是有Symbol.iterator
属性的,那么我们便可以根据该属性来判断对象是否为可迭代对象:
javascript
function isIterable(object){
return typeof object[Symbol.iterator] === 'function'
}
内置迭代器
ES6 为集合对象提供了内置迭代器,在大多数情况下我们无须自己手动实现。
entries()
, values()
, keys()
都会返回一个迭代器,但是它们的表现不同。但要注意虽然字符串也是可迭代对象,但是它没有这三个内置的迭代器方法,不过它可以使用for...of
进行迭代。
entries()
,值为键值对的组合values()
,值为集合的值keys()
,值为集合的键名
我们可以使用for...of
来访问迭代器。
entries迭代器
entries
会返回一个数组,数组中包含两个元素,分别表示集合中每个元素的键和值。
由于集合对象的不同,返回值也有所不同。
javascript
let map = new Map();
map.set('address', 'BeiJing');
map.set('phone', '110');
for(let m of map.entries()){
console.log(m)
}
//['address', 'BeiJing']
//['phone', '110']
返回结果中第一个元素为键名,第二个元素为值。
javascript
let set = new Set(['a', 'b', 'c']);
for(let s of set.entries()){
console.log(s);
}
//['a', 'a']
//['b', 'b']
//['c', 'c']
Set 集合的返回结果中,第一个元素和第二个元素都是值,即将值作为了键。
javascript
let arr = ['a', 'b', 'c'];
for(let item of arr.entries()){
console.log(item)
}
//[0, 'a']
//[1, 'b']
//[2, 'c']
数组集合的返回结果中,第一个元素为数字索引,第二个值为集合的值。
javascript
let str = 'abc';
for(let s of str.entries()){
console.log(s);
}
// TypeError: str.entries is not a function or its return value is not iterable
即字符串是没有entries迭代器方法的。
而values
与keys
迭代器则分别是返回值和键。
不同集合类型的默认迭代器
如果我们直接使用for...of
而不指定迭代器时,不同类型的集合会默认调用不同的迭代器。Map
的默认迭代器是entries()
,也很容易理解,因为对于Map
来说,键和值是映射关系。而对于Set
和数组
集合来说,默认迭代器是values
。
对于Map
javascript
let map = new Map();
map.set('address', 'BeiJing');
map.set('phone', '110');
for(let m of map){
console.log(m)
}
//['address', 'BeiJing']
//['phone', '110']
对于Set
javascript
let set = new Set(['a', 'b', 'c']);
for(let s of set){
console.log(s);
}
//a
//b
//c
对于数组
javascript
let arr = ['a', 'b', 'c'];
for(let item of arr){
console.log(item)
}
//a
//b
//c
迭代器的更多能力应该和生成器一起使用才能够体现出来,我们将在下一篇探讨迭代器与生成器的配合。
本文github地址:JavaScript_Everything 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士