【译】为什么 JS 空数组调用 every() 方法祂喵地会返回 true?

大家好,这里是大家的林语冰。

免责声明

本文属于是语冰的直男翻译了属于是,仅供粉丝参考,英文原味版请临幸 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() 产生了预期的结果。现在考虑这些例子:

这可能更加反直觉:返回 truefalse 的回调产生相同的结果。发生这种情况的真相只有一个,就是回调没有被调用,并且 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,欢迎关注最新动态和订阅前沿资讯。谢谢大家的彼芯,掰掰~

相关推荐
Vennn4 分钟前
Android自动化:使用 Web 方式实现某音未读消息检查与采集
前端·javascript·vue.js
Smilezyl6 分钟前
为了搞懂 AI Agent,我用 6000 行 JS 代码手搓了一个零依赖的 Coding Agent
前端·javascript·github
掰头战士8 分钟前
搞定JavaScript类型判断,一文就够了
javascript
光影少年11 分钟前
前端如何和蓝牙物联网进行通信和兼容性问题
前端·物联网·掘金·金石计划
周凡1231 小时前
AI 时代的 Web JavaScript 逆向分析实践与思考
前端·javascript·人工智能
zhoumeina991 小时前
分段创建产品,tab 页切换又要保留缓存
前端·javascript
The Sheep 20231 小时前
EFcore 查询数据
java·javascript
怕浪猫1 小时前
Electron 开发实战(七):网络通信与 API 集成全解
前端·javascript·electron
w_t_y_y2 小时前
vue父子组件通信(一)父子调用和通信(2)VUE3
前端·javascript·vue.js