通过函数调用进行迭代

Iterating through a function call

本文关键字:迭代 函数调用      更新时间:2023-10-16

经过多年在C++中编写循环的繁琐方式

for(int i=0; i<N; ++i) {
...
}

使用迭代器变得非常好

for(it i=v.begin(); i<v.end(); ++i) {
...
}

并最终转向范围迭代器

for(auto i:v) {
...
}

在JavaScript中也可以使用for,其风格几乎相同(减去类型声明和前/后增量运算符)上面的第一个。

尽管如此,在所有这些中,for仍然存在。D3.js图书馆展示了另一种选择。可以通过写入来迭代数组

d3.select("body")
.selectAll("p")
.data([4, 8, 15, 16, 23, 42])
.enter().append("p")
.text(function(d) { return "I’m number " + d + "!"; });

在这里,enter突变为for循环。文件很好地解释了联接的客户端视图。我缺少的是(函数式编程?)风格的独立示例将函数调用转换为迭代。

毫无疑问,这并不是D3.js独有的。这正是我遇到这个成语的地方。

你能建议几行独立的JavaScript代码吗通过函数调用演示迭代?

我脑海中至少有几个内置函数。

映射()

这一点很明显。

[1, 2, 3]
.map(someNumber => someNumber * someNumber)
.map((powered, index) => index + "::" + powered);
// --> [ "1::1", "2::4", "3::9" ]

链条很好,对吧?接受一些输入并生成由元素组成的结果,这些元素是通过逐元素应用函数计算得出的。

建议:尽可能尝试与纯函数一起使用(对相同的输入产生相同的结果,如果可能的话不要变异原始集合,也不会产生任何副作用)。

forEach()

这个函数也遍历数组的所有元素,并应用一个函数,而不返回任何内容。因此,它只能结束调用链,但不能用于进一步的链接。

[1, 2, 3, 4]
.forEach(number => console.info(number));

建议:forEach()在我们想要编写一些代码时非常有用,这些代码会导致迭代集合中的每个条目产生副作用。

筛选器()

Filter函数使用一个谓词,用于从谷壳中筛选小麦。谓词为您要在下一个"阶段"中处理的项目定义了一个标准。

[null, undefined, 0, 1, 2, 3, NaN, "", "You get the idea"]
.filter(Boolean)
.map(filteredElement => filteredElement + "!")
// --> [ "1!", "2!", "3!", "You get the idea!" ]

建议:尽可能尝试与纯函数一起使用。也就是说,除了与过滤逻辑本身直接相关的事情之外,不要在filter中做任何其他事情。

Object.keys()和Object.entries()

当我们需要迭代对象的键或键值对,而不是数组的元素时,这两个函数非常有用。

const targetObject = { a: 1, b: 2, c: 3 };
Object
.keys(targetObject)
.map(key => key + "=" + targetObject[key])
// --> [ "a=1", "b=2", "c=3" ]

可以像这个一样获得相同的结果

Object
.entries({ a: 1, b: 2, c: 3 })
.map((key, value) => key + "=" + value)
// --> [ "a=1", "b=2", "c=3" ]

建议:使用Object.keys(...)时,可能需要使用Object.hasOwnProperty(...)。有关详细信息,请参阅文档。

find()

这个几乎微不足道。让我们搜索与谓词匹配的项。搜索是"从左到右"的,只要找到第一个"匹配",搜索就会停止。

[1, 5, 10, 15]
.find(number >= 7)
// --> 10

findIndex()函数可用于查找与谓词匹配的元素的位置。

some()和every()

这些功能检查

a) 存在与谓词匹配的至少一个元素;或b) 每个元素都匹配一个谓词。

const arrayOfNumbers = [2, 4, 6, 8, 10];
arrayOfNumbers.every(number => number % 2 === 0); // --> true
arrayOfNumbers.every(number => number % 2 === 1); // --> false
arrayOfNumbers.some(number => number > 1);        // --> true
arrayOfNumbers.some(number => number <= 1);       // --> false

reduce()和`reduceRight()`

在这个快速回顾中最后要提到的是函数,它获取一个事物列表并将其聚合为一个结果。

[-1, 0, 1, 2, 3]
.filter(value => value >= 0) // [0, 1, 2, 3]
.map(value => value + 1)     // [1, 2, 3, 4]
.reduce((subTotal, currentValue) => subTotal + currentValue, 5);
// --> 15

建议:尽可能尝试与纯函数一起使用。


普遍适用的性能说明。在我的基准测试中(手头没有),手工编写的for循环总是比forEachmap和其他迭代函数快。除非性能受到严重影响,否则我仍然更喜欢这些功能。这主要有两个原因:1)更容易避免一次失误;2) 代码可读性更强,因为每个函数都定义了数据处理流程中的独立步骤,从而使代码更简单、更易于维护。


我希望,这是对一些内置的可链式JavaScript函数的一个好的概述。此处介绍更多内容。看看concat()sort()fill()join()slice()reverse()——我也经常使用它们。

如果您需要first()last()之类的东西,您将无法在本机函数中找到它们。要么编写自己的库,要么使用第三方库(例如lodash、rambda.js)

以下是Array.prototype.forEach:的示例实现

function foreach(array, cb) {
for (var i = 0; i < array.length; ++i)
cb(array[i], i, array);
}
foreach([2,8,739,9,0], (n, i) =>
console.log("number: %snindex: %sn", n, i));

我当然不用用勺子喂你了,是吗

function array_iterator(array) {
var i = 0;
function next() {
return array[i++];
}
function head() {
return array[i];
}
function tail() {
return array[array.length-1];
}
function more() {
return i < array.length;
}
function done() {
return !more();
}
function reset() {
i = 0;
}
return { next, head, tail, done, more, reset };
}
var nums = [3,34,4];
var iter = array_iterator(nums);
while (iter.more()) {
console.log(iter.next());
}