什么是迭代器?
迭代器是一种特殊的对象,这个对象需要符合迭代协议(iterator protocol),这个对象具有以下特点。
- 该对象有一个特定的next方法
const obj = {
next() {
}
}
const obj = {
next() {
return { done:(boolean) , value:...}
}
}
例如:创建一个迭代器对象来访问数组
-
next方法形参是一个无参数或者有一个参数的函数,返回一个应当拥有以下两个属性的对象
- done(boolean)
- 如果迭代器可以产生序列中的下一个值,则为 false。
- 如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。
- value
- 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。
const arr = ["a", "b", "c", "d", "e", "f"]let index = 0;const iteratorArr = {next() {if (index < arr.length) {return { done: false, value: arr[index++] }} else {return { done: true, value: undefined }}}}console.log(iteratorArr.next())console.log(iteratorArr.next())console.log(iteratorArr.next())console.log(iteratorArr.next())console.log(iteratorArr.next())console.log(iteratorArr.next())console.log(iteratorArr.next())
可迭代对象
可迭代对象和迭代器是一个不同的概念,当一个对象实现了iterator protocol协议时,它就是一个可迭代对象,这个对象必须实现@@iterator方法,在代码中我们可以用[Symbol.iterator]来访问该属性。
当一个对象变成可迭代对象的时候,进行某些迭代操作,比如for..of,其实就是调用的它的@@iterator方法。
- 可迭代对象代码
const arrIterator = {arr: ["a", "b", "c", "d", "e", "f"],//实现iterator protocol协议 , 使用[Symbol.iterator]访问@@iterator[Symbol.iterator]() {let index = 0;//迭代器 一个特殊的对象return {// 特定的方法next()next: () => {// 返回一个对象 包含两个属性 done valueif (index < this.arr.length) {return { done: false, value: this.arr[index++] };} else {return { done: true, value: undefined }}}}}}const iterator = arrIterator[Symbol.iterator]();console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());
原生迭代器对象
事实上我们平时创建的很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的,例如String,Array,Set,arguments,NodeList;
// String 内部实现了可迭代协议var str = "Hello World";console.log(str[Symbol.iterator]())// for .. of 实际上都是在调用迭代器for (const item of str) {console.log(item)}// 展开运算符 实际上都是在调用迭代器console.log(...str)// Map,Setvar map = new Map();map.set('a', {})map.set('b', {})console.log(map[Symbol.iterator])// 展开运算符 实际上都是在调用迭代器console.log(...map)// argumentsfunction foo(x, y, z) {console.log(arguments[Symbol.iterator])// 展开运算符 实际上都是在调用迭代器console.log(...arguments)}foo(1, 2, 3)
可迭代对象的应用
- JavaScript中语法:for ...of、展开语法(spread syntax)、yield*(后面讲)、解构赋值(Destructuring_assignment);
- 创建一些对象时:new Map([Iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable]);
- 一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable);
// 展开运算符 const names = ["haha", "gaga", "heihei"]console.log(...names) //names内部有迭代协议// 对象内部没有可迭代协议 也不能进行for ofconst obj = {a: "1",b: "2",c: "3"}// ES9(ES2018)中新增的一个特性,用的不是迭代器console.log({ ...obj })// 解构语法const [haha, ...rest] = namesconsole.log(haha, rest)// ES9(ES2018)中新增的一个特性,用的不是迭代器const { a, ...args } = objconsole.log(a, args)
迭代器的中断
比如遍历的过程中通过break、continue、return、throw中断了循环操作;
class classRoom {constructor(address, name, students) {this.address = address;this.name = name;this.students = students;}entry(student) {this.students.push(student);}[Symbol.iterator]() {let index = 0;return {next: () => {if (index < this.students.length) {return { done: false, value: this.students[index++] };} else {return { done: true, value: undefined };}},// 想要监听中断的话,可以添加return方法return() {return { done: true, value: undefined };}}}}const classroom = new classRoom("3幢5楼205", "计算机教室", ["james", "kobe", "curry", "why"])classroom.entry("lilei")for (const stu of classroom) {if (stu === 'kobe') break;console.log(stu)}
什么是生成器?
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
- 生成器也是一个函数
- 首先,生成器函数需要在function的后面加一个符号:*
- 其次,生成器函数可以通过yield关键字来控制函数的执行流程:
- 最后,生成器函数的返回值是一个Generator(生成器):
- 生成器实际上是一个特殊的迭代器
生成器函数执行
function* foo() {console.log("----start----")const value1 = 30console.log(value1)yieldconst value2 = 30console.log(value2)yieldconst value3 = 30console.log(value3)yieldconst value4 = 30console.log(value4)yieldconsole.log("----end----")return "ok"}// 这里不会调用函数,只是返回了一个遍历器对象,可以通过.next()调用const f = foo();console.log(f)f.next()f.next()f.next()f.next()f.next()f.next()
生成器传递参数
function* foo(num) {console.log("----start----")console.log(num)// x 不是yield的返回值,它是调用next() 回复当前 yield()执行传入的实参let x = yield '1' //第一次执行 f.next() 会暂停在这里 . 第二次执行yield 如果传入了形参,就会赋值给xconsole.log('x' + x); let y = yield '2'console.log('y' + y)let z = yield '3'console.log('z' + z)console.log("----end----")return "ok"}// 这里不会调用函数,只是返回了一个遍历器对象,可以通过.next()调用const f = foo(5);f.next() // 如果第一次就想传值 只能在foo() 这里面添加形参f.next(1)f.next(2)f.next(3)
生成器异步解决方案
// request.jsfunction requestData(url) {// 异步请求的代码会被放入到executor中return new Promise((resolve, reject) => {// 模拟网络请求setTimeout(() => {// 拿到请求的结果resolve(url)}, 2000);})}function* getData() {console.log('+++++')const res1 = yield requestData("第一次发送网络请求")const res2 = yield requestData("第二次发送网络请求" + res1)const res3 = yield requestData("第二次发送网络请求" + res2)console.log(res3 + '------')return 1}const generator = getData();// res1 res2 res3 都是形参generator.next().value.then(res => {console.log(res)generator.next('2').value.then(res => {console.log(res)generator.next('3').value.then(res => {console.log(res)generator.next('4').value.then(res => {console.log(res)})})})})