迭代器和生成器的使用
Oct 22, 2021
# ES6
生成器和迭代器的了解和使用
# 迭代器
# 使用
js
let arr = [1, 2, 3, 4];
let iter = arr[Symbol.iterator];
console.log(iter); // ƒ values() { [native code] }
// 执行这个方法
iter = arr[Symbol.iterator]();
console.log(iter); // Array Iterator {}
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 4, done: false}
console.log(iter.next()); // {value: undefined, done: true}
console.log(iter.next()); // {value: undefined, done: true}
# 模拟实现
js
function makeIterator(array) {
let nextIndex = 0;
return {
next: function() {
return nextIndex < array.length
? { value: array[nextIndex++], done: false }
: { value: undefined, done: true };
}
}
}
let iter = makeIterator([1, 2, 3, 4]);
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 4, done: false}
console.log(iter.next()); // {value: undefined, done: true}
# for...of
ES6借鉴了c++,java,c#,python,这些语言里都存在for...of
循环,这个实际上就是一个迭代的过程。javascript中,for...of
就是调用了Symbol.iterator
这个接口。
js
// 数组
let arr = [1, 2, 3, 4];
for(let i of arr) {
console.log(i);
}
// 1
// 2
// 3
// 4
// 对象
let obj = {
start: [1, 2, 3, 4],
end: [5, 6, 7]
}
//没有部署Symbol.iterator的情况下,进行迭代
for(let i of obj) { // Uncaught TypeError: obj is not iterable
console.log(i);
}
// for...in 是用来遍历对象的
for(let i in arr) {
console.log(i);
}
// 0
// 1
// 2
// 3
只有部署了迭代器接口Symbol.iterator
,for...of
才能遍历。 Symbol.iterator
对数据读取是一种有序的,连续的,基于拉取的方式,而对象数据的组织方式是无序的,所以要把对象排除在外。通过部署Symbol.iterator
,可以实现让for...of
迭代对象。
# 部署Symbol.iterator
js
let obj = {
start: [1, 2, 3, 4],
end: [5, 6, 7],
[Symbol.iterator]() {
let index = 0;
let arr = [...this.start, ...this.end];
let len = arr.length;
return {
next() {
return index < len
? { value: arr[index++], done: false }
: { value: undefined, done: true }
}
}
}
};
for (const i of obj) {
console.log(i); // 1 2 3 4 5 6 7
}
# tips
- 部署了
Symbol.iterator
接口的对象,可以使用...
运算符展开。 - 调用了
Symbol.iterator
的next()
方法,到{value: undefined, done: true}
后,再使用for...of
遍历可能会失效。
# 生成器
# yield关键字
js
function * test() {
yield 'a';
yield 'b';
yield 'c';
return 'd';
}
// 返回生成器对象, yield:产出一个值, 暂停函数运行
const gen = test();
console.log(gen.next()); // {value: "a", done: false}
console.log(gen.next()); // {value: "b", done: false}
console.log(gen.next()); // {value: "c", done: false}
console.log(gen.next()); // {value: "d", done: true} yield产出有值为false,产出没值为true
console.log(gen.next()); // {value: undefined, done: true}
// yield 暂停(记忆功能),只能出现在生成器函数中
// return 结束函数执行
# yield返回值的传入
js
function * test() {
let a = yield 'a';
console.log(a);
yield 'b';
yield 'c';
return 'd';
}
let gen = test();
console.log(gen.next()); // {value: "a", done: false}
console.log(gen.next(10)); // 10 {value: "b", done: false}
# yield单独成立方式
js
function * demo() {
// yield
console.log('hello' + (yield)); // yield不单独成立,要成立需加()
}
let gen = demo();
console.log(gen.next()); // {value: undefined, done: false}
js
function * demo() {
// yield
console.log('hello' + (yield 123));
}
let gen = demo();
console.log(gen.next()); // {value: 123, done: false}
# yield作为函数参数
js
function * demo() {
foo(yield 'a', yield 'b');
}
function foo(a, b) {
console.log(a, b);
}
let gen = demo();
console.log(gen.next()); // {value: "a", done: false}
console.log(gen.next()); // {value: "b", done: false}
console.log(gen.next()); // undefined undefined {value: undefined, done: true}
# 使用for...of遍历生成器对象
js
function * foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
yield 6;
return 7;
}
for(let i of foo()) {
console.log(i); // 1 2 3 4 5 6
}
# 接收yield的产出值
js
function * foo() {
let value1 = yield 1;
console.log('value1: ' + value1);
let value2 = yield 2;
console.log('value2: ' + value2);
let value3 = yield 3;
console.log('value3: ' + value3);
let value4 = yield 4;
console.log('value4: ' + value4);
}
// 蛇形的传值方式
let gen = foo();
console.log(gen.next()); // {value: 1, done: false}
console.log(gen.next('two')); // value1: two {value: 2, done: false}
console.log(gen.next('three')); // value2: three {value: 3, done: false}
console.log(gen.next('four')); // value3: four {value: 4, done: false}
console.log(gen.next('five')); // value4: five {value: undefined, done: true}
# 优化Symbol.iterator
js
let obj = {
start: [1, 2, 3],
end: [7, 8, 9],
[Symbol.iterator]: function * () {
let index = 0;
let arr = [...this.start, ...this.end];
let len = arr.length;
while(index < len) {
yield arr[index++];
}
}
}
for (let i of obj) {
console.log(i); // 1 2 3 7 8 9
}
js
let obj = {
a: 1,
b: 2,
c: 3,
[Symbol.iterator]: function * () {
let index = 0;
let map = new Map();
for (let [key, value] of Object.entries(this)) {
map.set(key, value);
}
let mapEntries = [...map.entries()];
while(index < mapEntries.length) {
yield mapEntries[index++];
}
}
}
for(let i of obj) {
console.log(i);
}
// [ 'a', 1 ]
// [ 'b', 2 ]
// [ 'c', 3 ]
# 模拟实现async/await
js
function Co (generator) {
return new Promise((resolve, reject) => {
const next = (data) => {
const { value, done } = generator.next(data);
if (done) {
resolve(data);
} else {
value.then(val => next(val), reject);
}
};
next();
});
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000);
});
function * test() {
const a = yield p1;
const b = yield p2;
console.log(`a: ${a}, b: ${b}`);
}
Co(test()); // a: 1, b: 2