[javascript核心-20] 通俗易懂的迭代器🐘

本文github地址:JavaScript_Everything 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士


什么是迭代器

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

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

如何模拟实现一个迭代器

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

javascript 复制代码
function createIterator(items){}

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

javascript 复制代码
function createIterator(items){
    return {
        next: function(){}
    }
}

而调用该方法会返回一个包含了valuedone属性的结果对象。且该结果对象的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迭代器方法的。

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

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

如果我们直接使用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 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士

相关推荐
没有bug.的程序员32 分钟前
JAVA面试宝典 -《安全攻防:从 SQL 注入到 JWT 鉴权》
java·安全·面试
彤银浦36 分钟前
Web学习笔记3
前端·笔记·学习·html5
江城开朗的豌豆42 分钟前
退出登录后头像还在?这个缓存问题坑过多少前端!
前端·javascript·vue.js
江城开朗的豌豆1 小时前
Vue的'读心术':它怎么知道数据偷偷变了?
前端·javascript·vue.js
江城开朗的豌豆1 小时前
手把手教你造一个自己的v-model:原来双向绑定这么简单!
前端·javascript·vue.js
我在北京coding1 小时前
el-tree 懒加载 loadNode
前端·vue.js·elementui
江城开朗的豌豆1 小时前
v-for中key值的作用:为什么我总被要求加这个'没用的'属性?
前端·javascript·vue.js
angen20181 小时前
Ruby如何采集直播数据源地址
前端·chrome
goldenocean2 小时前
React之旅-05 List Key
前端·javascript·react.js
亮学长2 小时前
lodash不支持 Tree Shaking 而 lodash-es可以
大数据·前端·elasticsearch