迭代器(iterator) 简单介绍
迭代器是一个对象,它可以帮助我们遍历某种容器对象(例如数组,map,set,字符串),即:迭代器是能够帮助我们对某个数据结构进行遍历的对象)
迭代器并不是js特有;java,python也有迭代器,实现方式不同
js中的迭代器
在js中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol)
迭代器协议定义产生一系列值(不管是有限个还是无限个)的标准方式;在js中,这个标准就是一个”特定”的next方法:
next方法要求:
1.一个无参或者有一个参数的函数,
2.返回一个对象, 这个对象包含两个属性
done:当所有想要遍历的对象已经遍历完了,则为true,否则为false
value:当前遍历到的值,如果done为true,则value可忽略(undefined)
ps:建议都写上,提高代码可读性
自定义迭代器 可遍历数组的迭代器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const arr = [1 , 2 , 3 ];const createIterator = (arr ) => { let index = 0 const iterator = { next ( ) { if (index < arr.length ) { return { value : arr[index++], done : false } } else { return { value : undefined , done : true } } } } return iterator; } let iterator = createIterator (arr);console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());
一个无限迭代器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createIterator ( ) { let index = 0 ; return { next ( ) { return { value : index++, done : false } } } } let a = createIterator ()console .log (a.next ());console .log (a.next ());console .log (a.next ());
可迭代对象(iterable) 在实现自定义迭代器的代码中,相关变量的 关联性较强;我们将以上代码再次抽取封装,让其变成一个可迭代对象.
简单介绍 什么是可迭代对象?
他与迭代器是不同的概念
当它实现了iterable protocol协议 ,它就是一个可迭代对象
对这个对象的要求是必须实现@@itaretor ,在代码中可使用[Symbol.iterator]来访问该方法
而[Symbol.iterator]要求返回一个迭代器(对象)
可迭代对象的实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const iterableObj = { values : [1 , 2 , 3 ], [Symbol .iterator ]: function ( ) { let index = 0 ; const iterator = { next : () => { if (index < this .values .length ) { return { value : this .values [index++], done : false } } else { return { value : undefined , done : true } } } } return iterator } } let iterator = iterableObj[Symbol .iterator ]();console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());iterator = iterableObj[Symbol .iterator ](); console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());
for…of 原理 for…of遍历的对象必须是一个可迭代对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 console .log (Object .getOwnPropertySymbols (Array .prototype )); console .log (Object .getOwnPropertySymbols (Map .prototype )); console .log (Object .getOwnPropertySymbols (Set .prototype )); console .log (Object .getOwnPropertySymbols (String .prototype ));console .log (Array .prototype [Symbol .iterator ]); console .log (Map .prototype [Symbol .iterator ]); console .log (Set .prototype [Symbol .iterator ]); console .log (String .prototype [Symbol .iterator ]);
可见,Array ,Map 和Set 和String 均实现了[Symbol(Symbol.iterator)]这个方法
类似的,arguments,NodeList集合也都是可迭代对象(实现了[Symbol(Symbol.iterator)]方法 )
1 2 3 4 5 function foo ( ) { console .log (Object .getOwnPropertySymbols (arguments )); } foo ()
对象{}因为没有实现[Symbol(Symbol.iterator)]这个方法,所以不能使用for…of循环
1 2 console .log (Object .getOwnPropertySymbols (Object .prototype ));
示例 原生数据结构使用for…of遍历 1 2 3 4 5 6 7 8 9 10 let arr = [1 , 2 , 3 ]let set = new Set (arr);let map = new Map ();map.set (1 , 1 ); for (let item of map) { console .log (item); }
自定义可迭代对象使用for…of 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 for (let item of iterableObj) { console .log (item); } let iterator = [1 , 2 , 3 ][Symbol .iterator ]();console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());console .log (iterator.next ());function foo ( ) { let iterator = arguments [Symbol .iterator ](); let next = iterator.next (); while (!next.done ) { console .log (next.value , 'next' ); next = iterator.next (); } } foo (1 , 2 , 3 )
在迭代器对象的next方法返回的对象的done为false时,js会将value赋值给for…of循环的item
这就是for…of循环的本质
可迭代对象的应用 for…of 展开语法(…) 1 2 3 4 5 6 console .log (...iterableObj);console .log (...new Set ([1 , 2 , 3 ]));console .log (...new Map ([[1 , 1 ], [2 , 2 ], [3 , 3 ]]));
yield* 见生成器模块
解构赋值 1 2 3 4 const names = ['hrm' , 'djw' , 'hrc' ]const [value1, value2] = names;console .log (value1, value2);
一些方法的调用 在创建一些对象时,其构造函数的参数可传入一个可迭代对象
(可参看Map,Set构造函数的ts文件类型声明)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface SetConstructor { new <T = any>(values?: readonly T[] | null ): Set <T>; readonly prototype : Set <any>; } interface MapConstructor { new (): Map <any, any>; new <K, V>(entries?: readonly (readonly [K, V])[] | null ): Map <K, V>; readonly prototype : Map <any, any>; } console .log (...new Set ([1 , 2 , 3 ]));console .log (...new Map ([[1 , 1 ],[2 , 2 ]]));
除此之外,还有例如Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable)等方法
自定义类的可迭代性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 class Room { constructor (address, name ) { this .address = address; this .name = name; this .students = []; } pushStudent (student ) { this .students .push (student); } [Symbol .iterator ]() { let index = 0 ; return { next : () => { if (index < this .students .length ) { return { done : false , value : this .students [index++] } } else { return { done : true , value : undefined } } }, return : () => { console .log ('迭代器提前终止' ); return { done : true , value : undefined } } } } } let room = new Room ('行远楼A105' , '软件工程教室' );room.pushStudent ('hrm' ); room.pushStudent ('xcl' ); room.pushStudent ('dmy' ); room.pushStudent ('gq' ); for (const student of room) { console .log (student); if (student === 'dmy' ) { break ; } } for (const student of room) { console .log (student); }
其他补充 展开运算符… 1 2 3 4 5 6 7 8 9 10 11 12 13 let obj = { name : 'hrm' , age : '18' , friends : { name : 'djw' , age : 18 }, } let info = { ...obj }info.friends .name = null ; console .log (info.friends );console .log (obj.friends );
说明:这是ES9引入的新语法,是对”…”展开运算符功能的拓展 ,和迭代器没有关系 ;而且,在对象内部使用…展开运算符,是对对象属性的浅拷贝.
想要对对象实现遍历,我们可以换一种思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 let obj = { name : 'hrm' , age : '18' , friends : { name : 'djw' , age : 18 }, } for (const entry of Object .entries (obj)) { console .log (entry) } const { name, age } = obj;console .log (name, age);