大家好,这里是大家的林语冰。
免责声明
本文属于是语冰的直男翻译了属于是,仅供粉丝参考,英文原味版请临幸 JavaScript WTF: Why does every() return true for empty arrays?。
作者:Nicholas C. Zakas(《JS 高级程序设计》兼 ESLint 作者)
当没有任何值可以测试时,如何满足条件?
JS(JavaScript)语言的核心太大了,所以很容易错误解读它的某些工作机制。我最近重构了某些使用了 every()
方法的代码,结果发现我其实并不理解该方法背后的逻辑。在我看来,我假设回调函数必然会被调用并返回 true
,然后才导致 every()
返回 true
,但实际情况并非如此。对于空数组,every()
会返回 true
,不管回调函数是什么,因为该回调函数永远不会被调用。考虑以下情况:
在此例子的每一种 case 中,every()
的调用都会检查数组中的每一项是否为一个数字。前四个调用一目了然,every()
产生了预期的结果。现在考虑这些例子:
这可能更加反直觉:返回 true
或 false
的回调产生相同的结果。发生这种情况的真相只有一个,就是回调没有被调用,并且 every()
的默认值是 true
。虽然但是,当没有值可以运行回调函数时,为什么空数组会让 every()
返回 true
?
要理解其中缘由,瞄一眼规范如何描述这个方法乃重中之重。
实现 every()
ECMA-262 定义了一个 Array.prototype.every()
算法,它大致可以转换为下述 JS 代码:
如你所见,every()
假设结果是 true
,并且当且仅当数组中的任意元素调用回调函数都返回 false
时,它才返回 false
。如果数组没有元素,那么就没有机会执行回调函数,因此,该方法无法返回 false
。
现在的问题是:为何如此设计 every()
的行为?
数学和 JS 中的全称量词("for all" quantifier)
关于空数组调用 every()
返回 true
的原因,MDN 网页 提供了答案:
every
的作用就像数学中的"for all"量词。特别是,对于空数组,它返回true
。(地球人都知道,空集的所有元素都满足任何给定的条件。)
空真(Vacuous truth) 是一个数学概念,它意味着若给定条件(称为前提)不能满足(比如给定条件不为真),则某事为真。用 JS 的术语来说,every()
返回 true
,是因为它无法调用回调。回调表示要测试的条件,如果因为数组中没有值而无法执行,那么 every()
必须返回 true
。
全称量词 是数学中一个更大主题的一部分,它允许您对数据集进行推理。考虑到 JS 数组对于执行数学计算的重要性,尤其是类型化数组,为此类操作提供内置支持是有意义的。every()
不是孤例。
数学和 JS 中的存在量词("there exists"量词)
JS 的 some()
方法在 特称量词(存在量词有时也被称为特称量词)中实现了 存在量词。存在量词表示对于任何空集而言,结果都为 false
。因此,对于一个空集,some()
方法返回 false
,并且它也不执行回调。以下是一些例子(一语双关):
其他语言中的量词
JS 并不是唯一一种为集合或可迭代对象实现量词方法的编程语言:
因此,JS 与 every()
和 some()
一拍即合。
every()
的全称量词含义
您是否认为 every()
的行为是反直觉的,这还有待商榷。虽然但是,抛开您的个人意见不谈,您需要意识到 every()
的全称量词本质,避免犯错。简而言之,如果您在使用 every()
或一个可能为空的数组,您应该事先添加一个显式的检查。举个栗子,如果您有一个依赖于数字数组的操作,并且会因为一个空数组而失败,那么您应该在使用 every()
之前检查数组是否为空:
同理可得,当且仅当您有不该被用于某操作的空数组时,这才显得重要;否则,您可以避免这种额外的检查。
结论
虽然我被空数组上 every()
的行为惊到,但一旦您理解了该操作更广义的上下文以及此功能的跨语言推广,那么您就会明白这别有深意。如果您也对这种行为感到困惑,那么我建议您在邂逅 every()
调用时改变解读它们的方式。将 every
解读为"此数组中是否存在某元素与此条件不匹配?",而不是解读为"此数组中的任一元素是否都匹配此条件?"。这种思维上的转变可以帮助您避免 JS 代码中的错误。
学废了的小伙伴可以点赞给语冰打 call,欢迎关注最新动态和订阅前沿资讯。谢谢大家的彼芯,掰掰~