You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
导读:本文是 Eslint 的作者 Nicholas C. Zakas 的一篇博文,文章从一个 Case 开始:数组的 every 方法对于空数组会返回 true,首先介绍了 every 在语言规范层面的实现,接着介绍了数学中的全称量词、存在量词和空洞定理,阐释了为什么会 JavaScript 会以这种方式来实现 every,由浅入深,最后提出了对 every 独特的理解,非常具有启发性 ❤️
functiondoSomethingWithNumbers(numbers){// first check the lengthif(numbers.length===0){thrownewTypeError("Numbers array is empty; this method requires at least one number.");}// now check with every()if(numbers.every(isNumber)){operationRequiringNonEmptyArray(numbers);}}
JavaScript 语言的核心部分非常庞大,以至于很容易误解其某些部分的工作原理。最近我在重构一些使用
every()
方法的代码时,发现我实际上并没有完全理解其背后的逻辑。在我的脑海中,我假设回调函数必须被调用并且对于every()
来说必须返回true
,但实际情况并非如此。对于一个空数组,every()
也会返回true
,无论回调函数是什么,因为那个回调函数从未被调用。考虑以下情况:在这个例子的每种情况中,对
every()
的调用都在检查数组中的每个项目是否为数字。前四次调用相当直接,every()
产生了预期的结果。现在考虑这些例子:这可能更令人惊讶:返回
true
或false
的回调函数有相同的结果。这种情况发生的唯一原因是如果回调函数没有被调用,而every()
的默认值是true
。但是,为什么当没有值去运行回调函数时,空数组对于every()
会返回true
呢?要理解这一点,重要的是要看一下规范是如何描述这个方法的。
实现 every()
ECMA-262 规范定义了
Array.prototype.every
算法,其大致可以翻译成以下的 JavaScript 代码:从代码中,你可以看到
every()
假设结果为true
,并且只有当回调函数在数组中的任何项上返回false
时才返回false
。如果数组中没有项,则没有机会执行回调函数,因此,该方法也就没有返回false
的可能。现在的问题是:为什么
every()
会以这种方式实现呢?数学和 JavaScript 中的 “for all” 量词
MDN 页面提供了为什么
every()
对于空数组返回true
的答案:空洞真理是一个数学概念,意味着如果给定条件(称为前提)不能被满足(即,给定条件不为真),那么某事是真的。将这个概念回归到 JavaScript 术语中,
every()
对于空集返回true
是因为没有办法调用回调函数。回调函数代表要测试的条件,如果因为数组中没有值而不能执行它,那么every()
必须返回true
。“for all” 量词是数学中称为普遍量化的更大主题的一部分,它允许你对数据集进行推理。考虑到 JavaScript 数组在执行数学计算中的重要性,特别是与类型化数组一起使用时,内置支持此类操作是有意义的。而
every()
并不是唯一的例子。数学和 JavaScript 中的 “there exists” 量词
JavaScript 的
some()
方法实现了来自存在量化(“there exists” 有时也被称为 “exists” 或 “for some”)的 “exists” 量词。 “exists” 量词声明对于任何空集,结果都是false
。因此,some()
方法对于空集返回false
,它也不会执行回调函数。以下是一些例子(双关语):其他语言中的量化
JavaScript 并不是唯一一个为集合或可迭代对象实现了量化方法的编程语言:
all()
函数实现了 “for all”,而any()
函数实现了 “there exists”。Iterator::all()
方法实现了 “for all”,而any()
函数实现了 “there exists”。因此,JavaScript 与
every()
和some()
一起处于不错的伴随状态。“for all” every() 的含义
你是否认为
every()
函数的行为违反直觉,这是一个值得讨论的问题。然而,不管你的看法如何,你都需要意识到every()
的 “for all” 特性,以避免错误。简而言之,如果你正在使用every()
或可能为空的数组,你应该事先进行明确的检查。例如,如果你有一个依赖数字数组的操作,并且在数组为空时会失败,那么你应该在使用every()
之前检查数组是否为空:同样,这只在一个数组为空,不应该用于操作的情况下才重要;否则,你可以避免这个额外的检查。
结论
虽然我对
every()
在空数组上的行为感到惊讶,但一旦你理解了操作的更广泛背景以及这一功能在各种语言中的普及,它就说得通了。如果你也对这种行为感到困惑,那么我建议你改变阅读every()
调用的方式。不要将every()
理解为“这个数组中的每个项目都符合这个条件吗?”而应该理解为,“这个数组中有没有不符合这个条件的项目?”这种思维的转变可以帮助你在未来避免在 JavaScript 代码中出错。写在最后
本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!
如果有疑问或者发现错误,可以在评论区进行提问和勘误,
如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: