Skip to content

Latest commit

 

History

History
198 lines (143 loc) · 5.7 KB

迭代器.md

File metadata and controls

198 lines (143 loc) · 5.7 KB

什么是迭代器

迭代器是一种特殊的对象,它具有一些为迭代过程设计的接口。迭代器在结构上具有如下特征:

  1. 有一个next方法,每次调用都会返回一个结果对象
  2. 结果对象有两个属性:valuedonevalue表示下一个将要返回的值,而done是一个布尔状态,表达当前迭代是否已经结束。如果迭代还未完成,则done的值为false, 反之则为true

什么是可迭代对象

具有Symbol.iterator属性的对象,即为可迭代对象。在 ES6 中,所有集合对象,包括数组字符串Set集合Map 集合

既然说到了Symbol.iterator属性,那它是什么呢?其实它是一个特殊的函数。我们可以直接来使用这个属性来进行迭代:

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其实就是一个函数,它的返回值是一个特殊的对象,这个对象有两个属性:valuedone。当迭代完成时,done的值为true,而其值为undefined。迭代完成后可以继续调用,没有次数的限制,但其返回的结果对象为{value: undefined, done: true}

如何模拟实现一个迭代器

首先Symbol.iterator是一个函数,它应该接收一个可迭代对象。因此我们可以创建一个函数:

function createIterator(items){}

其次该函数调用的结果会返回一个拥有next方法的函数:

function createIterator(items){
    return {
        next: function(){}
    }
}

而调用该方法会返回一个包含了valuedone属性的结果对象。且该结果对象的value迭代的,而done是表示当前迭代是否完成。因此我们需要一个能够记录当前迭代位置的指针,并且每一次的调用都去移动该指针。

function createIterator(items){
    let i = 0;
    return {
        next: function(){
            return i < items.length ?
                {value: items[i++], done: false} :
                {value: undefined, done: true};
              }
        }
}

我们来测试一下该函数:

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属性的,那么我们便可以根据该属性来判断对象是否为可迭代对象:

function isIterable(object){
    return typeof object[Symbol.iterator] === 'function'
}

内置迭代器

ES6 为集合对象提供了内置迭代器,在大多数情况下我们无须自己手动实现。

entries(), values(), keys()都会返回一个迭代器,但是它们的表现不同。但要注意虽然字符串也是可迭代对象,但是它没有这三个内置的迭代器方法,不过它可以使用for...of进行迭代。

  • entries(),值为键值对的组合
  • values(),值为集合的值
  • keys(),值为集合的键名

我们可以使用for...of来访问迭代器。

entries迭代器

entries会返回一个数组,数组中包含两个元素,分别表示集合中每个元素的键和值。

由于集合对象的不同,返回值也有所不同。

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']

返回结果中第一个元素为键名,第二个元素为值。

let set = new Set(['a', 'b', 'c']);
for(let s of set.entries()){
    console.log(s);
}

//['a', 'a']
//['b', 'b']
//['c', 'c']

Set 集合的返回结果中,第一个元素和第二个元素都是值,即将值作为了键。

let arr = ['a', 'b', 'c'];
for(let item of arr.entries()){
    console.log(item)
}

//[0, 'a']
//[1, 'b']
//[2, 'c']

数组集合的返回结果中,第一个元素为数字索引,第二个值为集合的值。

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迭代器方法的。

valueskeys迭代器则分别是返回值和键。

不同集合类型的默认迭代器

如果我们直接使用for...of而不指定迭代器时,不同类型的集合会默认调用不同的迭代器。Map的默认迭代器是entries(),也很容易理解,因为对于Map来说,键和值是映射关系。而对于Set数组集合来说,默认迭代器是values

对于Map

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

let set = new Set(['a', 'b', 'c']);
for(let s of set){
    console.log(s);
}

//a
//b
//c

对于数组

let arr = ['a', 'b', 'c'];
for(let item of arr){
    console.log(item)
}

//a
//b
//c

迭代器的更多能力应该和生成器一起使用才能够体现出来,我们将在下一篇探讨迭代器与生成器的配合。