我报名参与金石方案1期挑战——瓜分10万奖池,这是我的第2篇文章,点击检查活动详情
愉快的周末,我在试验室里清点礼品,当数到小黄鸭的时分,学长走了过来
学长:你这是不是一种迭代鸭子的行为。
我:emmm或许算是吧。
学长:那你给我讲一讲在JS中的迭代吧
这次我没有直接逃走,而是走到了学姐死后,哭诉学长对自己的镇压(苦楚流涕),于是乎学长掏出了红宝书阴沉沉地笑道:你学不学!
话不多说,本文将由浅入深地从根底与原理再到实战与手撕展开,探求JS迭代的原理,解析官方文档的案例
迭代的根底
举一个经典且刻在印象中的迭代比方:对数组的元素按序拜访,当遍历次数到达数组长度时中止迭代
let arr = [1,2,3]
for(let i = 0; i < arr.length; i++) {
console.log(arr[i])
}
上面我标注了按序拜访与中止迭代,思考一下,得出迭代必须满意如下两点
- 有某种固有的次序,能够按某种次序开始或许中止迭代。例如:在数组中次序对应着下标,遍历次数到达数组长度则中止
- 了解怎么拜访迭代方针的元素。例如:在数组中能够经过下标拜访对应的元素
迭代的原理
好,上面咱们现已掌握迭代的条件,那么将其带入Javascript
中,剖析一下其他可迭代的数据结构的迭代内部原理是怎么样的。
可迭代的数据结构
咱们运用for of
来对JS
中常见的数据结构进行迭代,成果如下
let str = "猪痞恶霸" // 猪 痞 恶 霸
let arr = [1] // 1
let obj = {
name:"猪痞恶霸"
} // obj is not iterable
let m = new Map() // ['猪痞恶霸', 1]
m.set(str,1)
let s = new Set([1]) // 1
for (item of str) {
console.log(item)
}
这儿看到只有obj
抛出了错误:obj is not iterable
,翻译为obj
不是可迭代目标,那么便是能够理解为,map
,set
,arr
等都是可迭代目标,这儿或许有人想到那obj
目标类型能够运用for in
进行迭代,为什么不能够叫做可迭代目标呢?其是因为内部的完成机制与其他数据成果的迭代不同,这点咱们放在实战部分聊。
上面这段话我标注了可迭代目标这一概念,而可迭代目标是什么呢?指的是map
,set
,str
这些数据成果吗?其实不然,官方文档针对可迭代目标给出了明确的要求:
- 完成正式的
interable
接口 - 能够经过
interator
迭代器消费
这是啥啊,看不明白,那么咱们下面来深化解析这两点要求。
interable
接口
interable
接口又称可迭代协议是一个比较笼统的东西,我将它理解为数据结构迭代操作的进口,不同的数据结构经过该接口能够完成相同的操作,而在javascript
中正是利用Symbol.iterator
来作为这个接口,假如不了解Symbol
的掘友能够看一下这篇文章:学长突然问我用过Symbol吗,我呜咽住了(准备挨骂)比较详尽地阐明了Symbol
的一些运用。
假如你了解Symbol
那么肯定知道内置Symbol
值是什么,Symbol.iterator
正是一种内置Symbol
值,它对应的外部操作即是for of
,当外部运用for of
办法时,内部就会启用以Symbol.iterator
为键的办法,下面咱们经过代码来剖析内部的构造
let arr = [1,2,3]
console.log(arr[Symbol.iterator]) // values() { [native code] }
values() { [native code] }
正是Symbol.iterator
对应的函数,而只有相关类型内置了Symbol.iterator
才会打印出来
let obj = {}
console.log(obj[Symbol.iterator]) // undefined
如上比方obj[Symbol.iterator]
为undefined
,这也是为什么说obj
无法进行for of
进行迭代,因为其没有内置Symbol.iterator
即interable
接口
承继interable
接口
interable
接口是能够被承继的,也便是说当父类完成了interable
接口,那么子类也会完成
class Son extends String {}
let son = new Son()
console.log(son[Symbol.iterator]) // [Symbol.iterator]() { [native code] }
咱们经过上面的代码能够看到Son
承继String
类,所以内部也完成了interable
接口
非常好,咱们经过上面的两点现已了解了可迭代目标的第一个要求:完成正式的interable
接口,那么interator
迭代器又是什么呢?
interator
迭代器
interator
迭代器本质上是目标,由interable
接口对应的函数履行生成
let arr = [1,2,3]
console.log(arr[Symbol.iterator]()) // Array Iterator{}
上面调用了arr[Symbol.iterator]()
办法获得了一个Array Iterator {}
目标,也便是迭代器,其实这种办法是手动获取的,真实迭代时的内部操作。
那interator
迭代器是怎么做到迭代数据结构的呢?他的核心便是运用next()
,下面咱们手动迭代一个字符串
let str = "猪痞恶霸"
let strInter = str[Symbol.iterator]()
strInter.next() // {value: '猪', done: false}
strInter.next() // {value: '痞', done: false}
手动迭代的过程是这样的
- 经过
str[Symbol.iterator]()
回来拿到迭代器 - 调用迭代器目标的
next()
办法 - 回来一个IteratorResult 目标比方
{value: '猪', done: false}
- 该目标内包括了两种特点对应着不同的作用
-
value
代表当时迭代的内容,done
符号是否能够进行下一次next()
- 若
done
为true
则不会进行下一次迭代,阐明迭代中止
迭代内部的机制咱们现已学习结束,下面开始操练时刻,来看看常用的迭代的实战场景
迭代的实战
咱们知道很多迭代的操作,比方最经典的for
,还有forEach
,for of
,for in
等等,那么同样是迭代,咱们该怎么选择正确的迭代方式呢?下面来逐个剖析
forEach
场景
forEach
办法常见的能够遍历目标有Set
,Map
,array
,NodeList
,它会迭代每个数据结构知道中止,因为无法在内部运用return
或许是break
所以无法中止迭代,所以说假如咱们的迭代无需中止,就能够运用forEach
let arr = [
{name:"猪痞恶霸",age:20},
{name:"Ned",age:21}
]
arr.forEach((item) => {
item.age+=1
})
比方运用forEach
来对数组元素进行批量处理
for of
场景
上面提到了forEach
的迭代是无法中止的,所以当咱们想中止迭代,那么咱们就能够运用for of
,比方在当元素满意某个条件的时分中止迭代
let arr = [1,3,10,7,10]
for(item of arr) {
console.log(item)
if(item%2 === 0) {
break
}
}
// 1,3,10
如上,当元素是偶数的时分那么就中止迭代,相对于forEach
来看for of
有个缺点便是无法获取当时元素对应的索引,而forEach
能够,所以咱们需求选择不同的迭代操作来适应当时的需求。
for in
场景
因为Object
没有内置迭代器,所以for of
无法对其进行迭代,咱们能够运用for in
办法来迭代目标,其回来的是特点的键名
let obj = {
name:"ned",
like:"man"
}
for(item in obj) {
console.log(item)
} // name like
当然假如你想要针对目标进行一些特性地迭代,比方迭代Symbol
特点,那么能够参阅一下这篇文章:JS遍历目标的七种办法
Very Good!!!经过上面的三种迭代场景,咱们学到了能够依据不同类型的迭代目标,参数需求,以及功能相关来判别运用哪种迭代操作,那么下面我给咱们带来点花的:手撕迭代
手撕迭代
来历:JS高程4,下面我会依据这个手撕来给咱们剖析,协助咱们进一步透彻迭代
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let count = 1,
limit = this.limit;
return {
next() {
if (count <= limit) {
return { done: false, value: count++ };
} else {
return { done: true, value: undefined };
}
},
};
}
}
let counter = new Counter(3);
for (let i of counter) { console.log(i); }
这是一个完成迭代的类Counter
,内部有构造函数与迭代器接口Symbol.iterator
-
创建实例的时分传入
limit
给予实例长度,其实这儿能够理解为咱们在初始化数组的时分给与数组长度 -
进行
for of
操作的时分对应内部Symbol.iterator
办法并调用,回来一个迭代器目标,该目标包括一个next
办法// 内部机制 counter[Symbol.iterator]() // { // {next: } // }
-
调用
next
办法,回来{ done: false, value: count++ }
格局数据,每次调用为count
和limit
做判别,假如超过范围,那么将回来的目标的done
特点符号为false
阐明迭代结束
最终
经过学长的鞭笞与学姐的鼓励,我又一次地领会了迭代器地微妙,收拾并输出了该篇文章,假如对你有协助就点小爱心吧!