【07】迭代器模式

01-介绍

用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

for 循环不是迭代器模式

简单的 for 循环并不是迭代器模式,因为 for 循环需要知道对象的内部结构。

如下面的例子

  • 要知道数组的长度
  • 要知道通过 arr[i] 形式来得到 item
const arr = [10, 20, 30]
const length = arr.length
for (let i = 0; i < length; i++) {
    console.log(arr[i])
}

简易迭代器

有些对象,并不知道他的内部结构

  • 不知道长度
  • 不知道如何获取 item
const pList = document.querySelectorAll('p')
pList.forEach(p => console.log(p))

forEach 就是最建议的迭代器

02-演示

注意,这个示例也许你会感觉繁琐,不理解。但慢慢的把这一章看完,你就能明白她的意义。

在这里插入图片描述

class DataIterator {
    private data: number[]
    private index = 0

    constructor(container: DataContainer) {
        this.data = container.data
    }

    next(): number | null {
        if (this.hasNext()) {
            return this.data[this.index++]
        }
        return null
    }

    hasNext() {
        if (this.index >= this.data.length) return false
        return true
    }
}

class DataContainer {
    data: number[] = [10, 20, 30, 40]
    getIterator() {
        return new DataIterator(this)
    }
}

const container = new DataContainer()
const iterator = container.getIterator()
while(iterator.hasNext()) {
    const num = iterator.next()
    console.log(num)
}

是否符合设计原则?

5 大设计原则中,最重要的就是:开放封闭原则,对扩展开放,对修改封闭

  • 使用者和目标数据分离,解耦
  • 目标数据自行控制内部迭代逻辑
  • 使用者不关心目标数据的内部结构

03-场景

JS 有序对象,都内置迭代器

  • 字符串
  • 数组
  • NodeList 等 DOM 集合
  • Map
  • Set
  • arguments

【注意】对象 object 不是有序结构

Symbol.iterator

每个有序对象,都内置了 Symbol.iterator 属性,属性值是一个函数。
执行该函数讲返回 iterator 迭代器,有 next() 方法,执行返回 { value, done } 结构。

// 拿数组举例,其他类型也一样

const arr = [10, 20, 30]
const iterator = arr[Symbol.iterator]()

iterator.next() // {value: 10, done: false}
iterator.next() // {value: 20, done: false}
iterator.next() // {value: 30, done: false}
iterator.next() // {value: undefined, done: true}

另外,有些对象的 API 也会生成有序对象

const map = new Map([ ['k1', 'v1'], ['k2', 'v2'] ])
const mapIterator = map[Symbol.iterator]()

const values = map.values() // 并不是 Array
const valuesIterator = values[Symbol.iterator]()

// 还有 keys entries

自定义迭代器

interface IteratorRes {
    value: number | undefined
    done: boolean
}

class CustomIterator {
    private length = 3
    private index = 0

    next(): IteratorRes {
        this.index++
        if (this.index <= this.length) {
            return { value: this.index, done: false }
        }
        return { value: undefined, done: true }
    }

    [Symbol.iterator]() {
        return this
    }
}

const iterator = new CustomIterator()
console.log( iterator.next() )
console.log( iterator.next() )
console.log( iterator.next() )
console.log( iterator.next() )

有序结构的作用

for…of

所有有序结构,都支持 for…of 语法

数组操作

数组解构

const [node1, node2] = someDomList

扩展操作符

const arr = [...someDomList]

Array.from()

const arr = Array.form(someDomList)
创建 Map 和 Set
const map = new Map([
    ['k1', 'v1'],
    ['k2', 'v2']
])

const set = new Set(someDomList)
Promise.all 和 Promise.race
Promise.all([promise1, promise2, promise3])
Promise.race([promise1, promise2, promise3])
yield* 操作符

下一节讲

总结

  • 有序结构
  • Symbol.iterator 生成迭代器
  • 迭代器的应用

04-Generator

迭代器和生成器,两者密不可分

基本使用

function* genNums() {
    yield 10
    yield 20
    yield 30
}

const numsIterator = genNums()
numsIterator.next() // {value: 10, done: false}
numsIterator.next() // {value: 20, done: false}
numsIterator.next() // {value: 30, done: false}
numsIterator.next() // {value: undefined, done: true}

// for (let n of numsIterator) {
//     console.log(n)
// }

yield* 语法

上一节说过,有序结构可用于 yield*

function* genNums() {
    yield* [100, 200, 300] // 相当于:循环数组,分别 yield
}
const numsIterator = genNums()
numsIterator.next() // {value: 100, done: false}
numsIterator.next() // {value: 200, done: false}
numsIterator.next() // {value: 300, done: false}
numsIterator.next() // {value: undefined, done: true}

// for (let n of numsIterator) {
//     console.log(n)
// }

最简单的自定义迭代器

class CustomIterator {
    private data: number[]

    constructor() {
        this.data = [10, 20, 30]
    }

    * [Symbol.iterator]() {
        yield* this.data
    }
}

const iterator = new CustomIterator()
for (let n of iterator) {
    console.log(n)
}

yield 遍历 DOM 树

有助于深入理解 Generator

function* traverse(elemList: Element[]): any {
    for (const elem of elemList) {
        yield elem

        const children = Array.from(elem.children)
        if (children.length) {
            yield* traverse(children)
        }
    }
}

const container = document.getElementById('container')
if (container) {
    for (let node of traverse([container])) {
        console.log(node)
    }
}

总结

  • 基本使用
  • yield* 语法
  • 遍历 DOM 树

05-总结

内容回顾

  • 概念介绍 + 解决的问题
  • UML 类图和代码演示
  • 场景 Symbol.iterator 和 Generator

重要细节

  • for…of 和迭代器的关系
  • Generator 和迭代器的关系
  • 对象 object 不是有序结构

注意事项

  • 模拟面试:请使用 Generator 遍历一个 DOM 树
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值