一、什么是迭代器
MDN上讲: 在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。
个人理解:迭代器可以帮助我们对某个数据结构进行遍历的一个对象,但是他与循环不同。可以重复执行多次,并且存在一个明确的终止条件。迭代器中存在next方法和done属性。每次调用的时候 执行next()方法,返回下一个可用的值,若在下一个值的返回的对象中返回的对象中的done为true,则该次迭代终止。
1、迭代器协议
迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。
若要将一个对象转化成一个迭代器。该对象中需要包含一个next()方法。该next方法 一个无参数的或者可以接受一个参数的函数,返回一个应当拥有以下两个属性的对象:
done,返回一个boolean值,若为false,则可以执行下一次,若为true,则表示该迭代器已经迭代完毕,
value:为迭代器返回的值,当done为true的时候可以省略
// 该种形式就是一个迭代器
const iteratorObject = {
next: function() {
return { done: false, value: names[index++] }
}
}
2、可迭代协议
可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 for…of 结构中,哪些值可以被遍历到。一些内置类型同时是内置可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object))。要成为可迭代对象, 一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性
若一个对象满足可迭代协议,那么就可以认为这个对象是可迭代的。
var iteratorObject = {
[Symbol.iterator] : function(){
return iterator;
}
}
二、创建一个可迭代对象
可迭代对象:迭代器接口是我们获取对象迭代器时默认调用的接口,一个实现了迭代接口的对象即是可迭代对象。当对象实现了iterable protocol协议就是可迭代对象
const iterableObj = {
names: ['zhangsan', 'lisi', 'wangwu'],
[Symbol.iterator]: function() {
let index = 0
return {
next: () => { // 一定要是箭头函数,this指向iterableObj才能访问到names属性
if(index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined}
}
}
}
}
}
JS中String,Array,Map,Set,arguments对象,NodeList集合等原生对象已经实现了可迭代协议,会生成一个可迭代对象。
例如:for..of语句只能作用于可迭代对象上,该循环调用的是可迭代对象上的可迭代方法,调用next函数,进行遍历。
展开运算符中
解构中
检测对象是否为可迭代对象
function isIterable(object) {
return typeof object[Symbol.iterator] === 'function';}
console.log(isIterable([1,2,3])); // true
console.log(isIterable('hello')); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false
}
三、什么是生成器
生成器对象是由一个generator function返回的,并且它符合可迭代规范,具备可迭代性。 生成器函数用function* 来定义 。 生成器是一个特殊的迭代器。
生成器与普通函数的区别:
生成器函数会在function后面加一个*号
生成器可以通过yield来控制函数的执行流程
生成器函数的返回值是一个生成器
例如:
function* generator() {
let a = yield 1; // 每一个yield都需要再次执行next()才会执行系一行代码
let b = yield 2;
let c = yield 3*b;
let d = yield 4;
console.log(`a = ${a}, b = ${b}, c = ${c}, d = ${d}`)
}
let gen = generator(); // "Generator { }"
console.log(gen.next(10)); // {value: 1, done: false}
console.log(gen.next(9)); // {value: 2, done: false}
console.log(gen.next(2)); // {value: 6, done: false}
console.log(gen.next(8)); // {value: 4, done: false}
// a = 9, b = undefined, c = 8
// {value: undefined, done: true}
next()中传入的参数,可以当成上一段代码的返回值。
next()`的参数会传给上一条执行的 yield语句左边的变量。
第一次调用gen.next(10),参数10并没有传递给任何变量。
第二次调用gen.next(9)时,参数 9 传递给了上一条执行的 yield语句左边的变量 a
第三次调用gen.next()时,没有传递参数,所以变量 b 的值为undefined
第四次调用gen.next(8)时,虽然返回值中done: true,但也不影响参数的传递
此外,next()函数的返回值,与传递的参数无关,只与yield右边返回的值以及生成器是否结束有关。
生成器中止/抛出异常
Generator.prototype.return()
// Generator.prototype.return()
gen.return(value) //return() 方法返回给定的值并结束生成器。不会再往下执行
Generator.prototype.throw()
// Generator.prototype.throw()
// gen.throw(exception) // 返回带有 done 及 value 两个属性的对象。
function* gen() {
const value = 100
try {
yield value
} catch (error) {
console.log(error)
yield "abc"
}
console.log("第二段代码继续执行")
const value2 = 200
yield value2
console.log('ending')
}
var g = gen();
const result = g.next()
if(result !== 200) {
g.throw('throw')
}
console.log(g.next());
// throw
// 第二段代码继续执行
// {value:200, done:false}
\