迭代器
迭代器是什么?
- 迭代器(Iterator)是一种为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
什么是可迭代对象?
-
实现了正式Iterable接口
-
可以通过迭代器Iterator消费
实现了Iterator的内置类型:
- 字符串
- 数组
- 映射-Map()
- 集合-Set()
- arguments对象
- NodeList等DOM集合类型
使用迭代器实现功能的方法:
- for-of循环
- 数组解构
- 扩展操作符
- 使用Array.from()创建数组
- 创建映射
- 创建集合
- promise.race()接收由期约组成的可迭代对象
- promise.all()接收由期约组成的可迭代对象
- yield*操作符在生成器中使用
迭代器的作用
-
为各种数据结构,提供一个统一的、简便的访问接口
-
使数据结构的成员能够按某种次序排列
-
实现Iterator接口提供给
for...of消费
迭代器的简单使用
- 使用
Symbol.iterator获取迭代器 - 使用
next()返回迭代器对象
//使用数组演示---数组是可迭代对象
let niter = [1,2,3]
//迭代器工厂函数
//每个可迭代对象使用[Symbol.iterator]都能够找到与之绑定的迭代器工厂函数
console.log(niter[Symbol.iterator])
//执行迭代器工厂函数---返回一个迭代器
let iter = niter[Symbol.iterator]()
//使用.next()对迭代器进行操作
//next()返回迭代器对象---包含拉钩属性:done和value
// done---布尔值,表示是否可以再次调用next()取得下一个值,当done为true时已无值可取
console.log(iter.next())//{ value: 1, done: false }
console.log(iter.next())//{ value: 2, done: false }
console.log(iter.next())//{ value: 3, done: false }
console.log(iter.next())//{ value: undefined, done: true }
迭代器是一个一次性使用的对象,在需要使用时调用相应的迭代器工厂函数产生。
如果迭代器在迭代期间被修改,则迭代器也会反应相应的变化。
迭代器维护着一个指向可迭代对象的引用,因此迭代器会阻止垃圾回收程序回收可迭代对象。
自定义迭代器
思路
使用类实现,在类中创建一个迭代器工厂函数,被调用时将next方法返回,并使用闭包记录相应的值。
实现
class Counter {
//size为最大值
constructor(size){
this.size = size
}
//迭代器工厂函数
[Symbol.iterator](){
let value = 1
let size = this.size
return {
next(){//实现自定义next()功能---使用了闭包
if(value <= size){
return {value:value++,done:false}
}else{
return {value:undefined,done:true}
}
}
}
}
}
let test = new Counter(2)
let testIter = test[Symbol.iterator]()
console.log(testIter.next())
console.log(testIter.next())
console.log(testIter.next())
// { value: 1, done: false }
// { value: 2, done: false }
// { value: undefined, done: true }
提前终止迭代器
可选的return()方法用于指定在迭代器提前关闭时执行的逻辑。迭代器内部调用了return()方法中止迭代。
可能会中止迭代的操作:
- for-of循环通过break,continue,return或throw提前退出。
- 解构操作并未消费所有值。
在自定义迭代器中模拟:
class Counter {
//size为最大值
constructor(size){
this.size = size
}
//迭代器工厂函数
[Symbol.iterator](){
let value = 1
let size = this.size
return {
next(){//实现自定义next()功能---使用了闭包
if(value <= size){
return {value:value++,done:false}
}else{
return {value:undefined,done:true}
}
},
return(){
console.log('迭代终止')//提示
return {done:true}//并返回值
}
}
}
}
let test = new Counter(5)
for(let i of test){
if(i === 3){
break;
}
console.log(i)
}
// 1
// 2
// 迭代终止
数组的迭代器是不能关闭的。
生成器
生成器是一种极为灵活的代码结构,拥有在一个函数块内暂停和恢复执行的能力。
什么是生成器?
生成器函数:
定义生成器:在函数前面加一个*号
//生成器的形式是一个函数,所有只需要在函数声明的基础上加一个*号
function* fn(){}
function *fn(){}
function * fn(){}
箭头函数不能用来自定义生成器。
生成器对象:
-
调用生成器函数,会生成一个生成器对象。
-
生成器对象一开始处于暂停执行状态。
-
生成器也实现了Iterator接口,所有具备
next()方法,调用next()方法,会让执行器开始或恢复执行。
生成器的基本使用
- 返回生成器
next()
//定义一个生成器方法
function* fn(){
return '测试!!'
}
//调用生成器方法,返回一个生成器对象
let test = fn()
console.log(test)//Object [Generator] {} --- 证明生成器对象确实是一个对象
console.log(test.next)//[Function: next] --- 证明具备next方法
//初次调用next()---开始执行生成器
//调用next()返回一个对象{value:生成器函数的返回值,done:是否已不可继续获取}
console.log(test.next())//{ value: '测试!!', done: true }
通过yield关键字中断执行
-
yield关键字可以让生成器停止和开始执行。
-
生成器函数在遇到yield关键字之前---会正常执行。
-
生成器函数在遇到yield关键字之后---执行会停止,函数作用域的状态会被保留。
-
停止执行的生成器函数只能通过生在成器对象上调用next()方法来恢复执行。
//定义一个生成器方法
function* fn(){
yield '断定1'
yield '断定2'
return '测试!!'
}
//调用生成器方法,返回一个生成器对象
let test = fn()
console.log(test.next())//{ value: '断定1', done: false }
console.log(test.next())//{ value: '断定2', done: false }
console.log(test.next())//{ value: '测试!!', done: true }//可理解为没有了yield,done就为true
yield关键字只能用于生成器内部,用在其他地方会报错!!
将生成器作为可迭代对象
function* fn(){
yield '断定1'
yield '断定2'
return '测试!!'
}
for(let i of fn()){
console.log(i)
}
// 断定1
// 断定2
使用yield实现输入和输出
由外向内传值:
function* fn(){
console.log(yield)
console.log(yield)
console.log(yield)
}
let test = fn()
test.next('第一个参数无效')
test.next('测试值1')
test.next('测试值2')
// 测试值1
// 测试值2
产生可迭代对象--yield的合并写法
使用*和数组号实现
function* fn(){
yield * ['断定1','断定2']
//与yield '断定1';yield '断定2'等效
}
for(let i of fn()){
console.log(i)
}
// 断定1
// 断定2
生成器的返回值为done: true时的返回值,在迭代器中这个值为undefined。
使用yield*实现递归算法
function* fn(n){
if(n>0){
yield * fn(n-1)//使用yield *实现递归
yield n-1//使用yield提供等待数据
}
}
for(let i of fn(3)){
console.log(i)
}
// 0
// 1
// 2
生成器可以作为默认迭代器
原因:生成器对象实现了Iterable接口。
提前终止生成器
生成器除了具备next()和return()方法外,还具备throw()方法
return()
return()方法强制生成器进入关闭状态。提供给return()方法的值,就是终止迭代器对象的值。
//定义一个生成器方法
function* fn(){
yield * ['断点1','断点2','断点3']
}
let test = fn()
console.log(test.next())
console.log(test.return('结束!!'))
console.log(test.next())
// { value: '断点1', done: false }
// { value: '结束!!', done: true } ---return()中的参数为value值
// { value: undefined, done: true }
throw()
- throw()方法会在暂停的时候将一个提供的错误注入到生成器中,如果错误未被处理,生成器就会关闭。
- 如果生成器函数内部处理了这个错误,那么生成器就不会关闭,而且还可以恢复执行。错误会跳过对应的yield。
function* fn(){
yield * ['断点1','断点2','断点3']
}
let test = fn()
console.log(test.next())
console.log(test.throw('异常!!'))
console.log(test.next())
// { value: '断点1', done: false }
// D:\desktop\前端知识点\js\js每日一题\text.js:4
// }
// ^
// TypeError: The iterator does not provide a 'throw' method......