ES6 Iterator与Generator

一 Iterator

  遍历器(Iterator),它是一种接口,为各种不同的数据结构提供统一的访问机制。。即for...of循环,当使用for...of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)

  1 作用: 

  • 1)是为各种数据结构,提供一个统一的、简便的访问接口  
  • 2)使得数据结构的成员能够按某种次序排列 
  • 3)Iterator接口主要供for...of消费

  2 模拟next方法返回值的例子

 1     //一个遍历器生成函数,作用就是返回一个遍历器对象
 2     function makeIterator(arr){
 3         var index = 0;
 4         return {
 5             next: function() {
 6                if(index<arr.length){
 7                    return {value:arr[index++],done:false}
 8                }else{
 9                    return {value: undefined,done:true}
10                }
11             }
12         }
13     }
14     var it = makeIterator(['a', 'b']);
15     //指针对象的next方法,用来移动指针。
16     //开始时,指针指向数组的开始位置。
17     //然后,每次调用next方法,指针就会指向数组的下一个成员。
18     //第一次调用,指向a;第二次调用,指向b。
19     console.log(it.next())//Iterator.html:21 {value: "a", done: false}
20     console.log(it.next())//{value: "b", done: false}
21     console.log(it.next())//{value: undefined, done: true}

  3 工作原理:

  1. 创建一个指针对象,指向数据结构的起始位置。
  2. 第一次调用next方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
  4. 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
    • value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
    • 当遍历结束的时候返回的value值是undefined,done值为true

  4 调用Iterator接口的场合

  • (1)解构赋值,对数组和Set结构进行解构赋值时,会默认调用Symbol.iterator方法。
    • let [first, ...rest] = set;
  • (2)扩展运算符
    • [...str]
  • (3)yield* yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
  • (4)for...of Array.from() Map() Set()
 1  //当使用扩展运算符(...)或者对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法
 2     // 原生具备iterator接口的数据(可用for of遍历)
 3     //数组 字符串  Set
 4     let arr1 = [1, 2, 3, 4];
 5     let arr2 = "kaixin";
 6     let set = new Set(["q","w","e","r","t","y"])
 7     for(let i of arr1){
 8         console.log(i); // 1 2 3  4
 9     }
10     console.log(...arr1) // 1 2 3 4
11     console.log(...arr2) // k a i x i n
12     console.log(...set) //q w e r t y
13     //对象不行 因为本身没有迭代器 会报错
14     var obj = { a: 2, b: 3 }
15     for(let i of obj){
16         console.log(i); // Iterator.html:38 Uncaught TypeError: obj is not iterable
17     }
18     //让对象支持for…of的办法就是手动给对象添加迭代器
19     obj[Symbol.iterator] = function(){
20         const _this = this
21         const keys = Object.keys(this)
22         let index = 0
23         return {
24             next(){
25                 return {
26                     value: _this[keys[index++]],
27                     done: index>keys.length
28                 }
29             }
30         }
31     }
32     for(let i of obj){
33         console.log(i); // 2,3
34     }

二 Generator 

  • Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
  • 两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态
  •  yield可暂停,next方法可启动。每次返回的是yield后的表达式结果

  1 特点

  1. 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的遍历器对象
  2. 每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。
  3. Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。

  2 写法

  yield语句:只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。在一个普通函数中使用yield语句,会产生一个句法错误

 1     //function关键字与函数名之间有一个星号;
 2     // 函数体内部使用yield表达式,定义不同的内部状态
 3     function* helloWorldGenerator(){
 4         console.log("开始执行")
 5         yield 'hello';  
 6         yield 'generator';
 7         yield '!!';
 8     }
 9     let gen = helloWorldGenerator();
10     console.log(gen.next());    //generator.html:12 {value: "hello", done: false}
11     console.log(gen.next());    //generator.html:13 {value: "generator", done: false}
12     console.log(gen.next());   //generator.html:14 {value: "!!", done: false}
13     console.log(gen.next()); //generator.html:15 {value: undefined, done: true}

  (1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

  (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。

  (3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

  (4)如果该函数没有return语句,则返回的对象的value属性值为undefined

  3 next也可以传递参数

  next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

 1     //next传递参数  
 2     // next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
 3     function* generatorExample () {
 4         console.log('开始执行')
 5         let result = yield 'hello'
 6         console.log(result)
 7         yield 'generator'
 8     }
 9     let gen1 = generatorExample();
10     console.log(gen1.next());   
11     console.log(gen1.next(123));  
12     //开始执行   
13     //generator.html:24 {value: "hello", done: false} 
14     //123   
15     //generator.html:25 {value: "generator", done: false}
16     //next把123 传进去,这时候打印的result不为hello,而是123

  4 与 Iterator 接口的关系

  由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

 1     //与 Iterator 接口的关系
 2     // 对象没有iterator接口,用for...of遍历时便会报错。
 3     let obj = { username: 'kobe', age: 39 }
 4     for (let i of obj) {
 5         console.log(i) //  Uncaught TypeError: obj is not iterable
 6     }
 7     //由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。
 8     let obj = { username: '王', age:  123}
 9     obj[Symbol.iterator] = function* myTest() {
10         yield '王';
11         yield 123;
12         yield 3;
13     };
14     for (let i of obj) {
15         console.log(i) // 王 // 123  //3
16     }

  5 yield* 表达式和取出嵌套数组

  如果yield命令后面跟的是一个遍历器对象,需要在yield命令后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*语句

 1     //yield* 表达式
 2     //如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的。
 3     //这个就需要用到yield*语句,用来在一个 Generator 函数里面执行另一个 Generator 函数。
 4     //如果yield命令后面跟的是一个遍历器对象,需要在yield命令后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*语句
 5     function* inner() {
 6         yield 'hello!';
 7     }
 8 
 9     function* outer1() {
10         yield 'open';
11         yield* inner();
12         yield 'close';
13     }
14     let out = outer1();
15     console.log(out.next());  //generator.html:62 {value: "open", done: false}
16     console.log(out.next());  //generator.html:63 {value: "hello!", done: false}
17     console.log(out.next());  //generator.html:64 {value: "close", done: false}
18     console.log(out.next());   //generator.html:65 {value: undefined, done: true}
19     //例子
20     // 数组
21     //yield*命令可以很方便地取出嵌套数组的所有成员
22     function* iterTree(tree) {
23         if (Array.isArray(tree)) {//如果取的值为数组,那么遍历yield出组里的数据
24             for(let i=0; i < tree.length; i++) {
25                 yield* iterTree(tree[i]);
26             }
27         } else {
28             yield tree;
29         }
30     }
31     const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
32     for(let x of iterTree(tree)) {
33         console.log(x);  
34     }
35     //a
36     //b
37     //c
38     //d
39     //e
40     //或者扩展运算符
41     console.log([...iterTree(tree)]) //["a", "b", "c", "d", "e"]

   参考链接:

posted @ 2020-12-13 00:08  willowTree  阅读(84)  评论(0)    收藏  举报