Skip to content

Latest commit

 

History

History
271 lines (219 loc) · 6.78 KB

ch05.md

File metadata and controls

271 lines (219 loc) · 6.78 KB

(五)迭代器与生成器

1. 迭代器

1.1 什么是迭代器

  • 宏观上来讲,迭代器的定义我们可以看出来,迭代器是 帮助我们对某个数据结构进行遍历的对象,无需关心内部实现,只需要能使用其接口和特性即可。
  • 微观上来讲(JS 角度),迭代器是一个具体的对象,这个对象需要符合 迭代器协议(iterator protocol)

1.2 迭代器协议

  • 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式
  • 在 JS 中这个标准就是一个特定的 next 方法;

1.3 next 函数(方法)

next 是一个无参或者一个参数的函数,返回一个具有两个属性(donevalue)的对象。

next: function() {
  return {
    done: boolean,
    value: any
  }
}

返回的对象属性:

  • donetrue 表示迭代结束,flase 未结束。
  • value: 本次迭代的值。

1.4 举例:封装一个迭代器函数

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](){},例如数组、字符串、类数组、MapSetNodeList 等。检测是否是可迭代对象:
Array.prototype[Symbol.iterator];
String.prototype[Symbol.iterator];
Map.prototype[Symbol.iterator];
Set.prototype[Symbol.iterator];

[!TIP] 一般对象没有实现 [Symbol.iterator](),但 ES9 底层做了处理,让对象也支持解构赋值和扩展运算符

创建一个可迭代对象

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 生成器替代迭代器的三种方法

// 生成器是一种特殊的迭代器,所以可以代替迭代器
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 的原理就是生成器。

模拟请求:

// 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. 第一种方案: 多次回调
// 回调地狱
requestData("why").then(res => {
  requestData(res + "aaa").then(res => {
    requestData(res + "bbb").then(res => {
      console.log(res)
    })
  })
})
  1. 第二种方案: Promise中then的返回值来解决
requestData("why").then(res => {
  return requestData(res + "aaa")
}).then(res => {
  return requestData(res + "bbb")
}).then(res => {
  console.log(res)
})
  1. 第三种方案: Promise + generator实现
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)
  1. 第四种方案: async/await
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. 迭代器与生成器问题

实现以下效果:

>>> [...5]
[1, 2, 3, 4, 5]

答案解析:http:https://idk-js.mphy.top/JS/JavaScript.html#_11-10