迭代器(iterator)
迭代器(iterator),是确使用户可在容器对象(container,例如链表或数组)上遍访的对象,使用该接口无需关心对象的内部实现细节。
迭代器是帮助我们对某个数据结构进行遍历的对象
在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol):
- 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式
- 在js中这个标准就是一个特定的next方法
next方法有如下的要求:
-
一个无参数或者一个参数的函数(如生成器就是个特殊的迭代器,他就需要一个参数),返回一个应当拥有以下两个属性的对象
1.1 done(boolean):如果迭代器可以产生序列中的下一个值,则为 false。(这等价于没有指定 done 这个属性。),如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即为迭代结束之后的默认返回值
1.2 value:迭代器返回的任何 JavaScript 值。done 为 true 时可省略
迭代器的实现
基本格式
const iterator = {
next: function() {
return {
done: true,
value: 1
}
}
}
通过迭代器对象访问数组
const nums = ['1', '2', '3']
let index = 0
const numsIterator = {
next: function() {
if (index < nums.length) {
return { done: false, value: nums[index++] }
} else {
return { done: true, value: undefined }
}
}
}
console.log(numsIterator.next()); // { done: false, value: '1' }
console.log(numsIterator.next()); // { done: false, value: '2' }
console.log(numsIterator.next()); // { done: false, value: '3' }
console.log(numsIterator.next()); // { done: true, value: undefined }
console.log(numsIterator.next()); // { done: true, value: undefined }
生成迭代器函数
const nums = ['1', '2', '3']
function createArrIterator(arr) {
let index = 0
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
const numsIterator = createArrIterator(nums)
console.log(numsIterator.next()); // { done: false, value: '1' }
console.log(numsIterator.next()); // { done: false, value: '2' }
console.log(numsIterator.next()); // { done: false, value: '3' }
console.log(numsIterator.next()); // { done: true, value: undefined }
console.log(numsIterator.next()); // { done: true, value: undefined }
可迭代对象
- 它和迭代器是不同的概念
- 当一个对象实现了iterable protocol协议时,它就是一个可迭代对象
- 这个对象的要求是必须实现 @@iterator 方法,在代码中我们使用 Symbol.iterator 访问该属性
- 当一个对象变成一个可迭代对象的时候,进行某些迭代操作,比如 for...of 操作时,其实就会调用它的@@iterator 方法
const iteratorObj = {
nums: ['1', '2', '3'],
[Symbol.iterator]: function() {
let index = 0
return {
next: () => {
if (index < this.nums.length) {
return { done: false, value: this.nums[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
}
const numsIterator = iteratorObj[Symbol.iterator]()
console.log(numsIterator.next()); // { done: false, value: '1' }
console.log(numsIterator.next()); // { done: false, value: '2' }
console.log(numsIterator.next()); // { done: false, value: '3' }
console.log(numsIterator.next()); // { done: true, value: undefined }
console.log(numsIterator.next()); // { done: true, value: undefined }
// 当他是可迭代对象时 就可以用for..of
for (const iterator of iteratorObj) {
console.log(iterator); // 1 2 3
}
可迭代对象的应用
- JavaScript中语法:for ...of、展开语法(spread syntax)、yield*、解构赋值(Destructuring_assignment)
- 创建一些对象时:new Map([Iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable])
- 一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable);
原生迭代器对象
很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的:String、Array、Map、Set、arguments对象、NodeList集合
生成器
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
生成器函数也是一个函数,但是和普通的函数有一些区别:
- 生成器函数需要在function的后面加一个符号:*
- 生成器函数可以通过yield关键字来控制函数的执行流程
- 生成器函数的返回值是一个Generator(生成器)
- 生成器事实上是一种特殊的迭代器
function* foo() {
console.log('start');
const val1 = 1
console.log(val1);
yield
const val2 = 2
console.log(val2);
yield
const val3 = 3
console.log(val3);
yield
console.log('end');
}
// 生成器函数foo的执行体没有执行,它只是返回了一个生成器对象
const generator = foo()
// 执行到第一个yield,且暂停
generator.next()
// 执行到第二个yield,且暂停
generator.next()
// 执行到第三个yield,且暂停
generator.next()
// 执行剩余的
generator.next()
生成器函数执行 上面代码中我们直接调用foo他是没有执行的,他只是返回了一个生成器对象。要执行需要调用next(),但在上面的代码中next返回的是一个undefined,如果我们不希望返回的是undefined,我们可以通过yield来返回结果
function* foo() {
console.log('start');
const val1 = 1
console.log(val1);
yield val1
const val2 = 2
console.log(val2);
yield val2
const val3 = 3
console.log(val3);
yield val3
console.log('end');
}
// 生成器函数foo的执行体没有执行,它只是返回了一个生成器对象
const generator = foo()
// 执行到第一个yield,且暂停
console.log(generator.next());
// 执行到第二个yield,且暂停
console.log(generator.next());
// 执行到第三个yield,且暂停
console.log(generator.next());
// 执行剩余的
console.log(generator.next());
生成器传递参数
function* foo(initial) {
console.log('start');
const val1 = yield '1' + initial
const val2 = yield '2' + val1
const val3 = yield '3' + val2
console.log('end');
}
const generator = foo('xt')
console.log(generator.next()); // { value: '1xt', done: false }
console.log(generator.next('tx')); // { value: '2tx', done: false }
console.log(generator.next('haha')); // { value: '3haha', done: false }
console.log(generator.next()); // { value: undefined, done: true }
生成器替代迭代器
const nums = ['1', '2', '3']
function* createArrIterator(arr) {
for (const item of arr) {
yield item
}
}
const numsIterator = createArrIterator(nums)
console.log(numsIterator.next()); // { done: false, value: '1' }
console.log(numsIterator.next()); // { done: false, value: '2' }
console.log(numsIterator.next()); // { done: false, value: '3' }
console.log(numsIterator.next()); // { done: true, value: undefined }
还可以使用yield*来生产一个可迭代对象
const nums = ['1', '2', '3']
function* createArrIterator(arr) {
yield* arr
}
const numsIterator = createArrIterator(nums)
console.log(numsIterator.next()); // { done: false, value: '1' }
console.log(numsIterator.next()); // { done: false, value: '2' }
console.log(numsIterator.next()); // { done: false, value: '3' }
console.log(numsIterator.next()); // { done: true, value: undefined }
解决回调地狱
function getData() {
requestData('url').then(res => {
requestData('url' + res).then(res1 => {
requestData('url' + res1).then(res2 => {
console.log(res2);
})
})
})
}
如上我们每次请求需要用到上一次的结果,这样我们就需要不停的去嵌套,这样就会变得很难维护,如何解决呢?
function getData() {
requestData('url').then(res => {
return requestData('url' + res)
}).then(res1 => {
return requestData('url' + res1)
}).then(res2 => {
return requestData('url' + res2)
}).then(res3 => {
console.log(res3);
})
}
我们首先会或想到我们可以直接返回一个Promise,如上,但是他的阅读性还是比较差,所以又可以想到用生成器的方式。
function* getData() {
const res = yield requestData('url')
const res1 = yield requestData('url' + res)
const res2 = yield requestData('url' + res1)
const res3 = yield requestData('url' + res2)
console.log(res3);
}
// 自动执行generator函数
function execGenerator(genFn) {
const generator = genFn()
function exec(res) {
const result = generator.next(res)
if (result.done) return result.value
result.value.then(res => {
exec(res)
})
}
exec()
}
execGenerator(getData)
因为在生成器函数中我们可以通过yield 来暂停函数的执行,所以我们可以利用这个特性解决他,看到这里你其实我们就可以想到async/await,其实async/await就是这个的语法糖