JavaScript空数组的every()方法实践

 更新时间:2024年03月11日 09:03:04   作者:Apifox.  
every()方法用于检测数组中的所有元素是否都满足指定条件, 本文主要介绍了JavaScript空数组的every()方法实践,具有一定的参考价值,感兴趣的可以了解一下

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

这可能更令人惊讶:无论是返回 true 还是 false 的回调,结果都是一样的。唯一的原因是如果回调没有被调用,every() 的默认值是 true。但是,为什么一个空数组会对 every() 返回 true,当没有值去执行回调函数时呢?

要理解原因,重要的是要看看规范是如何描述这个方法的。

实现 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 表现得像数学中的“全称量词”。特别是对于一个空数组,它返回 true。(空集合中的所有元素默认满足任何给定条件是一种空洞真理。)

空洞真理 是指如果给定条件(称为前提)不能满足(即,给定条件不是真的),那么某事是真的。把它转回 JavaScript 方面,every() 对一个空集返回 true 是因为没有办法调用回调。回调代表要测试的条件,如果因为数组中没有值而无法执行它,则 every() 必须返回 true

“全称量词”是数学中一个更大主题的一部分,称为普遍量化,它允许你对数据集合进行推理。鉴于 JavaScript 数组在执行数学计算中的重要性,尤其是与类型化数组一起使用,自然会支持这种操作。而且 every() 不是唯一的例子。

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

JavaScript 的 some() 方法实现了存在量化(“存在”有时也称为“存在”或“对于某些”)中的“存在”量词。"存在" 量词声明,对于任何空集合,结果是假。因此,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() 方法实现了“存在”。

因此,JavaScript 凭借 every() 和 some() 与众不同。

“全称” every() 的含义

不管你是否认为 every() 的行为违反直觉,这都是值得讨论的。然而,不管你的观点如何,你都需要意识到 every() 的“全称”本质,以避免错误。简而言之,如果你使用 every() 或可能为空的数组时,你应该事先进行明确的检查。例如,如果你有一个依赖数字数组的操作,而空数组会导致操作失败,那么你应该在使用 every() 之前检查数组是否为空:

function doSomethingWithNumbers(numbers) {

    // 首先检查长度
    if (numbers.length === 0) {
        throw new TypeError("Numbers array is empty; this method requires at least one number.");
    }

    // 现在用 every() 检查
    if (numbers.every(isNumber)) {
        operationRequiringNonEmptyArray(numbers);
    }

}

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

结论

虽然我对 every() 对一个空数组的行为感到惊讶,但一旦你理解了这个操作的更广泛上下文以及这个功能在不同语言中的普及,这就讲得通了。如果你对这个行为也感到困惑,那么我建议你在遇到 every() 调用时改变你的阅读方式。不要把 every() 看作是“这个数组的每一项是否满足这个条件?”而是看作是,“数组中是否有任何一项不满足这个条件?”这种思维的转变可以帮助你避免未来在你的 JavaScript 代码中出现错误。

原文作者:Nicholas C. Zakas (https://humanwhocodes.com/blog/2023/09/javascript-wtf-why-does-every-return-true-for-empty-array/)

到此这篇关于JavaScript空数组的every()方法实践的文章就介绍到这了,更多相关JavaScript空数组every()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JS实现星星海特效

    JS实现星星海特效

    这篇文章主要为大家详细介绍了JS实现星星海特效特效,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • JavaScript设计模式之原型模式(Object.create与prototype)介绍

    JavaScript设计模式之原型模式(Object.create与prototype)介绍

    这篇文章主要介绍了JavaScript设计模式之原型模式(Object.create与prototype)介绍,原型模式指使用原型实例来拷贝、创建新的可定制的对象,新建的对象,不需要知道原对象创建的具体过程,需要的朋友可以参考下
    2014-12-12
  • uniapp表单校验超详细讲解

    uniapp表单校验超详细讲解

    这篇文章主要给大家介绍了关于uniapp表单校验的相关资料,Uni-app内置了一些表单验证方法,可以帮助我们对表单进行有效的验证,需要的朋友可以参考下
    2023-10-10
  • Bootstrap Scrollspy源码学习

    Bootstrap Scrollspy源码学习

    这篇文章主要介绍了Bootstrap Scrollspy源码学习,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03
  • 浅谈在js传递参数中含加号(+)的处理方式

    浅谈在js传递参数中含加号(+)的处理方式

    下面小编就为大家带来一篇浅谈在js传递参数中含加号(+)的处理方式。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-10-10
  • 兼容IE和FF的图片上传前预览js代码

    兼容IE和FF的图片上传前预览js代码

    上传前预览使用js实现还是比较cool的,同时还可以兼容IE和FF的能做到这一点已经相当不容易了,下面与大家一起分享下具体的实现,感兴趣的你可不要错过了哈
    2013-05-05
  • JavaScript之Array常见的方法详解

    JavaScript之Array常见的方法详解

    这篇文章主要为大家介绍了JavaScript之Array常见的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助V
    2021-11-11
  • 微信小程序中多个页面传参通信的学习与实践

    微信小程序中多个页面传参通信的学习与实践

    刚接触微信小程序,对里面的语法和属性还不怎么了解,最近正在努力学习中,下面这篇文章主要给大家介绍了微信小程序中多个页面传参通信的相关资料,是最近学习的一个内容总结,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-05-05
  • 关于Javascript作用域链的八点总结

    关于Javascript作用域链的八点总结

    其实吧,关于作用域链相关的文章我也看了不少,但是我一直也没能做一个详细的总结,今天把我看到的一些东西,结合自己的想法,总结成以下8个点
    2013-12-12
  • 浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题

    浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题

    下面小编就为大家带来一篇浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09

最新评论