JavaScript之迭代器

简介: JavaScript之迭代器
看红宝书+查资料,重新梳理JavaScript的知识。

迭代就是指可以从一个数据集中按照一定的顺序,不断取出数据的过程。

那么迭代和遍历有啥子区别呢?

  • 迭代强调依次取数据的过程,不保证把所有的数据都取完
  • 遍历强调的是要把所有的数据依次全部取出

在JavaScript中,迭代器是能调用 next方法实现迭代的一个对象,该方法返回一个具有两个属性的对象。

  • value:可迭代对象的下一个值
  • done:表示是否已经取出所有的数据了。 false表示还有数据, true表示后面已经没有数据了。

迭代器简单使用

通过可迭代对象中的迭代器工厂函数 Symbol.iterator来生成迭代器。

const arr = []
console.log(arr)

image-20220415105242128

const arr = [1, 2, 3]

const iter1 = arr[Symbol.iterator]()   // 通过迭代器工厂函数` Symbol.iterator`来生成迭代器。
console.log(iter1)

console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())

console.log('%c%s', 'color:red;font-size:24px;', '================')

const mymap = new Map()
mymap.set('name', 'clz')
mymap.set('age', 21)

const iter2 = mymap[Symbol.iterator]()   // 通过迭代器工厂函数` Symbol.iterator`来生成迭代器。
console.log(iter2)

console.log(iter2.next())
console.log(iter2.next())
console.log(iter2.next())

image-20220415102139605

可以发现,迭代器是取完最后一个值之后,即迭代器下一个值 value undefined时,完成。

但是,上面的说法并不是很准确,并不是迭代器下一个值 value undefined时,就完成的。还需要判断是不是真的没有值,还是是可迭代对象里就有一个值为 undefined。如果是可迭代对象里有一个值为 undefined的情况,那么此时还是不会变成完成状态。

const arr = [1, 2, 3, undefined]

const iter1 = arr[Symbol.iterator]()   // 通过迭代器工厂函数` Symbol.iterator`来生成迭代器。
console.log(iter1)

console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())

image-20220415102828917

不同迭代器之间互不干扰

可以多次调用迭代器工厂函数来生成多个迭代器,每个迭代器都表示对可迭代对象的一次性有序遍历。不同迭代器之间互不干扰,只会独立地遍历可迭代对象

const arr = [1, 2, 3]

const iter1 = arr[Symbol.iterator]()   // 通过迭代器工厂函数` Symbol.iterator`来生成迭代器。
const iter2 = arr[Symbol.iterator]()

console.log('迭代器1:', iter1.next())
console.log('迭代器2:', iter2.next())
console.log('迭代器1:', iter1.next())
console.log('迭代器2:', iter2.next())

image-20220415110326267

迭代器对象可作为可迭代对象

const arr = [1, 2, 3]
const iter = arr[Symbol.iterator]()

for (const i of iter) {
    console.log(i)    // 依次输出1、2、3
}

迭代器"与时俱进"

如果可迭代对象在迭代期间被修改了,迭代器得到的结果也会是修改后的。

const arr = [1, 2, 3]
console.log(arr)

const iter = arr[Symbol.iterator]()
console.log(iter.next())

arr[1] = 999

console.log(iter.next())
console.log(iter.next())

image-20220415110751803

完成但并不完成

当我们迭代到 done: true之后,再调用next是不是会报错,或者不返回任何内容呢?

然而,并不是,迭代器会处于一种完成但并不完成的状态, done: true表示已经完成了,但是后续还能一直调用 next,虽然得到的结果一直都会是 { value: undefined, done: true }。这就是为什么说完成但并不完成

const arr = [1, 2, 3]

const iter = arr[Symbol.iterator]()

console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())

image-20220415120158682

自定义迭代器

从上面的例子中,我们就可以知道是通过通过迭代器工厂函数 Symbol.iterator来生成迭代器,所以我们需要实现一个迭代器迭代器工厂函数,然后迭代器可以调用 next方法,所以还需要实现一个 next方法,至于迭代器工厂函数,实际上直接返回实例 this

计数器例子:

class Counter {
  constructor(limit) {
    this.count = 1
    this.limit = limit
  }
  next() {
    if (this.count <= this.limit) {
      return {
        done: false,
        value: this.count++
      }
    } else {
      return { done: true, value: undefined }
    }
  }
  [Symbol.iterator]() {
    return this
  }
}
const counter = new Counter(3)
const iter = counter[Symbol.iterator]()

console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())

image-20220416105256374

乍一看,没啥问题,但是如果我们使用 for-of来遍历就能发现问题。

const counter = new Counter(3)

for (let i of counter) {
  console.log(i)
}

console.log('另一轮迭代:')
for (let i of counter) {
  console.log(i)
}

image-20220416105416835

使用 for-of循环也变成一次性的了。这是因为 count是该实例的变量,所以两次迭代都是使用的那一个变量,但是该变量第一次循环完之后,就已经超过限制了,所以再次使用 for-of循环就得不到任何的结果了。

可以把 count变量放在闭包里,然后通过闭包返回迭代器,这样子每创建一个迭代器都会对应一个新的计数器。

class Counter {
    constructor(limit) {
        this.limit = limit
    }
    
    [Symbol.iterator]() {

        let count = 1
        const limit = this.limit

        return {
            // 迭代器工厂函数必须要要返回一个带有next方法的对象,因为迭代实际就是通过调用next方法来实现的
            next() {
                if (count <= limit) {
                    return {
                        done: false,
                        value: count++
                    }
                } else {
                    return { done: true, value: undefined }
                }
            }
        }
    }
}

测试

const counter = new Counter(3)

for (let i of counter) {
    console.log(i)
}

console.log('另一轮迭代:')
for (let i of counter) {
    console.log(i)
}

image-20220416110540741

提前终止迭代器

就和使用 for-of循环一样,迭代器会很聪明地去调用 next方法,当迭代器提前终止时,它也会去调用 return方法。

[Symbol.iterator]() {

    let count = 1
    const limit = this.limit

    return {
        // 迭代器工厂函数必须要要返回一个带有next方法的对象,因为迭代实际就是通过调用next方法来实现的
        next() {
            if (count <= limit) {
                return {
                    done: false,
                    value: count++
                }
            } else {
                return { done: true, value: undefined }
            }

        },

        return() {
            console.log('提前终止迭代器')
            return { done: true }
        }
    }
}

测试

const counter = new Counter(5)

for (let i of counter) {
    if (i === 3) {
        break;
    }
    console.log(i)
}

image-20220416111320222

如果迭代器没有关闭,就可以继续从上次离开的地方继续迭代。数组地迭代器就是不能关闭的。

const arr = [1, 2, 3, 4, 5]
const iter = arr[Symbol.iterator]()

iter.return = function () {
    console.log('提前退出迭代器')

    return {
        done: true
    }
}

for (const i of iter) {
    console.log(i)
    if (i === 2) {
        break
    }
}

for (const i of iter) {
    console.log(i)
}

image-20220416112206220

目录
相关文章
|
3月前
|
前端开发 JavaScript 中间件
掌握JavaScript中的迭代器和生成器(下)
掌握JavaScript中的迭代器和生成器(下)
|
3月前
|
存储 JavaScript 前端开发
掌握JavaScript中的迭代器和生成器(上)
掌握JavaScript中的迭代器和生成器
|
6月前
|
JavaScript 前端开发
带你读《现代Javascript高级教程》十五、Iterator 迭代器:简化集合遍历的利器(1)
带你读《现代Javascript高级教程》十五、Iterator 迭代器:简化集合遍历的利器(1)
|
6月前
|
JavaScript 前端开发
带你读《现代Javascript高级教程》十五、Iterator 迭代器:简化集合遍历的利器(2)
带你读《现代Javascript高级教程》十五、Iterator 迭代器:简化集合遍历的利器(2)
|
9月前
|
JavaScript 前端开发
《现代Javascript高级教程》Iterator迭代器:简化集合遍历的利器
Iterator 迭代器:简化集合遍历的利器 引言 在 JavaScript 中,迭代器(Iterator)是一种用于遍历集合的接口。迭代器提供了一种统一的方式来访问集合中的元素,无论集合的类型和内部结构如何。通过使用迭代器,我们可以轻松地遍历数组、对象、Map、Set 等各种数据结构,并进行相应的操作。本文将详细介绍迭代器的概念、属性、应用场景,并提供相关的代码示例。
70 1
|
9月前
|
人工智能 前端开发 JavaScript
JavaScript自定义迭代器和提前终止迭代器
与 Iterable 接口类似,任何实现 Iterator 接口的对象都可以作为迭代器使用。下面这个例子中 的 Counter 类只能被迭代一定的次数
69 0
|
12月前
|
设计模式 JavaScript 前端开发
浅习一波JavaScript高级程序设计(第4版)p7-迭代器
JavaScript 高级程序设计第 4 版(后简称高程4),相较于第 3 版,增加了 ES6 至 ES10 的全新内容,删除了旧版过时的内容,并在原有基础上充实了更加翔实的内容。 中文译版于 2020 年发售,妥妥的“新鲜出炉”,你要是问本瓜:当今学 JavaScript 哪家强,我只能说:红宝书第 4 版最在行。
|
算法 JavaScript 前端开发
JavaScript专项算法题(7):Iterators迭代器
Iterators迭代器 挑战1 问题: A) 创建一个for循环,用于遍历数组,返回数组的所有元素的总和。 B) 创建一个函数式迭代器,调用时遍历传入的数组的每一个元素,每次一个元素。 题解: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // CHALLENGE 1 function sumFunc(arr) { // YOUR CODE HERE let sum = 0 for(let i = 0; i < arr.length; i
|
存储 前端开发 JavaScript
JavaScript之迭代器and生成器
JavaScript之迭代器and生成器
JavaScript之迭代器and生成器
|
存储 前端开发 JavaScript
重学JavaScript【迭代器和生成器】
重学JavaScript 篇的目的是回顾基础,方便学习框架和源码的时候可以快速定位知识点,查漏补缺,所有文章都同步在 公众号(道道里的前端栈) 和 github 上。
222 0