什么是迭代器?
迭代器是一种特殊的对象,这个对象需要符合迭代协议(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 value
if (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,Set
var map = new Map();
map.set('a', {})
map.set('b', {})
console.log(map[Symbol.iterator])
// 展开运算符 实际上都是在调用迭代器
console.log(...map)
// arguments
function 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 of
const obj = {
a: "1",
b: "2",
c: "3"
}
// ES9(ES2018)中新增的一个特性,用的不是迭代器
console.log({ ...obj })
// 解构语法
const [haha, ...rest] = names
console.log(haha, rest)
// ES9(ES2018)中新增的一个特性,用的不是迭代器
const { a, ...args } = obj
console.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 = 30
console.log(value1)
yield
const value2 = 30
console.log(value2)
yield
const value3 = 30
console.log(value3)
yield
const value4 = 30
console.log(value4)
yield
console.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 如果传入了形参,就会赋值给x
console.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.js
function 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)
})
})
})
})