JS 迭代器 和 for...of 循环


一、迭代器专用循环 for…of


for…of 用来遍历实现了迭代器相关接口的数据结构或对象。

1. 使用 for … of 迭代数组对象:

<script>
  let array = [1, 2, 3, 4, 5]
  for (let item of array) {
    console.log(item)
  }
  // 输出:
  // 1
  // 2
  // 3
  // 4
  // 5
</script>

2. 使用 for … of 迭代字符串对象:

<script>
  let str = '遍历字符串'
  for (let char of str) {
    console.log(char)
  }
  // 输出:
  // 遍
  // 历
  // 字
  // 符
  // 串
</script>

3. 使用 for … of 迭代 Set 对象:

<script>
  let set = new Set([1, 2, 3, 4, 5])
  for (let item of set) {
    console.log(item)
  }
  // 输出:
  // 1
  // 2
  // 3
  // 4
  // 5
</script>

4. 使用 for … of 迭代 Map 对象:

<script>
  let maps = new Map([['1', 2], ['3', 4]])
  for (let [key, val] of maps) {
    console.log(`${key}:${val}`)
  }
  // 输出:
  // 1:2
  // 3:4
</script>

5. 使用 for … of 迭代自定义对象: ( 遍历失败 )

<script>
  let obj = {number: [1, 2, 3, 4, 5]}
  for (let item of obj) {
    console.log(item)
  }
  // 输出:
  // Uncaught TypeError: obj is not iterable
</script>

二、迭代器接口


上面几个例子中,数组、字符串、Map、Set 之所以能够使用 for…of 遍历,是因为它们内部都定义了一个
函数 Symbol.iterator,而自定义对象使用 for…of 时会出现错误,正是因为它没有定义 Symbol.iterator 函数。


证明上述观点:

<script>
  // 数组
  let array = [1, 2, 3, 4, 5]
  console.dir(array[Symbol.iterator]) // 输出:函数对象

  // 字符串
  let str = '遍历字符串'
  console.dir(str[Symbol.iterator]) // 输出:函数对象

  // set
  let set = new Set([1, 2, 3, 4, 5])
  console.dir(set[Symbol.iterator]) // 输出:函数对象

  // map
  let maps = new Map([['1', 2], ['3', 4]])
  console.dir(maps[Symbol.iterator]) // 输出:函数对象

  // 自定义对象
  let obj = {number: [1, 2, 3, 4, 5]}
  console.dir(obj[Symbol.iterator]) // 输出:undefined
</script>

其实想正确的被 for…of 遍历,只定义 Symbol.iterator 函数是不够的, 还需要两个重要接口的实现 Iterator 和 IteratorResult。
※ 接口是抽象的,其目的是定义标准,具体逻辑由对象自行实现。


1. Iterator 接口:

方法返回值描述
next实现 IteratorResult 接口的对象 ( 每轮迭代的结果 )具体的迭代逻辑

2. IteratorResult 接口:

属性取值描述
donetrue / false是否可以继续迭代
value任意值返回给 for…of 中的数据

3. for…of 的正确运行需要3个接口搭配完成 Symbol.iterator、Iterator、
IteratorResult 三个缺一不可,通过这三个接口的描述,我们可以推断 for…of 的大致执行流程如下:

请添加图片描述
1. 执行 Symbol.iterator 函数,并得到返回的 Iterator 接口对象。
2. 执行 Iterator 接口对象的 next 方法,并得到返回的 IteratorResult 接口对象。
3. 判断 IteratorResult 接口对象的 done 属性,done 为 false 时, 将 value 属性赋值给 for…of 定义的
变量, 并执行 for…of 方法体,然后重复步骤 2 和 3,当 done 属性为 true 时退出 for…of 循环。


三、为对象自定义迭代器


有了上面对 for…of 运行机制的理解,那么就可以遵照这个规则,为对象自定义迭代器。

假设需求:
现有 person 对象,其有 name(姓名) 和 technology(技术) 两个属性,因为一个人会有多个技能,所以
technology是个数组类型,现在要求,让该对象可以被 for…of 遍历,并且遍历时把所有的 technology 罗列出来。

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>iterator</title>
</head>
<body>
<script>
  // 定义对象
  let person = {name: 'ares5k', technology: ['h5c3', 'vue', 'java', 'mysql', 'python']}

  // 为对象自定义迭代器
  person[Symbol.iterator] = () => {
    let index = 0
    let length = person.technology.length
    return { // 此处返回的是 Iterator 接口的实现
      next() {
        return index < length ? { // 此处返回的是 IteratorResult 接口的实现
          // 此处是先用 index 取值, 然后再对其进行自增( i++, ++i 相关理论)
          value: person.technology[index++],
          done: false
        } : {
          done: true
        }
      }
    }
  }

  // 测试
  for (let item of person) {
    console.log(item)
  }
  // 输出:
  // h5c3
  // vue
  // java
  // mysql
  // python
</script>
</body>
</html>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值