es6 迭代器和生成器

更新日期: 2021-06-04阅读: 1.4k标签: es6

一、可迭代对象和迭代器

1. 可迭代对象指的是,可通过 for/of 循环的对象,是es6的特性,包括(数组,字符串,set对象,map对象)

2. 扩展操作符...可以展开可迭代对象

let chars = [..."abcd"] // chars === ["a","b","c","d"]
let data = [1, 2, 3, 4]
Math.max(data) // 4

3. 迭代器可用于解构赋值

let purple = Uint8Array.of(255, 0, 255, 128)
let [r, g, b, a] = purple // a===128

4. 迭代map对象,返回值是[key,value]对, 在for/of中可直接解构赋值

let m = new Map([["one", 1], ["two", 2]])
for (let [k, v] of m) console.log(k, v);

5. 值迭代键或值,可使用keys()或values()

[...m]; // [["one", 1], ["two", 2]]
[...m.entries()]; // [["one", 1], ["two", 2]]
[...m.keys()]; // ["one","two"]
[...m.values()]; // [1,2]

6. 可接收Array对象的内置函数和构造函数,可接收任意迭代器

new Set("abc"); new Set(["a", "b", "c"]); // 两者相同

7. 迭代器原理

// 可迭代对象:具有迭代器的方法,且该方法返回迭代器的对象
let list1 = [1, 2]
// 迭代器对象:具有next()方法,且该方法返回迭代结果的对象list
let iterator = list1[Symbol.iterator]() //有一个Symbol.iterator方法,返回自己
// 迭代结果:具有属性value和done的对象
for (let result = iterator.next(); !result.done; result = iterator.next()) {
    console.log(result.value);
}
// 例
let list2 = [1, 2, 3, 4, 5]
let iter = list2[Symbol.iterator]()
console.log(iter.next().value); // 1
console.log([...iter]); // [2, 3, 4, 5]

二、可迭代对象的实现

原则:只要数据类型表示某种可迭代的结构,就应该实现为可迭代对象
实现:为了让类可迭代,必须实现一个Symbol.iterator的方法,该方法必须返回一个迭代器对象,该对象有一个next()方法,next()方法必须返回一个有value和done属性的对象
操作: 可迭代的数值Range类

1.Range对象表示一个数值范围{x: form<=x<=to}

/*
 Range定义了has()方法,
用于测试给定数值是不是该范围的成员 Range是可迭代的,迭代其范围内的所有整数 
*/
class Range {
    constructor(from, to) {
        this.from = from
        this.to = to
    }
    has(x) {
        return typeof x === 'number' &&
            x >= this.from &&
            x <= this.to
    }
    toString() { return `{x: ${this.from}<=x <=${this.to}}` }
    [Symbol.iterator]() {
        let next = Math.ceil(this.from);
        let last = this.to
        return {
            next() {
                return {
                    done: next > last,
                    value: next++,
                }
            }
        }
    }
}


console.log(new Range(1, 3).toString());
console.log(new Range(1, 3).has(4));

for (const i of new Range(1, 3)) {
    console.log(i);
}

console.log([...new Range(-1, 3)]);

2.定义返回可迭代值的函数

// 替代数组的map方法
function map(iterable, fn) {
    let iterator = iterable[Symbol.iterator]()
    // 返回一个可迭代对象,也是迭代器对象
    return {
        [Symbol.iterator]() { return this },
        next() {
            let obj = iterator.next()
            if (obj.done) {
                return obj
            } else {
                return { value: fn(obj.value) }
            }
        }
    }
}
console.log([...map([1, 2, 3], x => x * x)]);
// 替代数组的filter方法
function filter(iterable, fn) {
    let iterator = iterable[Symbol.iterator]()
    // 返回一个可迭代对象,也是迭代器对象
    return {
        [Symbol.iterator]() { return this },
        next() {
            while (true) {
                let obj = iterator.next()
                if (fn(obj.value) || obj.done) {
                    return obj
                }
            }
        }
    }
}
console.log([...filter([1, 2, 3, 4, 5, 6, 12, 56], x => x > 3)]);

3.懒惰迭代

// 假设一个非常长的字符串想要以空格为分隔,如果使用字符串split方法,那么要处理整个字符串,
// 会占用很多内存来保存返回的数组和其中的字符串.
// 使用懒惰迭代,则不必全部保存在内存
function words(s) {
    let r = /\s+|$/g
    r.lastIndex = s.match(/[^ ]/).index
    return {
        [Symbol.iterator]() { return this },
        next() {
            let start = r.lastIndex
            if (start < s.length) { let match = r.exec(s) if (match) { return { value: s.substring(start, match.index) } } }
            return { done: true }
        }
    }
} 
console.log([...words(" abc def ghi ")]);

三、生成器概念和实例

原则:生成器是es6语法定义的迭代器,适合要迭代的值不是某个数据结构的元素,而是计算结果的场景
实现:创建生成器需要先定义一个生成器函数,使用关键字function*,调用生成器函数不会执行函数体,而是返回一个迭代器。
操作: 调用迭代器的next方法,会导致生成器函数的函数体从头开始执行,知道遇见一个yield语句。yield语句的值,会成为返回值。 yield 和yield*只能在生成器函数中使用,常规函数不能出现!

1.例子

// 例1
function* oneDigitPrimes() {
    yield 2;
    yield 3;
    yield 5;
    yield 7;
}
let primes = oneDigitPrimes()
primes.next().value // 2
primes.next().value // 3
console.log(primes.next().value); // 5
console.log(primes.next().done); // false
console.log(primes.next().done); // true

let primesIterator = primes[Symbol.iterator]()// 可迭代对象
console.log(primesIterator.next()); // {value: undefined, done: true}

console.log([...oneDigitPrimes()]); //[2, 3, 5, 7]
let sum = 0
for (const v of oneDigitPrimes()) {
    sum += v
}
console.log(sum); // 17

// 例2
const seq = function* (from, to) {
    for (let i = from; i <= to; i++) yield i
}
console.log([...seq(3, 5)]); // [3, 4,5]

// 例3 类和对象字面量, 不能使用箭头函数定义生成器函数
let o = {
    x: 1, y: 2, z: 3,
    *g() {
        for (const key of Object.keys(this)) {
            yield key
        }
    }
}
console.log([...o.g()]); //["x", "y", "z", "g"]

// 例4 无限回送斐波那契数 如果执行[...fibonacciSequence()]会一直循环直到内存耗尽
function* fibonacciSequence() {
    let x = 0, y = 1;
    while (1) {
        yield y;
        [x, y] = [y, x + y]
    }
}

//通过限制条件, 返回第20个斐波那契数
function fibonacci(n) {
    for (const f of fibonacciSequence()) {
        if (n-- <= 0) {
            return f
        }
    }
}
console.log(fibonacci(10)); //89

// 配合take生成器(用另一个生成器封装一下) 回送指定可迭代对象的前n个元素
function* take(n, iterable) {
    let it = iterable[Symbol.iterator]()
    while (n-- > 0) {
        let next = it.next()
        if (next.done) {
            return
        }
        yield next.value
    }
}
console.log([...take(5, fibonacciSequence())]); //[1, 1, 2, 3, 5]

// 拿到可迭代对象数组,交替回送元素
function* zip(...iterables) {
    // 取得每个可迭代对象的迭代器
    let iterators = iterables.map(i => i[Symbol.iterator]())
    let index = 0
    while (iterators.length > 0) {
        if (index >= iterators.length) {
            index = 0
        }
        let item = iterators[index].next()
        if (item.done) {
            iterators.splice(index, 1)
        }
        else {
            yield item.value
            index++
        }
    }
}
console.log([...zip(oneDigitPrimes(), 'ab', [0])]); //[2, "a", 0, 3, "b", 5, 7]

// yield*与递归生成器
// 顺序回送元素
function* sequence(...iterables) {
    for (const iterable of iterables) {
        for (const item of iterable) {
            yield item;
        }
    }
}
console.log([...sequence("abc", [1, 2, 3])]); // ["a", "b", "c", 1, 2, 3]
// 使用yield* 简化
function* sequence2(...iterables) {
    for (const item of iterables) {
        yield* item
    }
}
console.log([...sequence2('abc', 'def')]); // ["a", "b", "c", "d", "e", "f"]    

2.生成器函数返回值

function* oneAndDone() {
    yield 1
    return "done"
}
// 正常迭代中不会出现返回值
console.log([...oneAndDone()]); // [1]
//在显式调用next可以得到
let generator = oneAndDone()
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 'done', done: true }
console.log(generator.next()); // { value: undefined, done: true }

作者:wangchao
链接:https://juejin.cn/post/6969842645417852964
来源:掘金


链接: https://fly63.com/article/detial/10394

es6 箭头函数的使用总结,带你深入理解js中的箭头函数

箭头函数是ES6中非常重要的性特性。它最显著的作用就是:更简短的函数,并且不绑定this,arguments等属性,它的this永远指向其上下文的 this。它最适合用于非方法函数,并且它们不能用作构造函数。

详解JavaScript模块化开发require.js

js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS和AMD

js解构赋值,关于es6中的解构赋值的用途总结

ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...

ES6中let变量的特点,使用let声明总汇

ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明

ES6的7个实用技巧

ES6的7个实用技巧包括:1交换元素,2 调试,3 单条语句,4 数组拼接,5 制作副本,6 命名参数,7 Async/Await结合数组解构

ES6 Decorator_js中的装饰器函数

ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。

基于ES6的tinyJquery

Query作为曾经Web前端的必备利器,随着MVVM框架的兴起,如今已稍显没落。用ES6写了一个基于class简化版的jQuery,包含基础DOM操作,支持链式操作...

ES6 中的一些技巧,使你的代码更清晰,更简短,更易读!

ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。

Rest/Spread 属性_探索 ES2018 和 ES2019

Rest/Spread 属性:rest操作符在对象解构中的使用。目前,该操作符仅适用于数组解构和参数定义。spread操作符在对象字面量中的使用。目前,这个操作符只能在数组字面量和函数以及方法调用中使用。

使用ES6让你的React代码提升到一个新档次

ES6使您的代码更具表现力和可读性。而且它与React完美配合!现在您已了解更多基础知识:现在是时候将你的ES6技能提升到一个新的水平!嵌套props解构、 传下所有props、props解构、作为参数的函数、列表解构

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!