forked from Hacker-C/notes
-
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
Showing
2 changed files
with
273 additions
and
1 deletion.
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,271 @@ | ||
# (五)迭代器与生成器 | ||
|
||
## 1. 迭代器 | ||
|
||
### 1.1 什么是迭代器 | ||
|
||
- 宏观上来讲,迭代器的定义我们可以看出来,迭代器是 **帮助我们对某个数据结构进行遍历的对象**,无需关心内部实现,只需要能使用其接口和特性即可。 | ||
- 微观上来讲(JS 角度),**迭代器是一个具体的对象**,这个对象需要符合 **迭代器协议(iterator protocol)**。 | ||
|
||
### 1.2 迭代器协议 | ||
|
||
- 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式 | ||
- 在 JS 中这个标准就是一个特定的 `next` 方法; | ||
|
||
### 1.3 next 函数(方法) | ||
|
||
`next` 是一个无参或者一个参数的函数,返回一个具有两个属性(`done` 和 `value`)的对象。 | ||
|
||
```js | ||
next: function() { | ||
return { | ||
done: boolean, | ||
value: any | ||
} | ||
} | ||
``` | ||
|
||
返回的对象属性: | ||
- `done`:`true` 表示迭代结束,`flase` 未结束。 | ||
- `value`: 本次迭代的值。 | ||
|
||
### 1.4 举例:封装一个迭代器函数 | ||
|
||
```js | ||
const colors = ['red', 'pink', 'green'] | ||
const nums = [100, 200, 300] | ||
|
||
const iteratorFunc = arr => { | ||
let index = 0 | ||
return { | ||
next() { | ||
if (index < arr.length) { | ||
return { done: false, value: arr[index++] } | ||
} | ||
return { done: true, value: undefined } | ||
} | ||
} | ||
} | ||
|
||
const colorsIterator = iteratorFunc(colors) | ||
const numsIterator = iteratorFunc(nums) | ||
|
||
console.log(colorsIterator.next()) | ||
console.log(colorsIterator.next()) | ||
console.log(colorsIterator.next()) | ||
console.log(colorsIterator.next()) | ||
|
||
console.log(numsIterator.next()) | ||
console.log(numsIterator.next()) | ||
console.log(numsIterator.next()) | ||
console.log(numsIterator.next()) | ||
``` | ||
|
||
### 1.5 可迭代对象 | ||
|
||
- 当一个对象实现了 **iterable protocol 协议** 时,它就是一个可迭代对象。 | ||
- 这个对象的要求是必须实现 `@@iterator` 方法,在代码中我们使用 `Symbol.iterator` 访问该属性 | ||
|
||
#### 如何定义一个可迭代对象 | ||
- 给该对象添加 `[Symbol.iterator](){}` 函数(方法)。 | ||
|
||
#### 可迭代对象有什么用 | ||
- 可迭代对象为可迭代特性服务,也就是说,一个对象实现了可迭代性,那么他就具有一些列可迭代对象所具有的一切特性。 | ||
|
||
- 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)`; | ||
|
||
|
||
#### 一个对象具有可迭代特性的条件 | ||
|
||
- 底层实现了可迭代方法,JS 或者就是 `[Symbol.iterator](){}`,例如数组、字符串、类数组、`Map`、`Set`、`NodeList` 等。检测是否是可迭代对象: | ||
```js | ||
Array.prototype[Symbol.iterator]; | ||
String.prototype[Symbol.iterator]; | ||
Map.prototype[Symbol.iterator]; | ||
Set.prototype[Symbol.iterator]; | ||
``` | ||
|
||
> [!TIP] 一般对象没有实现 `[Symbol.iterator]()`,但 ES9 底层做了处理,**让对象也支持解构赋值和扩展运算符**。 | ||
#### 创建一个可迭代对象 | ||
|
||
```js | ||
const iterableObj = { | ||
colors: ['red', 'green', 'orange'], | ||
[Symbol.iterator]() { | ||
let index = 0 | ||
return { | ||
next: () => { | ||
if (index < this.colors.length) { | ||
return { done: false, value: this.colors[index++] } | ||
} | ||
return { done: true, value: undefined } | ||
} | ||
} | ||
} | ||
} | ||
|
||
// 1、let ... of ... | ||
for (let color of iterableObj) { | ||
console.log(color) | ||
} | ||
// 2、spread | ||
console.log([...iterableObj]) | ||
// 3、distructing assignment | ||
const [red] = iterableObj | ||
console.log(red) | ||
// 4、new Set | ||
console.log(new Set(iterableObj)) | ||
// 5、Array.from | ||
console.log(Array.from(iterableObj)) | ||
// 6、Promise.all() | ||
Promise.all(iterableObj).then(res => { | ||
console.log(res) | ||
}) | ||
``` | ||
|
||
## 2. 生成器 | ||
|
||
### 2.1 什么是生成器 | ||
|
||
- 生成器是一个函数,能让我们更加方便的控制函数的暂停、继续执行。 | ||
- 生成器是一种特殊的迭代器,一般可以替代迭代器,简化代码 | ||
- 生成器的返回值还是一个生成器(Generator) | ||
|
||
### 2.2 生成器替代迭代器的三种方法 | ||
|
||
```js | ||
// 生成器是一种特殊的迭代器,所以可以代替迭代器 | ||
function* createIterableArray(arr) { | ||
// 1、写法1 | ||
// for (const e of arr) { | ||
// yield e | ||
// } | ||
// 2、写法2 | ||
// yield arr[0] | ||
// yield arr[1] | ||
// yield arr[2] | ||
// 3、写法3 | ||
yield* arr | ||
} | ||
|
||
const names = ['peter', 'jack', 'julia'] | ||
const namesIterator = createIterableArray(names) | ||
|
||
for (let item of namesIterator) { | ||
console.log(item) | ||
} | ||
``` | ||
|
||
### 2.3 async/await 与 生成器 | ||
|
||
在 `async/await` 出现之前,回调地狱问题的解决是靠生成器解决的。 `async/await` 的原理就是生成器。 | ||
|
||
模拟请求: | ||
```js | ||
// request.js | ||
function requestData(url) { | ||
// 异步请求的代码会被放入到executor中 | ||
return new Promise((resolve, reject) => { | ||
// 模拟网络请求 | ||
setTimeout(() => { | ||
// 拿到请求的结果 | ||
resolve(url) | ||
}, 2000); | ||
}) | ||
} | ||
``` | ||
|
||
需求: | ||
- url: why -> res: why | ||
- url: res + "aaa" -> res: whyaaa | ||
- url: res + "bbb" => res: whyaaabbb | ||
|
||
1. 第一种方案: 多次回调 | ||
|
||
```js | ||
// 回调地狱 | ||
requestData("why").then(res => { | ||
requestData(res + "aaa").then(res => { | ||
requestData(res + "bbb").then(res => { | ||
console.log(res) | ||
}) | ||
}) | ||
}) | ||
``` | ||
|
||
2. 第二种方案: Promise中then的返回值来解决 | ||
|
||
```js | ||
requestData("why").then(res => { | ||
return requestData(res + "aaa") | ||
}).then(res => { | ||
return requestData(res + "bbb") | ||
}).then(res => { | ||
console.log(res) | ||
}) | ||
``` | ||
|
||
3. 第三种方案: Promise + generator实现 | ||
|
||
```js | ||
function* getData() { | ||
const res1 = yield requestData("why") | ||
const res2 = yield requestData(res1 + "aaa") | ||
const res3 = yield requestData(res2 + "bbb") | ||
const res4 = yield requestData(res3 + "ccc") | ||
console.log(res4) | ||
} | ||
|
||
// 1> 手动执行生成器函数 | ||
// const generator = getData() | ||
// generator.next().value.then(res => { | ||
// generator.next(res).value.then(res => { | ||
// generator.next(res).value.then(res => { | ||
// generator.next(res) | ||
// }) | ||
// }) | ||
// }) | ||
|
||
// 2> 自己封装了一个自动执行的函数 | ||
function execGenerator(genFn) { | ||
const generator = genFn() | ||
function exec(res) { | ||
const result = generator.next(res) | ||
if (result.done) { | ||
return result.value | ||
} | ||
result.value.then(res => { | ||
exec(res) | ||
}) | ||
} | ||
exec() | ||
} | ||
|
||
execGenerator(getData) | ||
``` | ||
|
||
4. 第四种方案: async/await | ||
|
||
```js | ||
async function getData() { | ||
const res1 = await requestData("why") | ||
const res2 = await requestData(res1 + "aaa") | ||
const res3 = await requestData(res2 + "bbb") | ||
const res4 = await requestData(res3 + "ccc") | ||
console.log(res4) | ||
} | ||
getData() | ||
``` | ||
|
||
## 3. 迭代器与生成器问题 | ||
|
||
实现以下效果: | ||
```bash | ||
>>> [...5] | ||
[1, 2, 3, 4, 5] | ||
``` | ||
|
||
答案解析:http:https://idk-js.mphy.top/JS/JavaScript.html#_11-10 |