乐趣区

关于前端:JavaScript高级程序设计笔记07-迭代器与生成器

迭代器与生成器

1. 迭代

重复屡次执行一段程序,(有明确的终止条件)

迭代器、生成器 ES6

  • 计数循环(for):最简略的迭代

    迭代次数、迭代每次执行的操作(程序已知)

  • 古早迭代(有序 -> 数组):①必须数组,需援用数组自身;②递增索引的形式
  • 通用迭代(ES5):forEach

    • 长处:不需援用数组本身;不需索引
    • 毛病:只限数组间接调用,不能手动终止

2. 迭代器模式

内置可迭代类型:

String、Array、Map、Set、arguments 对象、NodeList 等 DOM 汇合类型

  1. (迭代的自我辨认)有默认迭代器属性,键为 Symbol.iterator,值为迭代器工厂函数
  2. (创立 Iterator 对象的能力)迭代器工厂函数返回【实现 Iterator 接口的对象】

    即蕴含键为 next 的属性、可能也蕴含键为 return 的属性

主动调用默认迭代器工厂函数来生成迭代器的操作:

for-of、数组解构、扩大操作符

Array.from(…)、new Set(…)、new Map(…)

Promise.all(…)、Promise.race(…)

yield* …

Iterator 保护一个指向可迭代对象的援用,会阻止垃圾回收。

每个内置可迭代对象的默认迭代器函数所产生的迭代器,也有本人的默认迭代器函数,返回指向本身(并未严格实现 Iterable 接口,默认迭代器函数不能创立新的迭代器)

class Counter {constructor(limit) {this.limit = limit;}

   [Symbol.iterator]() {
      let count = 1,
         o = this;

      return {next() {if( count <= o.limit) {
               return {
                  done: false,
                  value: count++
               };
            } else {
               return {done: true};
            }
         },
         return() {console.log( 'Exiting early');
            return {done: true};
         }
      }
   }
}

let counter1 = new Counter(5);
for (let i of counter1) {if(i > 2) {break;}
   console.log(i);
}
// 1
// 2
// Exiting early
let counter3 = new Counter(5);
let [a, b] = counter3;
console.log(a);
console.log(b);
// Exiting early
// 1
// 2

console.log("//======= 数组的迭代器不能敞开");
let a1 = [1, 2, 3, 4, 5];
let iter = a1[Symbol.iterator]();
for (let i of iter) {console.log( i);
   if(i > 2) {break;}
}
for (let i of iter) {console.log( i);
}
// 1
// 2
// 3
// 4
// 5

3. 生成器 generator

函数加前缀 * 号(箭头函数不行)——> 生成器函数

/*
Function generator() {constructor() {
    this = {next() {// ...}
    }
    return this;
  }
  [Symbol.iterator]() {return this;}
}
*/
let g = generator();
g === g[Symbol.iterator]

应用场景(yield):

  1. 生成器对象作为可迭代对象(有默认 [Symbol.iterator] 为键的属性,无实现创立新迭代器的工厂函数)
  2. yield 实现输入输出

    给 next 办法传参,参数会被当作上一个 yield 操作的后果值

  3. 产生可迭代对象

    yield* 可迭代对象

    yield* 操作的后果值为 Iterable 对象遍历到 done 为 true 时的 value 属性的值

  4. 实现递归 yield*(function* + yield*)

    可将递归后果包装成一个生成器对象(实现 Iterator 接口)

其余:

  • 生成器函数能够作为默认迭代器(工厂函数)

    产生 -> 生成器对象(实现 Iterator 接口)

  • 生成器对象有 API throw()

    生成器函数外部解决,可持续——> 跳过当下的 yield 操作(去到下一个 yield);没解决,就迭代终止。

function *generatorFn() {return 'foo';}
let generatorObject = generatorFn();
console.log(generatorObject);
console.log(generatorObject.next() );
// Object [Generator] {}         generatorFn {<suspended>}
// {value: 'foo', done: true}  {value: "foo", done: true}
function *generatorFn2() {console.log( 'foobar');
}
generatorObject = generatorFn2();
generatorObject.next(); // foobar
generatorObject.next();

function* generatorFn3() {
   yield 'foo';
   yield 'bar';
   return 'baz';
}
let g3 = generatorFn3();
console.log(g3.next() );
console.log(g3.next() );
console.log(g3.next() );
// {value: 'foo', done: false}
// {value: 'bar', done: false}
// {value: 'baz', done: true}
g3 = generatorFn3();
for(const x of g3) {console.log( x);
}
// foo
// bar
g3 = generatorFn2();
for(const x of g3) {console.log( x);
}
// foobar
class Node {constructor(id) {
      this.id = id;
      this.neighbors = new Set();}

   connect(node) {if (node !== this) {this.neighbors.add( node);
         node.neighbors.add(this);
      }
   }
}

class RandomGraph {constructor(size) {this.nodes = new Set();

      for (let i = 0; i< size; ++ i) {this.nodes.add( new Node(i) );
      }

      const threshold = 1 / size;
      for (const x of this.nodes) {for (const y of this.nodes) {if (Math.random() < threshold) {x.connect( y);
            }
         }
      }
   }

   print() {for (const node of this.nodes) {const ids = [...node.neighbors].map(n => n.id).join(',');

         console.log(`${node.id}: ${ids}` );
      }
   }

   // 整张图是否连通
   isConnected() {const visitedNodes = new Set();

      function *traverse(nodes) {for (const node of nodes) {if (!visitedNodes.has(node)) {
               yield node;
               yield* traverse(node.neighbors);
            }
         }
      }
      // 获得汇合中的第一个节点
      const firstNode = this.nodes[Symbol.iterator]().next().value;
      // 应用递归生成器迭代每个节点
      for (const node of traverse([firstNode])) {visitedNodes.add(node);
      }
      // function traverse(nodes) {//     for (const node of nodes) {//        if (!visitedNodes.has(node)) {//           visitedNodes.add(node);
      //           traverse(node.neighbors);
      //        }
      //     }
      // }

      return visitedNodes.size === this.nodes.size;
   }
}

const g = new RandomGraph(6);
g.print();
// 0: 1,2
// 1: 0,5
// 2: 0
// 3: 
// 4: 5
// 5: 1,4
console.log(g.isConnected() );

console.log('实现输入输出 1 ===========')
function *generatorFn(initial) {// console.log( initial);
   console.log(yield 1);
   console.log(yield 2);
   return yield 3;
}
let genObj = generatorFn('foo');
console.log(genObj.next('bar') );
console.log(genObj.next('baz') );
console.log(genObj.next('qux') ); 
console.log(genObj.next() );
// {value: 1, done: false}
// baz
// {value: 2, done: false}
// qux
// {value: 3, done: false}
// {value: undefined, done: true}

console.log('实现输入输出 2 ===========')
function *generatorFn2() {return yield 'foo';}
let genObj2 = generatorFn2();
console.log(genObj2.next() );
console.log(genObj2.next('bar') );
console.log(genObj2.next('test') );
// {value: 'foo', done: false}
// {value: 'bar', done: true} // 相当于给 done 的时候的 value 赋值?// {value: undefined, done: true}
for (const x of generatorFn2()) {console.log( x);
}
// foo

function* zeroes(n) {while(n--) {yield 0;}
}
console.log(Array.from(zeroes(8)) );

function *innerGeneratorFn() {
   yield 'foo';
   return 'bar';
}
function *outerGeneratorFn() {console.log('iter value:', yield* innerGeneratorFn());
}
for(const x of outerGeneratorFn()) {console.log( 'value:', x);
}
// value:  foo
// iter value:  bar
for(const x of innerGeneratorFn()) {console.log( x);
}
// foo

class Foo {constructor() {this.values = [1, 2, 3];
   }
   * [Symbol.iterator]() {yield* this.values;}
   // [Symbol.iterator]() {
   //     let i = 0, len = this.values.length, o = this;
   //     return {//        next() {
   //           return {//              value: o.values[i++],
   //              done: i > len
   //           }
   //        }
   //     }
   // }
}
const f = new Foo();
for(const x of f) {console.log( x);
}
// 1
// 2
// 3
退出移动版