Skip to content

Commit

Permalink
✨ 添加迭代器与生成器
Browse files Browse the repository at this point in the history
  • Loading branch information
Hacker-C committed Jun 2, 2022
1 parent 50f9877 commit 5185112
Show file tree
Hide file tree
Showing 2 changed files with 273 additions and 1 deletion.
3 changes: 2 additions & 1 deletion JS-Advance/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
* [ch01. JS 面向对象](JS-Advance/ch01)
* [ch02. 构造函数和原型](JS-Advance/ch02)
* [ch03. 函数进阶](JS-Advance/ch03)
* [ch04. 正则表达式](JS-Advance/ch04)
* [ch04. 正则表达式](JS-Advance/ch04)
* [ch05. 迭代器与生成器](JS-Advance/ch05)
271 changes: 271 additions & 0 deletions JS-Advance/ch05.md
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

0 comments on commit 5185112

Please sign in to comment.