11. 迭代器
迭代器(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
Iterator 是对象上的一个属性:Symbol.Iterator
11.1 for of 遍历
const arr = ['apple', 'banana', 'orange'] // for of 遍历 console.log('for of 遍历 ---- 获取的是键值') for (const iterator of arr) { console.log(iterator) } // for in 遍历 console.log('for in 遍历 ---- 获取的是键') for (const key in arr) { console.log(key) } console.dir(arr)
11.2 工作原理
a) 创建一个指针对象,指向当前数据结构的起始位置
let i = arr[Symbol.iterator]() console.log(i)
b) 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
console.log(i) console.log(i.next())
c) 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
d) 每调用 next 方法返回一个包含 value 和 done 属性的对象
console.log(i) console.log(i.next()) console.log(i.next()) console.log(i.next()) console.log(i.next()) console.log(i.next())
11.3 迭代器应用 – 自定义遍历数据
需要自定义遍历数据的时候,要想到迭代器。
注意:
返回的对象 done 一直为 false 会一直输出,for of 是以返回的对象 done 为 true 为停止条件。
// 声明一个对象 const obj = { name: '对象', content: ['属性1', '属性2', '属性3', '属性4'], // 1. 先添加iterator接口 [Symbol.iterator]() { // 5. 索引变量 let index = 0 // 6. 保存 obj 对象的 this const _this = this // Result of the Symbol.iterator method is not an object // 2. 返回的结果不是一个对象 return { // undefined is not a function // 3. 使用 for of 遍历时,会创建一个指针对象,指针对象中有一个next方法 next() { // 7. 判断是否遍历完成 if (index < _this.content.length) { // Iterator result undefined is not an object // 4. 需要返回一个对象 return { value: _this.content[index++], done: false, } } else { return { value: undefined, done: true } } }, } }, } // 使用 for of 遍历 // 每次返回的结果是 content 的成员 for (const iterator of obj) { console.log(iterator) }
12. 生成器
生成器是一个函数。生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
// 生成器函数 function 与函数名中间需要有一个 *,靠左靠右都行 function * gen() { console.log('hello') } // 生成器函数的执行 // 直接调用不能运行,需要调用next()方法 let i = gen() console.log(i) i.next()
生成器函数相当于一个迭代器对象
12.1 yield 语句
yield 可以看成函数的分割符,把函数分割成若干部分,由 next() 方法控制函数代码的执行。
function * gen() { console.log('第1个片段') yield 'yield 1' console.log('第2个片段') yield 'yield 2' console.log('第3个片段') yield 'yield 3' console.log('第4个片段') } let i = gen() i.next() // 执行第1个片段 i.next() // 执行第2个片段 i.next() // 执行第3个片段 i.next() // 执行第4个片段
生成器函数可以使用 for of 遍历:
function * gen() { // console.log('第1个片段') yield 'yield 1' // console.log('第2个片段') yield 'yield 2' // console.log('第3个片段') yield 'yield 3' // console.log('第4个片段') } let i = gen() // console.log( i.next() ) // 执行第1个片段 // console.log( i.next() ) // 执行第2个片段 // console.log( i.next() ) // 执行第3个片段 // console.log( i.next() ) // 执行第4个片段
for (const iterator of i) { console.log(iterator) }
12.2 生成器函数参数传递
function * gen( arg ) { console.log(arg) let res1 = yield 111 console.log(res1) let res2 = yield 222 console.log(res1) console.log(res2) let res3 = yield 333 console.log(res3) } // 获取迭代器对象 let i = gen( 'AAA' ) console.log(i.next()) // 执行 next 可以获取yield后面的值 // next参数可以传入实参,实参为yield语句的返回结果 // 第二个next传入的实参为第一个yield返回的结果 // 第x个next传入的实参从函数的第x个片段开始可以使用,为上一个yield语句的返回结果 console.log(i.next('BBB')) console.log(i.next('CCC')) console.log(i.next('DDD'))
12.3 生成器函数实例 - 1
生成器函数可以用于结果回调地狱。
回调地狱:
// 1s后输出111,2s后输出222,3s后输出333 setTimeout(() => { console.log(111) setTimeout(() => { console.log(222) setTimeout(() => { console.log(333) }, 3000) }, 2000) }, 1000)
function f1() { setTimeout(() => { console.log(111) // 继续向下调用 i.next() }, 1000) } function f2() { setTimeout(() => { console.log(222) i.next() }, 2000) } function f3() { setTimeout(() => { console.log(333) i.next() }, 3000) } // 声明生成器函数 function * gen() { yield f1() yield f2() yield f3() } // 迭代器对象 let i = gen() // 调用执行 i.next()