什么是迭代器
- 迭代器iterator,使用户在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节。
- 他就跟数据库中的光标,迭代器最早出现在1974年设计的CLU编程语言中;
- 在各种编程语言的实现中,迭代器的实现方式各不相同,但是基本都有迭代器,比如java、python等
- 迭代器是帮助我们对某个数据结构进行遍历的对象。
- 在javascript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol);
- 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式;
- 在JavaScript中这个标准就是一个特定的next方法;
- nex方法有如下的要求
- 一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象;
- done(boolean)
- 如果迭代器可以产生序列中的下一个值,则为false。
- 如果迭代器已将序列迭代完毕,则为true.这种情况下,value是可选的,如果它依然存在,即为迭代结束之后的默认返回值。
- value
- 迭代器返回的任何JavaScript值,done为true时可省略。
- 将一个普通对象变成可迭代对象
- 先实现一个特定的函数:[symbol.iterator]
- 这个函数需要返回一个迭代器(这个迭代器用于迭代当前的对象)
可迭代对象
- 可迭代对象和迭代器的概念不同;当一个对象实现了iterble protocol协议时,它就是一个可迭代对象这个对象要求是必须实现iterator方法,在代码中我们使用Synbol.iterator访问该属性;
- 当一个对象变成一个可以迭代对象的时候,就可以进行某些迭代操作;
- 比如for..of等操作就是调用它的iterator方法;
- 原生可迭代对象:string、Array、Map、Set、arguments对象、NodeList集合;
可迭代对象的应用
- JavaScript中的语法:for...of、展开语法、yield*、解构赋值
- 创建一些对象时:new Map([lterable])、new WeakMap([iterable])、new WeakSet([itearble]);
- 一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.form(iterable);
迭代器中断
- 迭代器在某些情况下会在没有完全迭代的情况下中断
- 遍历的过程中通过break、return、throw中断了循环操作;
- 结构赋值没有结构所有的值
- 这个时候可以监听中断
-
const obj = { name: "小明", age: 18, skill: "技能", [Symbol.iterator]() { const key = Object.keys(this); let index = 0; const iterator = { next() { if (index < key.length) { return { done: false, value: key[index++] }; } else { return { done: true }; } }, return() { //监听函数 console.log("停止"); return { done: true }; }, }; return iterator; }, };
生成器
-
什么是生成器
- 生成器就是es6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
- 生成器函数也是一个函数,但是和普通的函数有一些区别:
- 首先,生成器函数需要在function的后面添加一个符号:*
- 其次,生成器函数可以通过yield关键字来控制函数的执行流程
- 最后,生成器函数的返回值是一个(Generator)生成器对象;
- 需要执行函数内部的代码,需要生成器对象,调用它的next操作
- 当遇到yield时,就会中断执行。
- 生成器事实上是一种特殊的迭代器
-
function* a() { //生成器函数调用 console.log(1); console.log(2); yield; //暂停函数 console.log(3); console.log(4); yield 123;//yield后面可以跟值,value的值 console.log(5); console.log(6); } const arr = a(); //调用生成器函数,返回一个生成器对象 arr.next(); //调用next方法往下执行 arr.next(); //继续调用next方法往下执行 arr.next(); //继续调用next方法往下执行
-
生成器传递参数-nex函数
- 函数既然可以暂停来分段执行,那么函数应该是可以传递参数的,那样可以分段来传递参数;
- 调用nex的时候,可以給他传递参数,那么这个参数会作为上一个yield语句的返回值,第一次传递的时候就跟普通形参一样传递;
- 也就是说我们为本次的函数代码执行提供了一个值;
-
生成器提前结束
- return函数:return函数也是一个可以给生成器函数传递参数的方法
- return传值后这个生成器函数就会结束,之后调用next不会继续生成值了;
- throw函数:抛出异常后我们可以在生成器函数在捕获异常;
- 但是在catch语句中不能继续yield新的值了,但是可以在catch语句外是固态yield继续中断函数的执行。
-
生成器替代迭代器
- 对之前代码重构;
-
1 const arr1 = ["小明", "小红", "小绿"]; 2 const arr2 = [100, 120, 130, 156, 132, 198]; 3 function* nameiterator(arr) { 4 for (let i = 0; i < arr.length; i++) { 5 yield arr[i]; 6 } 7 } 8 const namesiterator = nameiterator(arr1);//因为生成器的返回值就是迭代器 9 console.log(namesiterator.next()); 10 console.log(namesiterator.next()); 11 console.log(namesiterator.next()); 12 console.log(namesiterator.next());
- yield*相当于一种yidield的语法糖,只不过会一次的迭代这个可迭代对象,每次迭代其中的一个值;
-
1 const arr1 = ["小明", "小红", "小绿"];4321` 2 const arr2 = [100, 120, 130, 156, 132, 198]; 3 function* nameiterator(arr) { 4 yield* arr;//只要是可以迭代的对象就可以使用语法糖,等于上面的for循环 5 } 6 const namesiterator = nameiterator(arr1); 7 console.log(namesiterator.next()); 8 console.log(namesiterator.next()); 9 console.log(namesiterator.next()); 10 console.log(namesiterator.next());
- yield在类中实现
-
class person { constructor(name, age, skill) { this.name = name; this.age = age; this.skill = skill; } *[Symbol.iterator]() { //在类中使用生成器*需要加在前面 yield* this.skill; } } const p = new person("李明", 18, [1, 2, 3]); console.log(p); for (const item of p) { console.log(item); }