迭代器Iterator
迭代器
是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节
JavaScript迭代器标准
JavaScript中,迭代器是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol)
那么在JavaScript中,这个标准就是一个特定的next方法
next方法要求
next方法为无参数或一个参数的函数,返回一个拥有以下两个属性的对象:
done(boolean)
如果迭代器可以产生序列中的下一个值,则为false
如果迭代器已将序列迭代完毕则为true(此时value属性可选)value
迭代器返回的任何JavaScript值,done为true时可省略
const arr=['123','456','789'];/* 迭代器案例 */function createArrayIterator(arr){let index=0;return{next(){if(index<arr.length){return {done:false,value:arr[index++]};}else{return {done:true,value:undefined};}}}}const arrIterator=createArrayIterator(arr);console.log(arrIterator.next()) // {done:false,value:'123'}console.log(arrIterator.next()) // {done:false,value:'456'}console.log(arrIterator.next()) // {done:false,value:'789'}console.log(arrIterator.next()) // {done:true,value:undefined}
可迭代对象
它与迭代器不同,当一个对象实现了可迭代协议(iterable protocol)时,它就是一个可迭代对象
这个对象的要求是必须实现@@iterator方法,在代码中我们使用Symbol.iterator访问该属性,且该方法返回迭代器
/* 可迭代对象 */const iterableObj={arr:['123','456','789'],[Symbol.iterator]:function(){let index=0;return{next:()=>{if(index<this.arr.length){return {done:false,value:this.arr[index++]};}else{return {done:true,value:undefined};}}}}}const iterator=iterableObj[Symbol.iterator]();console.log(iterator.next()) // {done:false,value:'123'}console.log(iterator.next()) // {done:false,value:'456'}console.log(iterator.next()) // {done:false,value:'789'}console.log(iterator.next()) // {done:true,value:undefined}for(const value of iterableObj){console.log(value); // '123' '456' '789'}
内置可迭代对象
Array
const arr=[1,2,3]console.log(arr[Symbol.iterator]) // function
Map/Set
const set=new Set()for(const item of set){console.log(item)}
函数arguments对象
function foo(){console.log(arguments[Symbol.iterator]) // functionfor(const argument of arguments){console.log(argument) // 10 20 30}}foo(10,20,30)
可迭代对象的应用
JavaScript语法
for of展开运算符(spread syntax) ```javascript / 正因为是可迭代对象,才可使用展开语法 / const arr=[1,2,3] const newArr=[…arr,…iterableObj]
/ 但对于普通对象,其为不可迭代对象,却依旧可以使用展开运算符 这是因为这是ES9(ECMA2018)中新增特性,其使用的并不是迭代器,在V8引擎实现该特性是通过遍历对象的key和value再赋值处理 / const obj={name:’rv’,age:20} const newObj={…obj}
-解构语法```javascript/* 数组的解构赋值也是利用的迭代器 */const [a,b]=[1,2,3]/* 同上ES9新增特性,普通对象也支持解构语法跟是否为可迭代对象无关,其实现原理不同 */
- 创建一些对象时
/* 在创建Set对象时可传入可迭代对象 */const set=new Set([1,2,3]);/* Set(iterable?: Iterable<any>):Set<any> */
new Map([iterbale])new WeakMap([iterable])new Set([iterable])new WeakSet([iterable])- 一些方法的调用
Promise.all(iterable)Promise.race(iterable)Array.from(iterable)
迭代器return方法
可用于拦截拦截器的提前终止操作
const iterator={next(){···},return(){console.log('迭代器提前终止')/* 需有该返回值 */return {done:true,value:undefined}}}
生成器Generator
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活地控制函数什么时候继续执行、暂停执行等
生成器事实上是一种特殊的迭代器
生成器函数
生成器函数也是一种函数,但和普通函数有一些区别
- 生成器函数需要在function后添加符号
* - 生成器函数可以通过
yield关键字来控制函数的执行流程 - 生成器函数返回值是一个生成器(Generator)
/* '*'符号的位置没有规定 */function* foo0(){···}function * foo1(){···}function *foo2(){···}/* 生成器函数案例 */function* foo(){console.log('函数执行开始')console.log(100)yieldconsole.log(200)yieldconsole.log(300)yieldconsole.log('函数执行结束')}
调用生成器函数
调用生成器函数并不会执行函数体代码,只会返回一个生成器,需要使用生成器才能执行函数体代码
const generator=foo()
生成器的使用
类似迭代器,每次调用next方法则会执行一段代码,但当遇到return时,其会直接停止生成器
generator1.next() // '函数执行开始' 100generator1.next() // 200generator1.next() // 300generator1.next() // '函数执行结束'
生成器next方法
next方法的返回值
生成器next方法的返回值同iterator相同,为含有value和done属性的对象
但在不同的情况下next方法的返回对象的value值不同
yield
仅是yield关键字,返回{done:false,value:undefined}yield 语法/值
此时会执行该语法并返回返回值{done:false,value:returnValue}return
生成器遇到return关键字直接停止生成器,返回{done:true:value:returnValue}
const generator=foo()consolle.log(generator.next()) // {done:false,value:undefined}consolle.log(generator.next()) // {done:false,value:undefined}consolle.log(generator.next()) // {done:false,value:undefined}consolle.log(generator.next()) // {done:true,value:undefined}
next方法传入参数
生成器的next方法可以在调用的同时传入参数,并在函数体中通过yield关键字的返回值获取并使用
function bar(){console.log('函数开始执行')const value1=yieldconsole.log(value1)console.log('函数结束执行')}const generator=bar()generator.next('abc') // '函数开始执行'generator.next() // 'abc' '函数结束执行'
生成器return方法
return方法可以同next方法一样调用,但并不会执行下一段代码,而是提前终止生成器函数代码,
return方法一样可以传入参数,但参数会直接作为返回值value返回
const generator=foo()consolle.log(generator.next()) // {done:false,value:undefined}consolle.log(generator.return(10)) // {done:true,value:10}
生成器throw方法
throw方法可以同next方法一样调用,其效果和return方法类似,并不会执行下一段代码的同时会抛出错误
但并不一定会提前终止生成器函数,在yield关键字处捕获错误的情况下,依旧可以执行之后的生成器函数代码
throw方法一样可以传入参数,参数作为错误信息
yield*
yield* <iterableObj>其后接可迭代对象,效果等同于for循环yield
生成器替代迭代器
const arr=['123','456','789'];/* 迭代器 */function createArrayIterator(arr){let index=0;return{next(){if(index<arr.length)return {done:false,value:arr[index++]};elsereturn {done:true,value:undefined};}}}/* 生成器代替迭代器 */function* createArrayIterator(arr){// 方式一for(const itme of arr){yield item;}// 方式二yield* arr;}class ClassRoom{constructor(students){this.students=students}// 方式三[Symbol.iterator]=function*(){yield* this.students}// 方式四*[Symbol.iterator](){yield* this.students}}
