翻译:猖獗的手艺宅
在本文中,我们将引见两种提取轮回内数据的要领:内部迭代和外部迭代。
轮回
举个例子,假设有一个函数 logFiles()
:
const fs = require('fs');
const path = require('path');
function logFiles(dir) {
for (const fileName of fs.readdirSync(dir)) { // (A)
const filePath = path.resolve(dir, fileName);
console.log(filePath);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
logFiles(filePath); // (B)
}
}
}
logFiles(process.argv[2]);
从 A 行最先的轮回用来纪录文件途径。它是 for-of
轮回和递归的组合(递归挪用在 B 行)。
假如你发明轮回内的某些数据(迭代文件)有效,但又不想纪录它,那应当怎么办?
内部迭代
提取轮回内数据的第一个要领是内部迭代:
const fs = require('fs');
const path = require('path');
function logFiles(dir, callback) {
for (const fileName of fs.readdirSync(dir)) {
const filePath = path.resolve(dir, fileName);
callback(filePath); // (A)
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
logFiles(filePath, callback);
}
}
}
logFiles(process.argv[2], p => console.log(p));
这类迭代体式格局与Array的 .forEach()
相似:logFiles()
内完成轮回并对每一个迭代值(行A)挪用 callback
。
外部迭代
内部迭代的替换计划是外部迭代:我们完成了一个iterable,可以用生成器协助我们完成:
const fs = require('fs');
const path = require('path');
function* logFiles(dir) {
for (const fileName of fs.readdirSync(dir)) {
const filePath = path.resolve(dir, fileName);
yield filePath;
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
yield* logFiles(filePath); // (A)
}
}
}
for (const p of logFiles(process.argv[2])) {
console.log(p);
}
假如是内部迭代,logFiles()
会挪用我们(“推”给我们)。而这一次,换我们来挪用它了(“拉”过来)。
请注意,在生成器中,必需经由历程 yield*
举行递归挪用(第A行):假如只挪用 logFiles()
那末它会返回一个iterable。但我们想要的是在该 iterable 中 yield
每一个项目。这就是 yield*
的作用。
生成器有一个非常好的特征,就是处置惩罚历程可以与内部迭代一样互锁:每当 logFiles()
建立另一个 filePath
时,我们可以马上检察它,然后 logFiles()
继承。这是一种简朴的合作式多使命处置惩罚,个中 yield
停息当前使命并切换到另一个使命。
扩大浏览
- Chapter “Iterables and iterators” in “Exploring ES6”.
- Chapter “Generators” in “Exploring ES6”.
迎接继承浏览本专栏别的高赞文章:
- 12个令人惊叹的CSS试验项目
- 天下顶级公司的前端口试都问些什么
- CSS Flexbox 可视化手册
- 过节很无聊?照样用 JavaScript 写一个脑力小游戏吧!
- 从设计者的角度看 React
- CSS粘性定位是如何事情的
- 一步步教你用HTML5 SVG完成动画结果
- 程序员30岁前月薪达不到30K,该何去何从
- 第三方CSS平安吗?
- 谈谈super(props) 的重要性