图解Js生成器和迭代器
·
Yin灏
下面使用 [...]
语法,将生成的值扩展到一个数组中去
function * getEmojis() {
yield 1;
yield 2;
yield 3;
yield 4;
}
const genObj = getEmojis();
console.log([...genObj]); // [1, 2, 3, 4]
或者使用 for…of 循环
function* getEmojis() {
yield 1;
yield 2;
yield 3;
yield 4;
}
const genObj = getEmojis();
for (let item of genObj) {
console.log(item); // 1 2 3 4
}
测试迭代器属性:
const array = ['1', '2', '3']; // 数组
const string = 'I love JavaScript!'; // 字符串
const object = { name: 'Lydia Hallie' }; // 对象
function regularFunction() { // 函数
return 'I am a basic function.';
}
function * generatorFunction() { // 生成器函数
return 'I am a generator function';
}
const generatorObject = generatorFunction();
console.log(array[Symbol.iterator]); // ƒ values() { [native code] }
console.log(string[Symbol.iterator]); // ƒ [Symbol.iterator]() { [native code] }
console.log(generatorObject[Symbol.iterator]); // ƒ [Symbol.iterator]() { [native code] }
console.log(object[Symbol.iterator]); // undefined
[Symbol.iterator]
必须返回一个迭代器,该迭代器包含一个 next() 方法,该方法返回一个对象 { value: '...', done: false/true }
下面让 object 变得可迭代:
const object = { name: 'Lydia Hallie' };
object[Symbol.iterator] = function *() {
yield this;
}
console.log(object[Symbol.iterator]);
console.log([...object])
for(let item of object) {
console.log(item)
}
// 获取对象的键值
object[Symbol.iterator] = function *() {
yield Object.keys(this);
}
搞定!Object.keys(this)
是个数组,所以生成的值也是一个数组。然后我们把这个生成的数组扩展到另一个数组中,结果就得到一个嵌套的数组。我们并不想要这个,只是想生成每个单独的键!
我们可以用 yield*
来做到这一点。然后我们委托给另一个生成器!
const emojis = ['😊', '😔', '😟'];
function* genFunc() {
yield '☀';
yield * emojis;
yield '🚀';
}
const genObj = genFunc();
console.log([...genObj]); // ['☀', '😊', '😔', '😟', '🚀']
在继续迭代 genObj
迭代器之前,被委托的生成器的每个值都生成了。
生成器函数的另一种用法,是我们可以(在某种程度上)将它们用作为观察者函数。生成器可以等待传入的数据,并且只有当该数据被传递过来时,才会处理它。比如:
function * generatorFunction() {
const second = yield 'First!';
console.log(second);
return 'All done';
}
这里最大的区别是我们不是只有前一个例子中所看到的 yield [value]
,而是将其赋值给一个称为 second
的值,并生成值字符串 First!
。这是第一次调用 next()
方法时会生成的值。
首先遇到第一行上的 yield
,并生成值 First!
。那么,变量 second
的值是什么呢?
变量second
的值实际上是下次我们调用 next()
方法时传递给该方法的值!这一次,我们给next()
方法传一个字符串'I like JavaScript'
。
生成器的最大优点之一就是它们是懒求值的。也就是说,在调用next()
方法后返回的值仅在我们明确要求后才计算!普通函数没有这种功能:所有值都为我们生成了,以防将来某个时间需要用到它。
案例
const bookClubs = [{
name: 'The Cool Club',
clubMembers: [{
name: 'John Doe',
books: [{
id: 'hs891',
title: 'A Perfect'
}, {
id: 'ey812',
title: 'A Good Book'
}]
}]
}, {
name: 'The Better Club',
clubMembers: [{
name: 'John Doe',
books: [{
id: 'u8291',
title: 'A Greet Book'
}, {
id: 'p9201',
title: 'A Nice Book'
}]
}]
}];
// console.log(bookClubs);
function* iterateBooks(books) {
for (let i = 0; i < books.length; i++) {
yield books[i];
}
}
function * iterateMember(members) {
for(let i = 0; i < members.length; i++) {
const clubMember = members[i];
yield * iterateBooks(clubMember.books)
}
}
function * iterateBookClubs(bookClubs) {
for(let i = 0; i < bookClubs.length; i++) {
const bookClub = bookClubs[i];
yield * iterateMember(bookClub.clubMembers);
}
}
// const it = iterateBookClubs(bookClubs);
// console.log(it.next())
// console.log(it.next())
// console.log(it.next())
// console.log(it.next())
// console.log(it.next())
function findBook(id) {
const genObj = iterateBookClubs(bookClubs);
let result = genObj.next();
while(!result.done) {
if(result.value.id === id) {
return result.value;
} else {
result = genObj.next();
}
}
}
console.log(findBook('p9201')); // {id: 'p9201', title: 'A Nice Book'}