浅析js中的every()对空数组总返回true

 更新时间:2023年09月19日 09:27:55   作者:王大冶  
JavaScript 语言的核心部分足够大,以至于我们很容易误解其某些部分的工作方式,本文就来和大家一起讨论下为什么JS中的 every()对空数组总返回 true,需要的可以参考下

JavaScript 语言的核心部分足够大,以至于我们很容易误解其某些部分的工作方式。最近在重构一些使用 every() 方法的代码时,发现实际上并不理解其背后的逻辑。在我的理解中,我认为回调函数必须被调用并返回trueevery() 才会返回 true ,但实际上并非如此。对于一个空数组, every() 无论回调函数是什么都会返回 true ,因为那个回调函数从未被调用过。考虑以下情况:

function isNumber(value) {
    return typeof value === "number";
}
[1].every(isNumber);            // true
["1"].every(isNumber);          // false
[1, 2, 3].every(isNumber);      // true
[1, "2", 3].every(isNumber);    // false
[].every(isNumber);             // true

在这个例子的每个情况中,对 every() 的调用都会检查数组中的每个项目是否为数字。前四次调用相当直接, every() 产生了预期的结果。现在考虑以下这些例子:

[].every(() => true);           // true
[].every(() => false);          // true

这可能更令人惊讶:返回 truefalse 的回调函数具有相同的结果。这只能发生的唯一原因是如果回调函数没有被调用,而 every() 的默认值是 true 。但是,为什么在没有值来运行回调函数时,空数组会返回 trueevery() 呢?

要理解为什么,我们需要仔细看看规范是如何描述这个方法的。

实现 every()

ECMA-262 定义了一个 Array.prototype.every() 算法,大致可以翻译成这段JavaScript代码:

Array.prototype.every = function(callbackfn, thisArg) {
    const O = this;
    const len = O.length;
    if (typeof callbackfn !== "function") {
        throw new TypeError("Callback isn't callable");
    }
    let k = 0;
    while (k < len) {
        const Pk = String(k);
        const kPresent = O.hasOwnProperty(Pk);
        if (kPresent) {
            const kValue = O[Pk];
            const testResult = Boolean(callbackfn.call(thisArg, kValue, k, O));
            if (testResult === false) {
                return false;
            }
        }
        k = k + 1;
    }
    return true;
};

从代码中,你可以看到 every() 假设结果是 true ,并且只有在回调函数对数组中的任何一项返回 false 时才返回 false 。如果数组中没有任何项目,那么就没有机会执行回调函数,因此,该方法无法返回 false

现在的问题是:为什么 every() 会表现出这样的行为?

在数学和JavaScript中的“对所有”的量词

MDN页面 提供了为什么 every() 会对空数组返回 true 的答案:

every 的行为就像数学中的“全称量词”。特别是对于空数组,它返回真值。(空集中的所有元素都满足任何给定条件,这是显然的真理。)

空真(Vacuous truth)是一个数学概念,意味着如果给定的条件(称为前件)不能被满足(即给定的条件不为真),那么某件事就是真的。用JavaScript的术语来说,every() 对于一个空集合返回 true,因为没有办法调用回调函数。回调函数代表要测试的条件,如果由于数组中没有值而无法执行它,那么 every() 必须返回 true

“全称量词”("for all" quantifier)是数学中更大主题“全称量化”(universal quantification)的一部分,它允许你对数据集进行推理。考虑到JavaScript数组在进行数学计算方面的重要性,尤其是在使用类型数组(typed arrays)的情况下,内置支持这样的操作是合理的。而**every()**方法并不是唯一的例子。

在数学和JavaScript中的“存在量词”

JavaScript的 some() 方法实现了存在量化(existential quantification)中的“存在量词”(“存在”有时也被称为“存在”或“对某些”)。这个“存在量词”规定,对于任何空集合,结果都是假的。因此,some() 方法对空集合返回 false,并且也不会执行回调函数。以下是一些相关的示例:

function isNumber(value) {
    return typeof value === "number";
}
[1].some(isNumber);            // true
["1"].some(isNumber);          // false
[1, 2, 3].some(isNumber);      // true
[1, "2", 3].some(isNumber);    // true
[].some(isNumber);             // false
[].some(() => true);           // false
[].some(() => false);          // false

其他语言中的量化

JavaScript并不是唯一实现了集合或可迭代对象的量化方法的编程语言:

  • Python: all() 函数实现了“对所有” ,而 any() 函数实现了“存在” 。
  • Rust: Iterator::all() 方法实现了“对所有” ,而 any() 函数实现了“存在” 。

“全称量词”(for all)的 every() 方法的含义与影响

无论你是否认为 every() 方法的行为违反直觉都是可以讨论的。然而,无论你的观点如何,你都需要了解 every() 的“全称量词”(for all)特性以避免错误。简而言之,如果你使用 every() 方法或可能为空的数组,你应该事先进行明确的检查。例如,如果你有一个依赖于数字数组的操作,并且在数组为空时会失败,那么在使用 every() 之前,你应该检查数组是否为空。

function doSomethingWithNumbers(numbers) {
    // first check the length
    if (numbers.length === 0) {
        throw new TypeError("Numbers array is empty; this method requires at least one number.");
    }
    // now check with every()
    if (numbers.every(isNumber)) {
        operationRequiringNonEmptyArray(numbers);
    }
}

再次强调,只有当你有一个数组在为空时不应该被用于操作时,这才重要;否则,你可以避免这个额外的检查。

结论

当我第一次看到 every() 在空数组上的行为时,我感到很惊讶,但一旦你理解了这个操作的更大背景和这个功能在各种语言中的广泛应用,就会觉得它是有道理的。如果你也对这个行为感到困惑,那么我建议你改变阅读 every() 调用的方式。不要把 every() 理解为“这个数组中的每一项是否都符合这个条件?”而应该理解为“这个数组中是否有任何一项不符合这个条件?”这种思维方式的转变可以帮助你避免在未来的JavaScript代码中出现错误。

以上就是浅析js中的every()对空数组总返回true的详细内容,更多关于js every()的资料请关注脚本之家其它相关文章!

相关文章

  • javascript基本常用排序算法解析

    javascript基本常用排序算法解析

    这篇文章主要为大家详细介绍了javascript基本常用排序算法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • Electron实现文件复制到剪切板的方案

    Electron实现文件复制到剪切板的方案

    这篇文章主要介绍了Electron实现文件复制到剪切板的解决方案,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-11-11
  • javascript使用smipleChart实现简单图表

    javascript使用smipleChart实现简单图表

    这篇文章主要介绍了javascript使用smipleChart实现简单图表的方法及示例分享,需要的朋友可以参考下
    2015-01-01
  • Es6 Generator函数详细解析

    Es6 Generator函数详细解析

    Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。这篇文章给大家介绍Es6 Generator函数的相关知识,感兴趣的朋友一起看看吧
    2018-02-02
  • JS中innerHTML和pasteHTML的区别实例分析

    JS中innerHTML和pasteHTML的区别实例分析

    这篇文章主要介绍了JS中innerHTML和pasteHTML的区别,结合实例形式较为详细的分析了innerHTML和pasteHTML的具体功能与使用区别,需要的朋友可以参考下
    2016-06-06
  • 简述冻结JS对象方法技术

    简述冻结JS对象方法技术

    本文主要介绍了冻结JS对象方法技术,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • 学习JavaScript设计模式之策略模式

    学习JavaScript设计模式之策略模式

    这篇文章主要为大家介绍了JavaScript设计模式中的策略模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下
    2016-01-01
  • uniapp小程序底部tabbar图标大小设置办法

    uniapp小程序底部tabbar图标大小设置办法

    这篇文章主要给大家介绍了关于uniapp小程序底部tabbar图标大小设置办法的相关资料,在使用uniapp进行开发时,tabbar是我们使用的很频繁的一个组件,但是在特定的平台会有一些使用上的限制,需要的朋友可以参考下
    2023-08-08
  • js H5 canvas投篮小游戏

    js H5 canvas投篮小游戏

    这篇文章主要为大家详细介绍了JavaScript结合H5 canvas实现投篮小游戏的具体代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • echarts学习之如何给饼图中间添加文字

    echarts学习之如何给饼图中间添加文字

    这篇文章主要介绍了echarts学习之如何给饼图中间添加文字问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03

最新评论