-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
581f4ab
commit 745e4ef
Showing
4 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
type PositiveInteger<T extends number> = `${T}` extends | ||
| "0" | ||
| `-${any}` | ||
| `${any}.${any}` | ||
? never | ||
: T; | ||
function test<T extends number>(n: PositiveInteger<T>) { | ||
/***/ | ||
} | ||
|
||
export default test; |
288 changes: 288 additions & 0 deletions
288
src/views/MyPlayGround/Iterator/iterator_and_generator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
/* eslint-disable @typescript-eslint/no-empty-function */ | ||
// ts-nocheck | ||
|
||
/* | ||
# [迭代器([Symbol.iterator]or `[Symbol.asyncIterator]`) 和 生成器(yield)](https://blog.openreplay.com/deep-dive-into-iterators-iterables-and-generators/) | ||
+ 实现迭代器其实就是根据语言提供的迭代器接口,实现一些符合要求的方法 | ||
+ 集合对象 -> 迭代器 ->遍历 : | ||
+ 迭代器是什么? | ||
+ 迭代器就是一个对象,在集合对象遍历时,运行时会**自动调用**迭代器提供的next方法来获取下一个要迭代的对象,异步迭代器同理 | ||
+ `yield`有什么作用 | ||
+ `yield`语法只有在`Generate`函数`function*`中才有作用,用于暂停这个函数的执行,要想继续执行,需要调用`next`方法 | ||
+ `yield`其实是有返回值的,这个返回值通常是由生成器函数调用的`next`方法里的参数决定的 | ||
## [JS的迭代协议](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols) | ||
+ 迭代器确实是一种协议,而不是内置的实现或者语法,相当于是一种JS给我们提供的约定,按照约定实现指定的方法,就可以实现 对象的可迭代 | ||
### (同步)迭代协议 | ||
+ `迭代协议`分为两部分: `可迭代协议(iterable protocol)`和`迭代器协议(iterator protocol)`,这两个又是环环相扣的 | ||
1. 可迭代协议(iterable protocol)实现 | ||
+ 怎么成为一个可迭代的对象: | ||
要成为可迭代对象,对象必须实现 `@@iterator`方法,对象或者原型链上必须有一个名为 `@@iterator`的属性,以及至少实现一个符合迭代器要求的`next`方法 | ||
可通过**常量`Symbol.iterator`**访问该属性, | ||
+ `[Symbol.iterator]`方法的特征 | ||
1. 这个函数没有参数 | ||
2. 该方法需要返回一个符合迭代器协议(iterable protocol)的对象(比如**返回一个包含`next`方法的匿名对象,或者this(这个this,this上一样可以找到`next`方法)**) | ||
3. 该函数可以是普通函数,也可以是生成器(Generator)函数(生成器函数内部,使用yield提供`entry`即条目) | ||
+ 迭代一个可迭代对象时发生了什么: | ||
+ 当一个对象需要被迭代的时候(比如被置入一个 for...of 循环时),首先,会不带参数调用它的 @@iterator 方法,然后使用此方法返回的迭代器获得要迭代的值。 | ||
2. 迭代器协议(iterator protocol)实现 | ||
迭代器协议定义了产生一系列值的标准方式 | ||
怎么实现 | ||
只有至少实现了一个拥有以下语义(semantic)的 next() 方法,一个对象才能成为迭代器: | ||
next函数需要为 无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的**对象** | ||
IteratorResult接口的对象其实就是一个包含 `done`,`value`属性的对象,这两个属性都是可选的 | ||
+ next() | ||
无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的对象(见下文)。如果在使用迭代器内置的语言特征(例如 for...of)时,得到一个非对象返回值(例如 false 或 undefined),将会抛出 TypeError("iterator.next() returned a non-object value")。 | ||
+ `IteratorResult`接口的对象是什么样的? | ||
所有迭代器协议的方法(next()、return() 和 throw())都应返回实现 IteratorResult 接口的对象。它必须有以下属性: | ||
+ done 可选 | ||
如果迭代器能够生成序列中的下一个值,则返回 false 布尔值。(这等价于没有指定 done 这个属性。) | ||
如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。 | ||
+ value 可选 | ||
迭代器返回的任何 JavaScript 值。done 为 true 时可省略。 | ||
+ 实际上,两者都不是严格要求的;如果返回没有任何属性的对象,则实际上等价于 { done: false, value: undefined }。 | ||
如果一个迭代器返回一个 done: true 的结果,则对任何 next() 的后续调用也返回 done: true,尽管这在语言层面不是强制的。 | ||
** next 方法可以接受一个值,该值将提供给方法体。任何内置的语言特征都将不会传递任何值。传递给生成器 next 方法的值将成为相应 yield 表达式的值。** | ||
+ 可选地,迭代器也实现了 return(value) 和 throw(exception) 方法,这些方法在调用时告诉迭代器,调用者已经完成迭代,并且可以执行任何必要的清理(例如关闭数据库连接)。 | ||
+ return(value) 可选 | ||
无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的对象,其 value 通常等价于传递的 value,并且 done 等于 true。调用这个方法表明迭代器的调用者不打算调用更多的 next(),并且可以进行清理工作。 | ||
+ throw(exception) 可选 | ||
无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的对象,通常 done 等于 true。调用这个方法表明迭代器的调用者监测到错误的状况,并且 exception 通常是一个 Error 实例。 | ||
### 异步迭代协议 | ||
+ 同样分为 `异步可迭代协议(async iterable protocol)`和`异步迭代器协议(async iterator protocol)` | ||
+ 对象怎么实现`异步可迭代协议(async iterable protocol)` | ||
实现`[Symbol.asyncIterator]`方法和符合异步迭代器要求的`next`方法 | ||
+ 如何实现`[Symbol.asyncIterator]`方法: | ||
+ 返回对象的无参数函数,并且符合异步迭代器协议(async iterator protocol)。 | ||
+ 如何符合异步迭代器协议,对象需要至少实现一个符合要求的`next`方法 | ||
+ 当对象实现以下方法时,这个对象就符合了`异步迭代器协议(async iterator protocol)` | ||
+ next() | ||
无参数或者接受一个参数的函数,并返回 promise。promise 兑现为一个对象,该对象符合 IteratorResult 接口,并且这些属性与同步迭代器有着相同的语义。 | ||
+ return(value) 可选 | ||
无参数或者接受一个参数的函数,并返回 promise。promise 兑现为一个对象,该对象符合 IteratorResult 接口,并且这些属性与同步迭代器有着相同的语义。 | ||
+ throw(exception) 可选 | ||
无参数或者接受一个参数的函数,并返回 promise。promise 兑现为一个对象,该对象符合 IteratorResult 接口,并且这些属性与同步迭代器有着相同的语义。 | ||
### 生成器 | ||
+ 生成器函数什么? | ||
+ 生成器是 ECMAScript 6 新增的一个极为灵活的结构,拥有在一个函数块内暂停和恢复代码执行的能力。这种新能力具有深远的影响,比如,使用生成器可以自定义迭代器和实现协程。 | ||
+ 生成器函数怎么创建 | ||
+ 生成器的形式是一个函数,函数名称前面加一个星号(*)表示它是一个生成器。只要是可以定义函数的地方,就可以定义生成器。(除了箭头,其他生成的方法都可以,包括Class里的静态函数) | ||
+ 生成器函数干了什么? | ||
+ 调用生成器函数会产生一个生成器对象。生成器对象**一开始处于暂停执行(suspended)的状态。**与迭代器相似,生成器对象也实现了 Iterator 接口,因此具有 `next()`方法。调用这个方法会让生成器开始或恢复执行。 | ||
+ 生成器对象如何生成,特性是什么 | ||
+ 生成器对象本身就是一个可迭代对象,可通过 `for of`等来遍历,就不用显式调用`next`方法了 | ||
+ 初次调用生成器函数时生成的为生成器对象 | ||
```js | ||
// yield可以暂停Generator`function*`函数,而yield的返回值由next函数参数来决定 | ||
function* test_generator() { | ||
console.log("不调用next不执行"); | ||
const a = yield 2; // yield的返回值由下一次生成器函数调用next函数参数决定的 | ||
console.log(a); | ||
} | ||
const g = test_generator(); // 初次调用并不会执行函数体内的任何一行代码,返回了一个生成器,只有在初次调用next后才会执行代码 | ||
console.log( | ||
g, | ||
"generator", | ||
"调用Generator函数时,相当于生成这个函数签名的Generator实例", | ||
); | ||
console.log( | ||
g[Symbol.iterator](), | ||
"Generator的迭代器是自引用的,调用迭代器方法后返回的还是本身的Generator实例", | ||
); | ||
console.log(g.next()); //调用next方法返回迭代器对象 { value: 2, done: false } | ||
console.log(g.next("a")); // yield的返回值由 next的参数决定 | ||
``` | ||
+ **初次调用并不会执行函数体内的任何一行代码,返回了一个生成器,只有在初次调用next后才会执行代码** | ||
+ 生成器对象也实现了`Iterator`接口,它们默认的迭代器是自引用的,调用这个迭代器方法返回的还是本身(当前的生成器实例) | ||
+ 生成器函数的执行 暂停和恢复 | ||
+ 生成器函数代码遇到`yield`会暂停执行,保留作用域,使用`生成器函数的实例`调用`next`方法,继续执行 | ||
+ 同一个生成器函数生成的两个`Generator`实例,调用`next`不会互相影响 | ||
+ `yield`的关键字**必须**出现在生成器函数的定义中,即便是在 生成器函数中**嵌套定义一个函数**再使用`yield`也会报错 | ||
+ `yield*`语法,可以使用星号增强 yield 的行为,让它能够迭代一个可迭代对象(比如使用yield遍历一个数组时),从而一次产出一个值 | ||
```js | ||
function* generatorFn() { | ||
// for (let i of [1, 2, 3]) { | ||
// yield i; | ||
// } | ||
// 相当于 | ||
yield* [1, 2, 3];// `yield*`用于增强`yield`,迭代可迭代对象 | ||
} | ||
let generatorInstance = generatorFn(); // 生成Generator实例,这个实例本身就是一个可迭代对象 | ||
generatorInstance.next(); | ||
for (let i of generatorInstance) { | ||
// `Generator`实例本身就是一个可迭代对象,因为它实现了`[Symbol.Iterator]`(自引用)和`next`方法 | ||
console.log(i); | ||
// 依次打印 2,3而不是1,2,3 | ||
} | ||
``` | ||
+ `Generator`实例的三个方法 | ||
+ 显示调用`next`方法如`g.next()`或者`g.next('传给yield要返回的参数')`,初始化执行以及恢复生成器函数内部因为`yield`或者`yield*`而暂停的函数 | ||
+ 显示调用 `g.return()`可提前终止迭代 | ||
+ 显示调用 `g.throw()`用于报错,如果生成器函数内部没有处理,g就会提前终止 | ||
### `for...of`遍历是怎么知道什么时候该停止遍历的 | ||
+ **其实 `yield`的作用可以用来简化迭代器的实现,相当于自动实现了 `next`方法** | ||
+ 对于使用 `yield`的生成器函数 | ||
关于for...of循环如何知道何时停止的问题,这是因为生成器实例实现了迭代器协议。当生成器函数中没有更多的yield语句时,生成器实例的next()方法会返回一个对象,其done属性为true,表示迭代已经完成。for...of循环会检查这个done属性,当它为true时,循环就会停止。 | ||
```js | ||
// `yield*`迭代可迭代对象 | ||
function* generatorFn() { | ||
for (let i of [1, 2, 3]) { | ||
yield i; | ||
} | ||
} | ||
let generatorInstance = generatorFn(); // 生成Generator实例,这个实例本身就是一个可迭代对象 | ||
generatorInstance.next(); | ||
for (let i of generatorInstance) { | ||
// `Generator`实例本身就是一个可迭代对象,因为它实现了`[Symbol.Iterator]`(自引用)和`next`方法 | ||
console.log(i); | ||
// 依次打印 2,3而不是1,2,3 | ||
} | ||
``` | ||
+ 手动实现`next`方法,没有yield属性的对象,需要手动在`next`函数的返回中返回一个对象,在合适的条件下使得属性`done`为`true`,一定要返回,不然使用`for of`遍历的时候就死循环了 | ||
```js | ||
// 手动根据条件返回{done:true},使得遍历停止 | ||
let iterable = { | ||
array: [1, 2, 3], | ||
index: 0, | ||
next: function() { | ||
if (this.index < this.array.length) { | ||
return { value: this.array[this.index++], done: false }; | ||
} else { | ||
return { value: undefined, done: true }; | ||
} | ||
}, | ||
[Symbol.iterator]: function() { return this; } | ||
}; | ||
for (let value of iterable) { | ||
console.log(value); // 依次打印 1, 2, 3 | ||
} | ||
``` | ||
```js | ||
// 不使用生成器函数,也不主动返回{done:true},会造成无限遍历的死循环 | ||
let iterable = { | ||
next: function() { | ||
return { value: 1, done: false }; | ||
}, | ||
[Symbol.iterator]: function() { return this; } | ||
}; | ||
// 这个循环会无限循环下去,因为next方法永远返回的对象的done属性为false | ||
for (let value of iterable) { | ||
console.log(value); | ||
} | ||
``` | ||
*/ | ||
|
||
// #ts-ignore | ||
const str = "abc"; | ||
str[Symbol.iterator](); // StringIterator {},`[Symbol.iterator]`方法调用返回一个迭代器, Stringl内置类型内部实现的一个StringIterator迭代器对象 | ||
|
||
const arr1 = [123, 45, 67]; | ||
// 显式调用迭代器对象 | ||
const iter = arr1[Symbol.iterator](); // 因为这个`@@iterator`方法一定会返回一个迭代器 | ||
console.log(iter); //Array Iterator {} | ||
console.log(iter.next()); //{value: 123, done: false} | ||
console.log(iter.next()); //{value: 123, done: false} | ||
console.log(iter.next()); //{value: 123, done: false} | ||
|
||
const myIterator = { | ||
next() { | ||
console.log("is next"); | ||
}, | ||
[Symbol.iterator]: function () { | ||
return this; | ||
}, | ||
}; | ||
|
||
// 箭头函数 不能生成器 | ||
const arrow_generator = () => {}; | ||
|
||
class Foo1 { | ||
// 静态函数 也可以 | ||
static *generator() {} | ||
// 私有函数 可以定义为生成器函数 | ||
*generator() {} | ||
} | ||
|
||
// yield可以暂停Generator`function*`函数,而yield的返回值由next函数参数来决定 | ||
function* test_generator(): any { | ||
console.log("不调用next不执行"); | ||
const a = yield 2; // yield的返回值由下一次生成器函数调用next函数参数决定的 | ||
console.log(a); | ||
} | ||
|
||
const g = test_generator(); // 初次调用并不会执行函数体内的任何一行代码,返回了一个生成器,只有在初次调用next后才会执行代码 | ||
console.log( | ||
g, | ||
"generator", | ||
"调用Generator函数时,相当于生成这个函数签名的Generator实例", | ||
); | ||
console.log( | ||
g[Symbol.iterator](), | ||
"Generator的迭代器是自引用的,调用迭代器方法后返回的还是本身的Generator实例", | ||
); | ||
console.log(g.next()); //调用next方法返回迭代器对象 { value: 2, done: false } | ||
console.log(g.next("a")); // yield的返回值由 next的参数决定 | ||
|
||
/* async await 版本 */ | ||
async function asyncFunction() { | ||
const result = await doSomethingAsync(); | ||
console.log(result); | ||
} | ||
|
||
/* 生成器函数版本 */ | ||
|
||
function doSomethingAsync() { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
resolve("2000"); | ||
}, 2000); | ||
}); | ||
} | ||
|
||
function* generator(): any { | ||
const result = yield doSomethingAsync(); | ||
console.log(result); | ||
} | ||
|
||
const gen = generator(); | ||
gen.next().value.then((result: any) => gen.next(result)); | ||
|
||
function* generatorFn() { | ||
// for (let i of [1, 2, 3]) { | ||
// yield i; | ||
// } | ||
// 相当于 | ||
yield* [1, 2, 3]; // `yield*`用于增强`yield`,迭代可迭代对象 | ||
} | ||
|
||
const generatorInstance = generatorFn(); // 生成Generator实例,这个实例本身就是一个可迭代对象 | ||
generatorInstance.next(); | ||
|
||
for (const i of generatorInstance) { | ||
// `Generator`实例本身就是一个可迭代对象,因为它实现了`[Symbol.Iterator]`(自引用)和`next`方法 | ||
console.log(i); | ||
// 依次打印 2,3而不是1,2,3 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const SyncExternalStore = () => { | ||
return <div>SyncExternalStore</div>; | ||
}; | ||
|
||
export default SyncExternalStore; |