介绍
ES5 和 ES6 对 JavaScript 带来了许多变化,包括更好的方法来处理以数组表达的数据集.虽然该语言对数组上的声明性数据操纵的支持得到了显著提高,许多现代浏览器支持它们,但只使用了有限的数组功能。
本文将展示显著的JavaScript数组函数为.map()
,.filter()
和.reduce()
,然后将通过例子,其中.every()
和.some()
将节省计算功率在更显著的解决方案。
.map()
、 .filter()
和 .reduce()
: 主要的 JavaScript 数组函数
使用 .map()
、 .filter()
和 .reduce()
是非常常见的,为本文的目的,让我们把这三个函数称为 MFR。
这些函数比JavaScript中的其他操作数组数据的函数更为突出。 Google Trends例如,显示了对 MFR的更多活跃搜索,而不是其他函数的样本:
Google Search还显示,对 MFR的搜索查询会产生更多的结果。
搜索结果为 .filter()
超过 74M 这比结果为 .every()
高 99.97%,比结果为 some()
高 99.98% 这意味着人们正在说话,写作和教学更多 MFR,这表明随着时间的推移使用量增加。
.every() 和.some() 的优点
在JavaScript中,它通常被教导将数组的计算限制在迭代(.forEach)和转换(.map,.filter,.reduce - a.k.a MFR)操作中。
几乎任何使用 MFR,在那里你需要尽早退出而不通过整个集合,都是对 .some()
或 .every()
的主要候选人,因为它们既缩短电路,也尽早退出数组迭代,而不是运行到尽头,并可能浪费计算资源。
在我们正在构建更复杂的应用程序并向客户端发送更多字节的时代,这导致了显著的JavaScript分析过剩(参见TTI),任何我们可以做的三次迭代(由于短路)而不是三十是非常受欢迎的。
「Array.prototype.every()」
every()
方法允许我们确定一个数组中的每一个元素是否满足一定的要求,它在第一次发现一个不满足该要求的元素时停止对数组(短路)进行评估。
「Array.prototype.some()」
some()
方法允许我们确定一个数组中的 some(至少一个)元素是否满足一定的要求. 它第一次找到满足给定的要求的元素时停止对数组(短路)进行评估。
一个例子,两个场景
假设您被要求写一个简单的 add()
函数来添加一堆整数. 该函数应该预期会给出任何要添加的整数,并在计算添加后返回其总数。
以下是完成这个任务的两种方法。
剧本一
如果 add() 函数被给予混合输入(例如,整数,浮动,未定义,字符串等),它应该只使用整数来返回添加它们的总和 我们可以这样实现 add() 函数:
1const add = (...entries) => {
2 return entries
3 .filter(Number.isInteger)
4 .reduce((sum, int) => {
5 return sum + int;
6 }, 0);
7};
由于这个场景预计add()
函数将从给定的输入中存在的任何整数计算出总和,所以我们可以像我们在上一个代码块中一样执行函数,我们首先使用.filter()
来提取只有整数,然后用.reduce()
计算总和。
还可以说,我们可以得到这样的更好的解决方案:
1const add = (...entries) => {
2 return entries.reduce((sum, entry) => {
3 if(Number.isInteger(entry)) {
4 return sum + entry;
5 }
6 return sum;
7 }, 0);
8};
这个add()
的选择有点好,因为,与第一个重复来识别有效的数据,然后再重复来计算基于有效的数据的结果不同,现在只有一个重复块。
我们需要一种方法,首先知道收集的输入是否至少有一个整数. 由于我们正在尝试从输入中找到总数,我们实际上需要至少有两个整数。
让我们使用 .some()
以确保满足此条件:
1const add = (...entries) => {
2 let theSum = 0;
3 if(hasTwoOrMoreInts(entries)){
4 // there are >= 2 integers, lets sum them
5 theSum = entries.reduce((sum, entry) => {
6 if(Number.isInteger(entry)) {
7 return sum + entry;
8 }
9 return sum;
10 }, 0);
11 }
12 return theSum;
13};
现在我们有一个条件,它阻止了总数的计算,除非我们确信有两个或多个整数。
1const hasTwoOrMoreInts = (entries) => {
2 let lastIndex = -1;
3 let hasMinimumIntsCount = false;
4
5 const hasAnInt = entries.some((entry, index) => {
6 lastIndex = index;
7 return Number.isInteger(entry);
8 });
9
10 if(hasAnInt === true) {
11 // we've got one int, is there another?
12 const hasMoreInts = entries.slice(lastIndex + 1).some(Number.isInteger);
13 hasMinimumIntsCount = (hasMoreInts === true) && hasAnInt;
14 }
15
16 return hasMinimumIntsCount;
17};
剧本2
如果add()
函数可以接收混合输入(如整数、浮数、未定义、字符串等),但只需要继续计算给定的输入的总和,如果所有输入都是整数,那么由MFR的突出性影响的一种常见方法可能看起来像这样:
1const add = (...entries) => {
2 let theSum = 0;
3 const nonInts = entries.filter(entry => !Number.isInteger(entry));
4 if(nonInts.length === 0) { // are there non-ints?
5 theSum = entries.reduce((sum, int) => {
6 return sum + int;
7 }, 0);
8 }
9 return theSum;
10}
再一次,‘entries.filter()’试图通过重复整个‘entries’数组来查看是否有无效的输入,以收集每个输入而不是整数。如果没有无效的输入(‘nonInts.length === 0’),我们用‘.reduce()’计算了总和。
在这种情况下,一旦找到一个不是整数的输入,它就不会再看得出来(通过退出返回false
)并允许我们继续进行下一个重要的事情。
让我们看看‘.every()’如何为我们做到这一点:
1const add = (...entries) => {
2 let theSum = 0;
3 const areAllInts = entries.every(Number.isInteger);
4 if(areAllInts === true) { // are these indeed all ints?
5 theSum = entries.reduce((sum, int) => {
6 return sum + int;
7 }, 0);
8 }
9 return theSum;
10};
由于entries.every()
会返回false
,一旦发现不是整数,我们可以对entries
中的无效元素进行进一步测试,从而释放可能需要为移动用户提供流畅滚动体验的资源。
一个更实际的例子
程序员通常不会每天写add()
函数,所以让我们看看一个现实世界的例子:将.every()
和.some()
应用到假设的HTML形式验证中。
我们希望在从表单中获取并验证所有所需数据后才提交表单,我们也希望至少填写一份可选数据:
1const requiredFields = Array.of(
2 isFirstNameValid(),
3 isLastNameValid(),
4 isEmailValid(),
5 isAboutMeValid()
6);
7
8const optionalFields = Array.of(
9 isTwitterValueValid(),
10 isFacebookValue(),
11 isGoogleplusValueValue()
12);
13
14const isValid = (inputStatus) => inputStatus === true;
15
16if(requiredFields.every(isValid) && optionalFields.some(isValid)) {
17 // all required fields are valid
18 // and at least one social media field is valid
19 // lets proceed to submit the form now
20} else {
21 // lets tell the user we are serious here
22 // this will happen really fast since we are short-circuiting
23 // with .some and .every above
24}
如上所述, Array.of()
中的所有函数都对特定表单字段进行验证,然后返回 true
或 false
. 由于 Array.of()
是从所给定参数构建数组的工厂(验证功能在我们的情况下),这意味着 optionalFields' 最终会看起来像
[true, false, false]'。 因此,如果 `requiredFields' 中的任何值都是假的,或者如果任何一个 'optionalFields' 都不是真的,我们不会提交表单。
结论
这个探索有助于揭示 MFR的突出地位,它还揭示了为什么.every()
和.some()
可以为您提供更有效的策略来操纵数组。
以下是促進或採用.some()
或.every()` 在您的 JavaScript 代碼庫中的幾個可能的情況:
- A
.filter()
随后立即由.forEach()
,.map()
,.reduce()
,或涉及它们的任何连锁组合 - A
.filter()
随后立即由检查召唤结果的条件,并且该条件的体内包含一个.forEach()
,.map()
,.reduce()
,或涉及它们的任何连锁组合,作用于.filter()
的结果。
对于这些和其他相关情况,尽快退出检查将优化您的资源。