循环、迭代、递归区别
循环、迭代、递归均有“重复”的含义。
种类 | 定义 |
---|---|
循环 | 最基础的概念,凡是重复执行某行代码,都称之为循环 |
迭代 | 数学中的迭代表示在多次循环中逐步接近结果;编程中的迭代表示按顺序访问线性结构中的每一项 |
递归 | 在函数内调用自身,将复杂情况逐步转变为基本情况 |
迭代与循环的区别:循环代码中参与运算的变量同时是保存结果的值,当前保存的值作为下一次循环的初始值。
迭代器模式
可迭代对象(Iterable)
可迭代对象(Iterable):数组、集合和具有类似数组行为的数据结构。它们包含的元素是有限的,而且都具有无歧义的遍历顺序。
迭代器(Iterator):迭代器(Iterator)可以消费(Consume)可迭代对象(Iterable)。
1.迭代器是按需创建的一次性对象,迭代器API使用next()方法在可迭代对象中遍历数据;每次成功调用next(),返回一个IteratorResult对象;如果不调用next(),则无法知道迭代器当前的位置。
2.每个迭代器都会关联一个可迭代对象。
3.迭代器无需了解与其相关联的可迭代对象的结构,只需要知道如何取得连续的值。
内置可迭代类型
- 字符串:String
- 数组:Array
- 映射:Map
- 集合:Set
- arguments:传递给函数的参数的类数组对象。
- NodeList等DOM集合类型
接收可迭代对象的原生语言
- for...of:循环遍历
- 数组解构:[...Array]
- Array.from()
- 创建集合:
new Set(可迭代对象)
- 创建映射:
new Map(可迭代对象)
- Promise.all()接收由期约组成的可迭代对象
- Promise.reac()接收由期约组成的可迭代对象
- yield* 操作符,在生成器中使用
可迭代协议
作用:允许 JavaScript 对象定义或定制它们的迭代行为。
要成为可迭代对象, 一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性:
属性 | 值 |
---|---|
[Sysbol.Iterator] | 一个无参的函数,其返回值为一个符合迭代器协议的对象 |
迭代执行流程
当一个对象需要迭代的时候,首先,会不带参数调用它的@@iterator方法,然后使用此方法返回的迭代器获取迭代的值。
@@iterator方法的调用将作为可迭代对象的方法进行调用,所以在此函数内部可以访问可迭代对象的属性,以决定在迭代过程中提供什么。
@@iterator方法可以普通函数,也可以是生成器函数,以便在调用时返回迭代器对象。在此生成器函数的内部,可以使用yield提供每个条目。
迭代器协议
迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个,所有的值被迭代完毕后,则会返回一个默认返回值。
迭代器的标准:只有实现一个拥有以下语义的next()方法,一个对象才能成为迭代器:
属性 | 值 |
---|---|
next | 一个无参函数,返回以下类型的对象: done(boolean):迭代完毕为false,迭代未完毕为true value:返回任何JavaScript值,done为true可以忽略 |
迭代器是按需创建的一次性对象,迭代器API使用next()方法在可迭代对象中遍历数据;每次成功调用next(),返回一个IteratorResult对象;如果不调用next(),则无法知道迭代器当前的位置。
迭代器的注意事项
[1]创建迭代器(Iterator):访问可迭代对象中的@@Iterator方法。let iterator = arr[Symbol.iterator]();
[2]访问可迭代对象中的值:调用next()方法,直至不产生新值。只要done的状态为true,表示已经访问完毕,后面再调用next()方法,返回值相同。
[3]不同迭代器之间没有关系。
[4]迭代器与可迭代对象进行绑定,如果可迭代对象修改了,迭代器也会反映相应的变化。
[5]next()方法必须返回一个对象,否则会抛出一个TypeError异常。
提前终止迭代器
可选的return()方法用于指定在迭代器提前关闭时执行的逻辑。提前终止迭代器可能的情况如下:
- for-of循环通过break、continue、return、throw提前退出。
- 结构操作并未消费所有值。
如果迭代器没有关闭,则还可以继续从上次离开的地方继续迭代。如数组的迭代器就是不能关闭的。
生成器
生成器:拥有在一个函数块内暂停和恢复代码执行的能力。该能力的体现:自定义迭代器和实现协程。
生成器基础
生成器的形式是一个函数,函数名称前面加一个星号(*)表示它是一个生成器。只要定义函数的地方就可以定义生成器。
生成器函数实现了Iterable接口,因此可以用在任何消费可迭代对象的地方。
语法
function * 生成器函数() {}
class {
* 生成器函数() {}
}
注意事项:
1、生成器函数不能当做构造器使用。
2、箭头函数不能用来定义生成器函数。
3、生成器函数返回一个生成器对象(Genertor)
Generator
原型对象中常用的方法有:
方法 | 作用 |
---|---|
Generator.prototype.next() | 返回一个由yield 表达式生成的值 |
Generator.prototype.return() | 返给定的值并结束生成器 |
Generator.prototype.throw() | 向生成器抛出一个错误 |
生成器函数执行流程:
1.调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行的状态。
2.调用生成器函数的next()方法,其内函数体开始执行直至遇到yeild
的位置为止。【生成器函数只会在生成器对象执行next()方法的时候执行】
中断生成器:yield和*yield
yield
yield
关键字用来暂停和恢复一个生成器函数,即生成器的能力体现。生成器函数在遇到yield关键字之前会正常执行,遇到此关键字后,执行会停止,函数作用域的状态也会保留。停止执行的生成器函数只能通过在生成器对象上调用next()方法来恢复执行。
语法:[rv] = yield [expression]
expression:定义通过迭代器协议从生成器函数返回的值。如果省略,则返回undefined
。
rv:返回传递给生成器的next()
方法的可选值,以恢复其执行。
作用:
1、yield
关键字使生成器函数执行暂停,yield
关键字后面的表达式的值返回给生成器的调用者。
2、yield
作为中间参数使用。生成器函数返回一个生成器对象,第一次执行next()方法的时候,next()传递的参数不会被使用,因为这一次调用是为了开始执行生成器函数。之后调用next()方法的时候,传递的参数将会被使用。
返回值:yield
关键字实际返回一个IteratorResult
对象,有两个属性:value和done。value对yield
表达式求值的结果,done表示生成器函数是否完成。
迭代器对象中的next()与生成器对象中的next()的区别
- 迭代器对象中的next()方法不接收参数
- 生成器对象中的next()方法可以接收参数
注意事项
[1] 多个生成器对象之间相互不干扰。
[2] yield关键字只能在生成器函数内部使用,在其他地方使用就会抛出异常。【内部嵌套的非生成器函数也不行】
[3] 生成器对象作为可迭代对象:生成器对象当做可迭代对象,使用更加方便。
[4] yield不仅可以作为函数的中间返回语句使用,还可以作为函数中间参数使用。
增强版的yield--->yield *
yield *
能够迭代一个可迭代对象,从而一次产出一个值。
作用
1、迭代一个可迭代对象
2、递归操作
案例:
// 迭代一个可迭代对象
function * a() {
yield * [1,2,3,4]
}
for (let value of a()) {
console.log(value)
}
// 递归操作
function * b(n) {
if (n > 0) {
yield * b(n-1);
yield n-1;
}
}
for (let value of b(3)) {
console.log(value)
}
区别 | yield | yield * |
---|---|---|
定义 | 让生成器停止和开始执行 | 增强yield行为,迭代可迭代对象 |
作用 | 暂停函数的执行 通过next()实现输入输出 |
递归操作 |
提前终止生成器
与迭代器类似,生成器也支持可关闭状态。生成器对象有return()和throw()方法进行终止生成器。
1、return() 强制生成器进行关闭状态,进入关闭方法后,无法恢复。
2、throw()暂停的时候,将一个提供的错误注入到生成器对象中。如果错误未处理,生成器关闭;否则生成器不会关闭。
生成器作为默认迭代器
因为生成器对象实现了Iterable接口,而且生成器函数和默认迭代器被调用之后都产生迭代器,所以生成器很适合作为默认迭代器。
class Foo {
constructor() {
this.values = [1,2,3,4]
}
* [Symbol.iterator]() {
yield * this.values;
}
}
const f = new Foo();
for (const x of f) {
console.log(x)
}
迭代器 VS 生成器
区别 | 迭代器 | 生成器 |
---|---|---|
来源 | 可迭代对象的Symbol.iterator方法的返回值 | 生成器函数的返回值 |
next()方法访问当前的值 | 返回{done: true/false, value: JavaScript类型/undefined} | 返回{done: true/false, value: JavaScript类型/undefined} |
return()终止 | 迭代器没有此函数,但是可以添加。添加后,返回格式与next()相同,但是不会真正终止 | 真正终止生成器 |
throw() | 迭代器没有此函数 | 如果内部捕获,不会终止;否则终止 |