ES6 迭代器和生成器

作者 | wangchao
链接 | https://juejin.cn/post/6969842645417852964
一、可迭代对象和迭代器
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()方法,且该方法返回迭代结果的对象listlet 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); // 1console.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 = fromthis.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.toreturn {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+|$/gr.lastIndex = s.match(/[^ ]/).indexreturn {[Symbol.iterator]() { return this },next() {let start = r.lastIndexif (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、例子
// 例1function* oneDigitPrimes() {yield 2;yield 3;yield 5;yield 7;}let primes = oneDigitPrimes()primes.next().value // 2primes.next().value // 3console.log(primes.next().value); // 5console.log(primes.next().done); // falseconsole.log(primes.next().done); // truelet primesIterator = primes[Symbol.iterator]()// 可迭代对象console.log(primesIterator.next()); // {value: undefined, done: true}console.log([...oneDigitPrimes()]); //[2, 3, 5, 7]let sum = 0for (const v of oneDigitPrimes()) {sum += v}console.log(sum); // 17// 例2const 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 = 0while (iterators.length > 0) {if (index >= iterators.length) {index = 0}let item = iterators[index].next()if (item.done) {iterators.splice(index, 1)}else {yield item.valueindex++}}}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 1return "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 }
本文完~
学习更多技能
请点击下方公众号
![]()

评论
