迭代器
在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议:
- 迭代器协议定义了产生一系列值(无论是有限还是无限)的标准方式
- 在JavaScript中这个标准就是一个特定的next方法
next方法:
- 无参数或一个参数的函数
- 返回一个拥有 done:Boolean、value:具体值/undefined 两个属性
- done:true 序列已经迭代完,value 可以省略;false 序列中可以产生下一个值
const names = ['aaa', 'bbb']
const nums = [12,23]
// 给names创建一个迭代器
function createArrayIterator(arr) {
let index = 0
return {
next() {
if (index < arr.length) {
return {done: false, value: arr[index++]}
}else {
return {done: true}
}
}
}
}
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // {done: false, value: 'aaa'}
console.log(namesIterator.next()); // {done: false, value: 'bbb'}
console.log(namesIterator.next()); // {done: true}
const numsIterator = createArrayIterator(nums)
console.log(numsIterator.next()); // {done: false, value: '12'}
console.log(numsIterator.next()); // {done: false, value: '23'}
可迭代对象
当一个对象变成一个可迭代对象的时候,就可以进行for...of 操作,它的本质是调用
@@iterator
- 当一个对象实现了iterable protocol协议时,它就是一个可迭代对象;
- 这个对象的要求是必须实现
@@iterator方法,在代码中我们使用Symbol.iterator访问该属性
将info变成一个可迭代对象
必须实现一个特定的函数:[Symbol.iterator]
这个函数需返回一个迭代器,这个迭代器用于迭代当前这个对象
const infos = {
friends: ['www', 'qqq', 'wqq'],
[Symbol.iterator]() {
let index = 0
const infosIterator = {
// next如果是普通函数,this就会是迭代器
next: () => {
if (index < this.friends.length) {
return {done: false, value: this.friends[index++]}
} else {
return {done: true}
}
}
}
return infosIterator
}
}
const iterator = infos[Symbol.iterator]()
console.log(iterator.next()); // {done: false, value: 'www'}
console.log(iterator.next()); // {done: false, value: 'qqq'}
// 可迭代对象可以进行for...of操作
for (const item of infos) {
console.log(item); // www qqq wqq
}
// 可迭代对象必然有一个[Symbol.iterator]函数
const stu = ['aa', 'bb']
const stuIterator = stu[Symbol.iterator]()
console.log(stuIterator.next()); // {done: false, value: 'aa'}
console.log(stuIterator.next()); // {done: false, value: 'bb'}
console.log(stuIterator.next()); // {done: true, value: undefined}
迭代key/value
// 迭代infos的key/value
const infos = {
name:'www',
age:'19',
height:1.66,
[Symbol.iterator](){
console.log(this,'this');
// const keys = Object.keys(this) // 拿到key
// const value = Object.values(this) // 拿到value
const entries = Object.entries(this) // 拿到key,value
let index = 0
return {
next() {
if (index < entries.length) {
return {done: false, value: entries[index++]}
} else {
return {done: true}
}
}
}
}
}
for (const item of infos) {
const [key,value] = item
console.log(key, value); // name www...
}
原生可迭代器对象
平时创建的很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的:
String、Array、Map、Set、arguments对象、NodeList集合
可迭代对象应用
JavaScript中语法:for ...of、展开语法、yield*、解构赋值
创建一些对象时:new Map()、new WeakMap()、new Set()、new WeakSet()
一些方法的调用:Promise.all()、Promise.race()、Array.from()
自定义类的迭代
class Person {
constructor(name,age,friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法
[Symbol.iterator]() {
let index = 0
const iterator = {
next:() => {
if(index < this.friends.length) {
return {done:false,value:this.friends[index++]}
}else {
return {done: true}
}
},
return(){
console.log('迭代器中断了'); // 便利过程中通过break、return、throw,解构的时候没有解构所有的值
return {done: true}
}
}
return iterator
}
}
// 语法糖
class Person {
constructor(name,age,friends) {
this.name = name
this.age = age
this.friends = friends
}
*[Symbol.iterator]() {
yield* this.friends
}
}
// 都变成可迭代对象
const p1 = new Person('www',12,['aa','bb','cc'])
for (const item of p1) {
console.log(item);
}
生成器
生成器是ES6中新增的一种函数控制使用的方案,可以更加灵活的控制函数什么时候继续执行、暂停执行等
- 生成器函数需要在function的后面加一个符号:*
- 代码的执行可以通过yield关键字来控制
- 生成器函数的返回值是一个生成器(生成器:是一种特殊的迭代器)
// 定义生成器函数
function* foo() {
console.log('1111');
yield
console.log('2222');
yield
console.log('3333');
}
const generator = foo() // 返回一个生成器对象
// 调用next方法执行函数,遇到yield就中断,继续执行再第=调用next
generator.next()
generator.next()
生成器传递参数
function* foo(meg1) {
console.log('1111',meg1);
const meg2 = yield 'aaa'
console.log('2222');
// return 'rrr' 被return后,调用next不会继续生成值了
console.log('3333',meg2);
const meg3 = yield 'bbb'
console.log('4444',meg3);
}
const generator = foo('meaagee1111') // 第一次传参
console.log(generator.next()); // {value: 'aaa', done: false}
console.log(generator.next('meaagee3333')); // {value: 'bbb', done: false}
// 提前结束函数
console.log(generator.return('结束'));
// 抛出异常
console.log(generator.throw(new Error('异常')));
console.log(generator.next('meaagee4444')); // {value: undefined, done: true}
生成器替代迭代器
const names = ['aaa', 'bbb']
function createArrayIterator1(arr) {
let index = 0
return {
next() {
if (index < arr.length) {
return {done: false, value: arr[index++]}
}else {
return {done: true}
}
}
}
}
// 对createArrayIterator1进行重构
function* createArrayIterator2(arr) {
for (let i = 0;i< arr.length;i++){
yield arr[i]
}
}
// 可以使用yield*来生产一个可迭代对象
// 相当于yield语法糖,会依次迭代这个可迭代对象,每次迭代其中一个值
function* createArrayIterator(arr) {
yield* arr
}
const namesIterator = createArrayIterato2(names);
console.log(namesIterator.next()); // {done: false, value: 'aaa'}
console.log(namesIterator.next()); // {done: false, value: 'bbb'}
console.log(namesIterator.next()); // {done: true}
生成某个范围之间的值
function* createRangeGenerator(start, end) {
for (let i = start; i < end; i++) {
yield i
}
}
const range = createRangeGenerator(3, 5)
console.log(range.next()); // {value: 3, done: false}
console.log(range.next()); // {value: 4, done: false}
console.log(range.next()); // {value: undefined, done: true}
异步请求
// 封装一个请求的方法:url -> promise(result)
function requestData(count) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(count)
}, 2000)
})
}
// 发送一次网络请求
requestData('https://wcc.com').then(res => console.log(res))
我们需要向服务器发送网络请求获取数据,一共需要发送三次请求
第二次的请求url依赖于第一次的结果
第三次的请求url依赖于第二次的结果
方式一:层层嵌套(回调地狱)
function getDate1() {
// 第一次请求
requestData('www').then(res1 => {
console.log('第一次的结果', res1)
// 第二次请求
requestData(res1 + 'qqq').then(res2 => {
console.log('第二次的结果', res2)
// 第三次请求
requestData(res2 + 'rrr').then(res3 => {
console.log('第二次的结果', res3)
})
})
})
}
getDate1()
方式二:使用 promise 链式调用(解决回调地狱)
function getDate2() {
// 第一次请求
requestData('www').then(res1 => {
console.log('第一次的结果', res1)
return requestData(res1 + 'qqq')
}).then(res2 => {
console.log('第二次的结果', res2);
return requestData(res2 + 'rrr')
}).then(res3 => {
console.log('第三次的结果', res3);
})
}
getDate2()
方式三:生成器
function* getDate3() {
const res1 = yield requestData('www') // yield会等到requestData('www')有返回值,才会继续往下执行
console.log('第一次的结果', res1)
const res2 = yield requestData(res1 + 'qqq')
console.log('第二次的结果', res2);
const res3 = yield requestData(res2 + 'rrr')
console.log('第三次的结果', res3);
}
const generator = getDate3()
generator.next().value.then(res1 => {
generator.next(res1).value.then(res2 => {
generator.next(res2).value.then(res3 => {
generator.next(res3)
})
})
})
// 自动化执行生成器函数(了解)
function execGenFn(genFn) {
// 1. 获取对应函数的generator
const generator = genFn()
// 2. 定义一个递归函数
function exec(res) {
const result = generator.next(res) // 对象类型:{done:'true',value:'值'}
if(result.done) return
result.value.then(res => {
exec(res)
})
}
exec() // 执行递归函数
}
execGenFn(getDate3)
方式四:async/await(最终方案)
async function getDate4() {
const res1 = await requestData('www')
console.log('第一次的结果', res1)
const res2 = await requestData(res1 + 'qqq')
console.log('第二次的结果', res2);
const res3 = await requestData(res2 + 'rrr')
console.log('第三次的结果', res3);
}
getDate4().catch(err => {
console.log('err:', err);
})
异步函数
// 异步函数写法
async function foo1() {}
const foo2 = async () => {}
const foo3 = async function() {}
class Person {
async foo4() {}
}
异步函数返回值
情况一:异步函数可以有返回值,返回值相当于被包裹到Promise.resolve中
情况二:如果我们的异步函数的返回值是Promise,状态由会由Promise决定
情况三:如果异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定
如果在async中抛出了异常,程序不会像普通函数一样报错,而是会作为promise的reject来传递
async function foo() {
// 1.返回普通的值
return [12,31]
'123'.filter() // 报错会作为promise的reject来传递
// 2. 返回一个promise
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
},2000)
})
// 3. 返回thenable对象
return {
then(resolve, reject) {
setTimeout(() => {
resolve('bbb')
}, 2000)
}
}
}
foo().then(res => console.log(res))
.catch(err => {
console.log(err)
}
)
异步函数 async function
普通函数不能使用await
- 使用await是后面会跟上一个表达式,这个表达式会返回一个Promise
- await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数
- 如果await后面是一个普通的值,那么会直接返回这个值
- 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值
- await 后面返回的Promise是 reject 状态,会将这个 reject 结果直接作为函数的Promise的reject值
// 1. 定义一些其他函数
async function test() {
console.log('test function');
const res = 'test'
}
async function bar() {
console.log('bar function');
return new Promise((resolve) => {
setTimeout(() => {
resolve('bar')
},2000)
})
}
function demo() {
console.log('demo function');
return{
then(resolve) {
resolve('demo')
}
}
}
// 2. 调用的入口函数
async function foo() {
const res1 = await test()
const res2 = await bar()
const res3 = await demo()
}
foo()