迭代器和生成器

发布时间:2022-06-28 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了迭代器和生成器脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

参考高级程序设计4

迭代器

for循环是一种最简单的迭代,循环是迭代机制的基础,循环可以指定迭代的次数,迭代在一个有序集合上进行,如数组。

数组有已知长度且可以通过索引获取,所以在循环的时候可以通过递增的方式来遍历,因为以下几个原因循环来执行例程并不理想。

  1. 迭代之前需要知道数据结构,取值的时候只能通过[]来获取,这种方式并不适合所有的数据结构
  2. 遍历顺序并不是数据结构固有的,并不适合其他具有隐式顺序的数据结构

es5新增高阶函数forEach就是为了解决这个问题而诞生的,但是它不能识别何时终止迭代

迭代器模式和可迭代协议

  1. 把一些些结构称为“可迭代对象”(iterable),因为它们实现了正式的 Iterable 接口
  2. 任何实现 Iterable(可迭代协议) 接口的数据结构都可以被实现 Iterator 接口的结构
  3. 很多数据都内置了Iterator 接口如字符串、数组、映射、nodeList,arguments,
let str = 'abc'
let arr = ['a', 'b', 'c']
console.log(str[Symbol.iterator])
console.log(arr[Symbol.iterator])
// f values() { [native code] } 
// f values() { [native code] }

迭代器协议 可以通过next()调用迭代器,返回一个 IteratorResult 对象。IteratorResult中有done和value两个属性,done返回布尔值来确定后面是否还有值,done为true时表示状态耗尽,不能再次迭代。value返回对应的当前值

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

console.log(iter.next()); // { done: false, value: 1 } 
console.log(iter.next()); // { done: false, value: 2 } 
console.log(iter.next()); // { done: true, value: undefined }

每个迭代器都表示对可迭代对象的一次性有序遍历。不同迭代器的实例相互之间没有联系,只会独 立地遍历可迭代对象

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

console.log(iter.next()); // { done: false, value: 1 } 
console.log(iter.next()); // { done: false, value: 2 } 
console.log(iter2.next()); // { done: false, value: 1 } 
console.log(iter2.next()); // { done: false, value: 2 } 

自定义迭代器

class Counter { 
    constructor(limit) { 
        this.limit = limit; 
    } 
    [Symbol.iterator]() { 
        let count = 1, 
        limit = this.limit; 
        return { 
            next() { 
                if (count <= limit) { 
                    return { done: false, value: count++ }; 
                } else { 
                    return { done: true, value: undefined }; 
                } 
            },
            return () {
                return { done: true }; 
            }
        }; 
    } 
}
const counter = new Counter(3)
for (const item of counter) {
    if (item > 2) {
        continue
    }
    console.log(item) // 1, 2
}

生成器

  1. 生成器可以在函数中,暂停和恢复代码执行的能力
  2. 在函数名的前面加一个“ * ”代表生成器,在生成器实现的时候一开始是暂停状态,当调用next的时候才会恢复执行
  3. yield之前的代码会正常执行,遇到yield会暂停,函数中的作用域会的状态会被保留,当调用next才会调用yield之后的代码
// return和yield同样的效果,但是yield能写多个, 如果有多个return那么后面的代码都不会执行,只会执行第一个return
function* generatorFn(opt) {  
    yield opt; 
    yield '1'; 
    return '2';  
    return '3'; 
}
// 

const fn = generatorFn('123')
console.log(fn.next(), fn.next(), fn.next(), fn.next())
// {value: '123', done: false} 
// {value: '1', done: false} 
// {value: '2', done: true} 
// {value: undefined, done: true}

console.log(bbb.next()) 
// {value: '123', done: false} 

生成器作为可迭代对象

function* nTimes(n) { 
    while(n--) {  
        yield n; 
    } 
}
for (const i of nTimes(3)) { 
    console.log(i)   // 直接得到value, 返回的不是{ done,value }
}

function* generatorFn (opt) {
    console.log(yield) // 作为函数中间参数使用
    console.log(yield)
    console.log(yield)
}

// 如果 yield 在第一行 后面没有东西返回0,其他的yield后面没有东西返回undefined
const generfn = generatorFn()
generfn.next() // 0
generfn.next() // undefined
generfn.next() // undefined


function* generator (opt) {
    console.log(opt) 
    console.log(yield) 
    console.log(yield)
}

const gener = generator(123)
gener.next(321) // 123, 因为正在调用生成器
gener.next(456) // 456
gener.next(789) // 789

yield 关键字可以同时用于输入和输出

const gener = generator()
console.log(gener.next(), gener.next(456))
// {value: '123', done: false} 
// {value: 456, done: true}

// 函数要对表达式求值才能确定返回的值,当遇到yield时并计算值123,
// 下一次调用的时候传入了456,交给同一个yield处理,
// 然后456被确定为生成器函数产出的值

通过“ * ”加强yield

function* generatorFn2 () {
    yield* [1, 2, 3]

    // 等价于
    // for (const i of [1, 2, 3]) {
    //     yield i
    // }
}
console.log(Array.from(generatorFn2())) // [1, 2, 3]

通过yield*调用生成器

function* innerFn() { 
    yield 'foo'; 
    return 'bar'; 
} 

function* outerFn(genObj) { 
    console.log('iter value:', yield* innerFn()); 
} 

for (const x of outerFn()) { 
    console.log('value:', x); 
}

生成器作为默认迭代器

// 生成器函数 产生一个生成器函数
class Foo { 
    constructor (opt) {
        this.opt = opt
    }
    * [Symbol.iterator] () {
        yield* this.opt
    }
}
for (const key of new Foo([1, 2, 3])) {
    console.log(key)
}

终止生成器 使用throw(), return() 终止生成器

// 所有的生成器都有 return() 会提前进入结束状态, 后续调用都是 done: true
function * Fn () {
    for (const item of [1, 2, 3]) {
        yield item
    }
}

const fn = Fn()
console.log(fn.next()) // {value: 1, done: false}
console.log(fn.return(666)) // {value: 666, done: true} value默认undefined

function* Fn2() { 
    for (const x of [1, 2, 3]) { 
        yield x
    } 
} 
const g = Fn2()

for (const x of g) { 
    if (x > 1) { 
        g.return(4); 
    } 
    console.log(x); // 1, 2
}

// 通过throw 来调用会抛出一个错误,如果处理这个错误会继续往下面执行
function* Fn2() { 
    yield* [1, 2, 3]
}

const fn2 = Fn2()
try {
    fn2.throw('困')
} catch (err) {
    console.log(err) // 困
}

// 如果在生成器内部处理这个错误,那么调用throw时会跳过对应的这一项,如果throw放在所有next前面,也不会执行后面的
function* Fn3() { 
    for (const item of [1, 2, 3]) {
        try {
            yield item
        } catch {}
    }
}

const fn3 = Fn3()
console.log(fn3.next()) // 1
fn3.throw('困')         // yield抛出一个错误,不会在产出2
console.log(fn3.next()) // 3

脚本宝典总结

以上是脚本宝典为你收集整理的迭代器和生成器全部内容,希望文章能够帮你解决迭代器和生成器所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: