携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情 >>
迭代器
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
你可以理解为:迭代器是一个入口,为不同数据提供统一的入口。(数据要想传送过去必须通过iterator这个入口),任何数据结构只要添加Iterator接口,就可以完成遍历操作。
-
ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费
-
原生具备iterator接口的数据(可用for of遍历)
a) Array b) Arguments c) Set d) Map e) String f) TypedArray g) NodeList
扩展对比一下for...of和for...in在对象,数组中的区别
// 数组
let arr = [1,3,4,5,6];
// for of
for(let item of arr){
console.log(item);//数组的每一项
}
// for in
for(let item in arr){
console.log(item);//数组的下标
}
// 对象
let obj = {
name:"oko",
age:18
}
// for in
for (let item in obj) {
console.log(item);//item是对象的每一个属性名
}
// for of
for(let item of obj){
console.log(item);//obj is not iterable
}
迭代器的工作原理:
a) 创建一个指针对象,指向当前数据结构的起始位置
b) 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
c) 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
d) 每调用next方法返回一个包含value和done属性的对象
看到这里可能有小伙伴还是有点迷,下面我将用图片和代码的形式详细的解释一下:
图片:
1.创建一个指针对象(对象),指向当前数据结构(数组)的起始位置(不是指向第一个成员,是第一个成员之前)
2.调用指针对象上的next方法,现在指针对象指向1
3.同上不断调用next()方法,指针一直向后移动,每调用一次,指针指向下一个成员
4.每调用一次next的方法,方法的返回值是一个对象,对象的属性有value 和 done
注意:不断调用next()方法的执行过程是先调用next方法,返回了对象 然后再调用next方法
代码解释
//定义一个数组
let arry1 = [1,2,3,4,5];
//指针对象-----理解为就是个对象
let iterator = arry1[Symbol.iterator]();
定义一个变量存储这个指针对象,arry1[Symbol.iterator]()这里需要用到Symbol的知识 Symbol的内置方法中有一个
Symbol.iterator方法,返回该对象的默认遍历器(迭代器)。数组是数组,数组也是一个对象.
那么数组arr1={[Symbol.iterator]:function(){}}
如果你看不懂[Symbol.iterator]这是个啥 你需要去复习一下[]语法 或者看我的symbol文章(比这篇文章写得应该更清楚 我写到这里已经不想写下去了 感觉理的不清楚);
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: 4, done: false}
console.log(iterator.next());//{value: 5, done: false}
console.log(iterator.next());//{value: undefined, done: true}
console.log(iterator.next());//{value: undefined, done: true}
value指的是数据成员的值 done的布尔值表示是否要继续遍历下去 如果等于true则结束遍历
带大家看下这个指针对象长啥样
console.log(iterator);
手写迭代器
我们知道了迭代器以及迭代器的原理,接下来我们手写迭代器(对照着迭代器原理来写):
const game = {
name: '游戏',
members: [
'星际争霸',
'英雄联盟',
'骑马与砍杀',
'鬼谷八荒'
],
//1.设置迭代器的属性 因为对象身上没有迭代器这个属性 所以我们要添加上去
[Symbol.iterator]:function(){
// 6.设置index初始值为0 同时作为下标
let index = 0;
// 2.设置返回值 这个返回值就是指针对象 --->对象
return{//指针对象还有啥特点? 指针对象上有next方法
// 3.设置next方法
next:function(){
// 5.定义一个value值 值对应members数组中下标为[index]的值
let value = game.members[index];
// 7. 定义done 初始值为false 为啥初始值为false 因为遍历还没开始
let done = false;
// 8.结束遍历的判断条件
if (index >= game.members.length ) {//如果index的值大于数组的长度 则代表指针对象已经走到头了
done = true;//将done的值变为true 代表遍历结束了
}
//让index 自增
index++;
// 4.next方法也有一个返回值,返回一个对象,对象中有{value,done}属性
return{value,done}//完整的对象写法是:{value:value,done:done}
}
}
}
}
for(let item of game){
console.log(item);//输出结果如下图所示 为啥不是对象的属性 因为自己手写迭代器里面写的就是对象game.members这个数组
}
如果你对手写迭代器还有些疑惑 接下来我将根据原理和手写迭代器继续解释一下
let iterator = arry1[Symbol.iterator[]();
1.arry1[Symbol.iterator]() ===> arry1是数组也是对象 arry1 = {[Symbol.iterator]:function(){}}
arry1身上有[Symbol.iterator]这个Symbol的内置方法
2.arry1 = {[Symbol.iterator]:function(){return{}}}
arry1身上有[Symbol.iterator]这个Symbol的内置方法的返回值是一个对象 为啥是对象看
console.log(iterator.next());//{value: 1, done: false}.左边的就是对象 ()左边的是函数
3.arry1 = {[Symbol.iterator]:function(){return{next:function(return{value,done})}}}
arry1身上有[Symbol.iterator]这个Symbol的内置方法的返回值是一个对象,对象上有一个next方法,next方法返回值
也是一个对象 {value,done}
手写迭代器的一些注意点:
1.index的设置要设置在 [Symbol.iterator]这个方法中,为啥不能设置到next中?
因为每次调用next方法相当于在堆内存中每次开辟一个函数空间,index的值永远自增到1,所以得设置到next方法的外边.那么return{}里面为啥不行? 因为对象里面怎么let index = 0;
- if (index >= game.members.length ) 为啥等于(=)不就行了 还需要>=呢?
console.log(iterator.next()) //{value: '星际争霸', done: false}
console.log(iterator.next()) //{value: '英雄联盟', done: false}
console.log(iterator.next()) //{value: '骑马与砍杀', done: false}
console.log(iterator.next()) //{value: '鬼谷八荒', done: false}
console.log(iterator.next()) //{value: undefined, done: true}
console.log(iterator.next()) //{value: undefined, done: false}
console.log(iterator.next()) //{value: undefined, done: fasle}
当我在指针对象到达数组最后一个成员后 再调用一次就会发现 done的值又变为了false,
而我们知道只有done变为true才会接受循环,这显然与我们预期的不符
3. console.log(game[Symbol.iterator]().next())的value值始终等于星际争霸
我们知道 let iterator = game[Symbol.iterator]();可以推出
iterator.next() = game[Symbol.iterator]().next()
那么为啥iterator.next()输出正常 而game[Symbol.iterator]().next()输出错误呢
原理和1.index差不多 game[Symbol.iterator]()每次调用 都会生成一个指针对象 调用4次 生成四个指针对象
四个指针对象都只调用了一次next()方法 自然输出的结果就是数据的第一个成员了
而let iterator = game[Symbol.iterator]()只生成了一个指针对象 调用了四次next方法
4.我们看一下自己手写迭代器的 指针对象长啥样 let iterator = game[Symbol.iterator]()