Iterator——迭代器
什么是迭代器
js中有数组和对象数据类型,es6之后又添加了set与map类型,这些数据类型的遍历方式并不相同,我们期望可以通过统一的形式实现对不同数据类型的遍历。ES6 新增的迭代器可以很好地解决 ES6 之前的迭代问题。
迭代器(Iterator)是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作,依次处理该数据结构的所有成员。 当我们需要执行迭代时,可迭代对象(iterable) 的 迭代器工厂函数会创建一个迭代器(iterator) ,这个迭代器会在每一次的循环中返回我们需要的数据。
迭代器执行逻辑
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的
next方法,可以将指针指向数据结构的第一个成员。 - 第二次调用指针对象的
next方法,指针就指向数据结构的第二个成员。 - 不断调用指针对象的
next方法,直到它指向数据结构的结束位置。
function myIterator(arr) {
let nextIndex = 0
return {
next: function() {
return nextIndex < arr.length
? { value: arr[nextIndex++], done: false }
: { value: undefined, done: true }
}
}
}
let arr = [1, 4, 'ads']// 准备一个数据
let iteratorObj = myIterator(arr)
console.log(iteratorObj.next()) // 所有的迭代器对象都拥有next()方法,会返回一个结果对象
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
调用 Iterator 接口的场合
- for...of
- 展开语法
- 解构语法
- yield*
- promise.all(iterable)
- promise.race(iterable)
- Array.from(iterable)
遍历方式的对比
// for循环 写法繁琐
for (var index = 0; index < myArray.length; index++) {
console.log(myArray[index]);
}
// forEach 无法跳出循环
myArray.forEach(function (value) {
console.log(value);
});
// for...in... 针对对象设计,使用索引获取键值
for (var index in myArray) {
console.log(myArray[index]);
}
//for...of... 不支持普通对象的遍历
for (let value of myArray) {
console.log(value);
}
// 提供了遍历所有数据结构的统一操作接口。
//不同于forEach方法,它可以与break、continue和return配合使用。
//有着同for...in一样的简洁语法,比for...in更适合数组操作。
原生object对象是默认没有部署Iterator接口,即object不是一个可迭代对象。因为遍历时,不知道到底哪个属性先遍历,哪个属性后遍历,需要开发者手动指定。不过object部署Iterator接口没有必要,因为ES6提供了Map数据结构。实际上对象被解构时,会被当作Map进行解构。所以虽然Map和Object很多地方相似,但ES6引入Map、Set对象是有其原因的
Generator——生成器
什么是生成器
生成器是ES6新增的一种可以对函数控制的方案,能灵活的控制函数的暂停执行,继续执行等。是一种异步编程解决方案,语法行为与传统函数完全不同。
生成器函数与普通函数对比
- 定义: 普通函数
function定义,生成器函数function*,要在后面加* - 生成器函数可以通过
yield来控制函数的执行,可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果。 - 生成器函数返回一个生成器(generator),生成器是一个特殊的迭代器
生成器执行
- 调用生成器函数会产生生成器对象
- 生成器对象一开始处于暂停状态 suspended
- 生成器对象拥有和迭代器相似 的 next() 方法
- 生成器函数只会在初次调用 在next() 方法后开始执行
yield 的特性是
- yield 只能在生成器内部使用,其他地方会报错
- 生成器函数遇到 yield 之前会正常执行
- 遇到之后,执行会停止,函数作用域会被保留
- 通过生成器对象调用 next() 恢复 yield 暂停的生成器执行
- yield 将生成的值返回给 next() 返回的对象里
- 直到遇到 return,生成器退出,并处于 done: true 状态
/*执行 Generator 函数会返回一个生成器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对
象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。*/
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()// { value: 'hello', done: false }
hw.next()// { value: 'world', done: false }
hw.next()// { value: 'ending', done: true }
hw.next()// { value: undefined, done: true }
// next()参数会被当作上一个 yield 表达式的返回值
function* createIterator() {
let first = yield 1
let second = yield first + 2
yield second + 3
}
let iterator = createIterator()
iterator.next() // {value:1,done:false}
iterator.next(4) // {value:6,done:false}
iterator.next(5) // {value:8,done:false}
iterator.next() // {value:undefined,done:true}