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 工作原理:
- 创建一个指针对象,指向数据结构的起始位置。
- 第一次调用next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
- 每调用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 特点
- 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的遍历器对象
- 每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。
- 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"]
参考链接: