迭代器
背景知识
1.什么是迭代?
从一个数据集合中按照一定的顺序,不断取出数据的过程
2.迭代和遍历的区别?
迭代强调的依次区数据,并不保证取多少,也不保证把所有数据取完
遍历强调的是整个数据依次全部取出
3.迭代器
对迭代过程的封装,在不同的语言中有不同的表现的形式,通常为对象
4.迭代模式
一种设计模式,用于统一迭代过程,并规范化迭代器规格:
- 迭代器应该具有得到下一个数据的能力
- 迭代器应该具有判断是否还有后续数据的能力
JS中的迭代器
JS规定,如果一个对象具有next方法,并且该对象返回一个对象,该对象的格式如下:
{value:值,done:是否迭代完成}
则认为该对象是一个迭代器
含义:
- next方法用于得到下一个数据
- 返回的对象
-
- value:下一个数据的值
- done:boolen是否迭代完成
实现一个迭代器
const arr = [1, 2, 3, 4, 5]
const iterator = {
i: 0,
next(){
let result = {
value: arr[this.i],
done: this.i >= arr.length
}
this.i++
return result
}
}
iterator.next()//{value:1,done:false}
iterator.next()//{value:2,done:false}
iterator.next()//{value:3,done:false}
iterator.next()//{value:4,done:false}
iterator.next()//{value:5,done:false}
实现一个创建迭代器的函数
function createIterator(arr){
let i = 0
return {
next(){
let result = {
value: arr[i],
done: i >= arr.length
}
i++
return result
}
}
}
const arr1 = [1, 2, 3, 4, 5]
const arr2 = [4, 5, 6, 7, 8]
const iter1 = createIterator(arr1)
const iter2 = createIterator(arr2)
iter1.next()//{value: 1, done: false}
iter2.next()//{value: 4, done: false}
实现一个斐波那契数列的迭代器
function createFeiboIterator(){
let prev1 = 1 //当前数的前一个数
let prev2 = 1 //当前数的前两个数
let n = 1//第几个数
return {
next(){
let value
if(n <= 2) {
value = 1
}else {
value = prev1 + prev2
}
let result = {
value,
done: false//永远不会迭代完成
}
prev2 = prev1
prev1 = result.value
n++
return result
}
}
}
const feiboIterator = createFeiboIterator()
feiboIterator.next() //{value:1,done:false}
feiboIterator.next() //{value:1,done:false}
feiboIterator.next() //{value:2,done:false}
feiboIterator.next() //{value:3,done:false}
feiboIterator.next() //{value:5,done:false}
可迭代协议与for-of循环
- 迭代器(iterator):一个具有next方法的对象,next方法返回下一个数据并且能指示是否迭代完成
- 迭代器创建函数(iterator creator):一个返回迭迭代器的函数
可迭代协议
ES6规定,如果一个对象具有知名符号属性Symbol.iterator,并且属性值是一个迭代器创建函数,则该对象是可迭代的(iterable)
//可迭代对象
const obj = {
[Symbol.iterator](){
return {
next(){
return {value:"1",done:false}
}
}
}
}
//obj就是一个可迭代对象,因为它具有知名符号属性Symbole.iterator,其属性值是一个迭代器创建函数
思考:如何知晓一个对象是否是可迭代的
//数组是一个可迭代对象
const arr = [1, 2, 3]
const iterator = arr[Symbol.iterator]()
iterator.next()//{value:1,done:false}
iterator.next()//{value:2,done:false}
iterator.next()//{value:3,done:false}
思考:如何遍历一个可迭代对象?
//遍历一个可迭代对象
const arr = [1,2,3]
const iterator = arr[Symbol.iterator]()
let result = iterator.next()
while(!result.done){
let item = result.value
console.log(item)
//下一次迭代
result = iterator.next()
}
for-of循环
for-of循环用于遍历可迭代对象,格式如下
const arr = [1,2,3]
for(let item of arr){
console.log(item)
}
//for of 必须遍历的是可迭代对象,其就是下方遍历方式的语法糖
const iterator = arr[Symbol.iterator]()
let result = iterator.next()
while(!result.done){
let item = result.value
console.log(item)
result = iterator.next()
}
只要是可迭代对象for-of都可以遍历,看下面的例子
const obj = {
a:1,
b:2,
[Symbol.iterator](){
//获取属性
let keys = Object.keys(this)
let i = 0
return {
next: () => {
let paramName = keys[i]
let paramValue = this[paramName]
let result = {
value:{paramName,paramValue},
done: i >= keys.length
}
i++
return result
}
}
}
}
for(const item of obj){
console.log(item)
}
//{paramName: "a", paramValue: 1}
//{paramName: "b", paramValue: 2}
展开运算符与可迭代对象
展开运算符可以作用于可迭代对象,这样就可以轻松的将可迭代对象转换为数组
const obj = {
a:1,
b:2,
[Symbol.iterator](){
//获取属性
let keys = Object.keys(this)
let i = 0
return {
next: () => {
let paramName = keys[i]
let paramValue = this[paramName]
let result = {
value:{paramName,paramValue},
done: i >= keys.length
}
i++
return result
}
}
}
}
const arr = [...obj]
console.log(arr)
//[{paramName: "a", paramValue: 1},{paramName: "b", paramValue: 2}]
生成器(Generator)
1.什么是生成器?
生成器是一个通过构造函数Generator创建的对象,生成器即是一个迭代器,同时又是一个可迭代对象
2.如何创建生成器?
生成器的创建必须使用生成器函数(Generator Function)
3.如何书写一个生成器函数?
//这是一个生成器函数,该函数一定返回一个生成器
function *method(){
}
const generator = method()
4.生成器函数内部是如何执行
生成器函数内部是为了给生成器的每一次迭代提供数据,
每次调用生成器的next的方法,将导致生成器函数运行到下一个yield关键字的位置
yield是一个关键字,该关键字只能在生成器内部使用,表达“产生”一个迭代数据
function *test(){
console.log("第一次执行")
yield 1
console.log("第二次执行")
yield 2
console.log("第三次执行")
}
const generator = test()
console.log(generator.next())
//"第一次执行" {value:1,done:false}
console.log(generator.next())
//"第二次执行" {value:2,done:false}
console.log(generator.next())
//"第三次执行" {value:undefined,done:true}
生成器简化迭代器的书写
const arr = [1,2,3]
function createIterator(arr){
let i = 0
return {
next(){
let result = {
value:arr[i],
done:i >= arr.length
}
i++
return result
}
}
}
const iterator = createIterator(arr)
iterator.next()
function *createGenerator(arr){
for(const item of arr){
yield item
}
}
const generator = createGenerator(arr)
generator.next()
简化斐波那契数列迭代器的案例
function createFeiboIterator(){
let prev1 = 1
let prev2 = 1
let n = 1
return {
next(){
let value
if(n <= 2){
value = 1
}else {
value = prev1 + prev2
}
let result = {
value,
done:false
}
prev2 = prev1
prev1 = result.value
n++
}
}
}
const iterator = createFeiboIterator()
iterator.next()
function *createFeiboGenerator(){
let prev1 = 1
let prev2 = 1
n = 1
while(true){
if(n <= 2) {
yield 1
}else{
let newValue = prev1 + prev2
yield newValue
prev2 = prev1
prev1 = newValue
}
n++
}
}
const generator = createFeiboGenerator()
generator.next()
5.有哪些需要注意的细节?
-
1)生成器函数可以有返回值,返回值出现在第一次done为true的value属性中
-
function *test(){ console.log("第一次执行") yield 1 console.log("第二次执行") yield 2 console.log("第三次执行") return 3 } const generator = test() console.log(generator.next()) //"第一次执行" {value:1,done:false} console.log(generator.next()) //"第二次执行" {value:2,done:false} console.log(generator.next()) //"第三次执行" {value:3,done:true} -
2)调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值
-
function* test(){ let info = yield 1 console.log(info) yield 2 + info } const generator = test() generator.next() //{value 1,done:false} generator.next() //5 //{value 7,done:false} -
3)第一次调用next方法的时候传参没有任何意义
-
4)在生成器函数内部可以调用其他生成器函数,但是注意要加上*号
-
function* test1(){ yield 1 } function* test(){ yield *test1() yield 2 } const generator = test() generator.next() //{value:1,done:false} generator.next() //{value:2,done:false} generator.next() //{value:undefined,done:true}
6.生成器的其他API
-
return方法:调用该方法可提前结束生成器函数,从而让整个迭代过程结束
-
function *test(){ yield 1 yield 2 yield 3 } const generator = test() generator.next()//{value:1,done:false} generator.return()//{value:undefined,done:true} generator.next()//{value:undefined,done:true} -
throw方法:调用该方法可以在生成器中产生一个错误