在JavaScript中,every 和some 可以帮助你测试某个东西对数组中的每个元素或某些元素是否为真。
在这篇文章中,我将告诉你如何使用这些有用的数组方法。
1 every() 和some() 如何工作 - 概述
首先,我们需要一些数据来测试。为了简单起见,我们考虑一个数字数组:
const nums = [34, 2, 48, 91, 12, 32];
现在我们假设要测试数组中的每个数字是否小于100 。使用every ,我们可以很容易地进行测试,如下所示:
nums.every(n => n < 100);
// true
又短又好!你可以这样想,这里发生的事情是这样的:
every在数组元素上从左到右循环。- 对于每一个迭代,它调用给定的函数,以当前数组元素作为它的第一个参数。
- 循环一直持续到该函数返回一个**错误的值**。在这种情况下,
every返回false- 否则返回true。
some 循环的工作方式也与 非常相似。every
some在数组元素上从左到右循环。- 对于每个迭代,它调用给定的函数,并将当前数组元素作为其第一个参数。
- 循环一直持续到该函数返回一个**真值**。在这种情况下,
some返回true- 否则返回false。
现在让我们用some 来测试数组中的某个数字是否为奇数:
nums.some(n => n % 2 == 1);
// true
这确实是真的!91 是奇数。
但这并不是故事的结束。这些方法还有一些深度。让我们深入了解一下。
2 every 和some#的参数
使用every 和some 数组方法的方法是完全一样的。它们有相同的参数集,这些参数的含义也是相同的。所以,要想一下子学会它们是非常容易的。
我们已经使用了这些方法的第一个参数,它是一个函数。我们称这个函数为谓词。
在计算机科学中,**谓词**是一组参数的函数,返回一个布尔值作为答案。JavaScript把我们给
every/some的函数当作一个谓词。我们可以返回任何类型的值,但那些都被当作布尔值,所以通常把这个函数称为谓词。
它们还有一个可选的第二参数,用于控制非箭头谓词中的this 。我们称它为thisArg 。
所以你可以通过以下方式调用这些方法:
arr.every(predicate)
arr.every(predicate, thisArg)
arr.some(predicate)
arr.some(predicate, thisArg)
下面我们来看看predicate 和可选的thisArg 的详细情况。
2.1predicate
通过predicate,every/some ,不仅可以让我们访问当前的数组元素,还可以通过其参数访问其索引和原始数组,如下所示:
- 第一个参数:当前的数组元素。
- 第二个参数:当前元素的索引.
- 第三个参数:调用
every/some的数组本身。
在前面的例子中我们只看到第一个参数的作用。让我们通过定义另外两个参数来抓住索引和数组。这一次,假设我们有一些T-Shirt数据来测试是否所有的T-Shirt都有freeCodeCampe:
let tshirts = [
{ size: "S", color: "black", logo: "freeCodeCamp" },
{ size: "S", color: "white", logo: "freeCodeCamp" },
{ size: "S", color: "teal", logo: "freeCodeCamp" },
{ size: "M", color: "black", logo: "freeCodeCamp" },
{ size: "M", color: "white", logo: "freeCodeCamp" },
{ size: "M", color: "teal", logo: "freeCodeCamp" },
{ size: "L", color: "black", logo: "freeCodeCamp" },
{ size: "L", color: "white", logo: "freeCodeCamp" },
{ size: "L", color: "teal", logo: "freeCodeCamp" },
];
tshirts.every((item, i, arr) => {
console.log(i);
console.log(arr);
return item.logo == "freeCodeCamp";
})
在你的控制台中试一下,看看输出结果。不要忘了也玩一下some 。
2.可选的 thisArg#
如果在任何情况下你需要在你的谓词里面有一个特定的this 值,你可以用thisArg 。注意这只适用于非箭头谓词,因为箭头函数没有this 绑定。
如果你省略了这个参数,this 在谓词(非箭头函数)里面照常工作,也就是说:
- 在严格模式下,
this是undefined。 - 在马虎模式下,
this是全局对象,在浏览器中是window,在Node中是global。
我想不出任何关于thisArg 的好用例。但我认为它的存在是好的,因为现在你可以在你的谓词中控制this 。因此,即使有一天需要用到它,你也会知道有一个方法。
如果你有任何关于使用thisArg 的好主意,请在Twitter上告诉我 :)
3 every 和some#的边缘案例
3.1当every 和some 在一个空数组上被调用时会发生什么?
有时你想测试的数组可能是空的。例如, 你从一个API中获取了一个数组, 它在不同的时候可以有任意数量的元素, 包括0.
对于every ,一个true 的返回值可能意味着两种情况:
- 如果数组有超过零的元素,那么数组的所有元素都满足谓词。
- 数组没有任何元素。
所以如果我们想的话,我们可以在谓词里面做一些疯狂的事情,就像下面这样:

const myCatsBankAccounts = [];
myCatsBankAccounts.every(account => account.balance > elonMusk.totalWealth)
并且仍然得到true 作为返回值!
如果数组是空的,JavaScript直接返回true ,而不需要调用任何谓词。
这是因为在逻辑学中,你可以对空集的元素说任何话,而这被认为是真实的,或者更确切地说,是空的真实。这样的逻辑在日常使用中可能显得很无稽,但这就是逻辑的工作方式。阅读上面链接的维基页面以了解更多信息。
所以,如果你得到true ,作为every 的返回值,你应该注意到这个数组可能是空的。
some 另一方面,在空数组上直接返回 ,不需要调用 ,也没有任何奇怪的地方。false predicate
3.2不存在的元素会被忽略
如果你的数组里有像下面这样的洞,它们会被every/some 忽略:
const myUntiddyArry = [1,,,3,,42];
3.3篡改谓词中的数组
我不会在这里讨论这种情况,因为在大多数情况下,突变原始数组只会使事情复杂化,为bug提供更多空间。
如果你真的需要或有兴趣,请阅读规范中的说明以了解细节。
4给你一个挑战
在JavaScript中用some ,用some ,用every 来表达every 。
我希望你也能感受到我在发现这种关系时得到的巨大快乐和惊奇!
解决方案
让我们一步一步来吧。首先让我们尝试用some 来表达every :
- 对于数组的每个元素,谓词都是真的。
- 对于数组中的某些元素,谓词不为真,这不是真的。
我们可以像下面这样把它翻译成JavaScript:
const myEvery = (arr, predicate) => !arr.some(e => !predicate(e));
现在让我们用every 来表达some 。这和之前几乎一样。只是some 被every 所取代。试着去理解发生了什么:
- 对于数组中的某些元素,该谓词是真的。
- 对于数组中的每个元素,谓词都不是真的。
在JavaScript中:
const mySome = (arr, predicate) => !arr.every(e => !predicate(e));
请注意,当arr 是空的时候,上述的实现也是有效的。而且为了简单起见,我排除了predicate 和thisArg 的其他参数。如果你有兴趣,可以尝试自己添加这些细节。在这个过程中,你可能会学到一件或几件事情!