JavaScript:为什么every() 对空数组返回 true?

JavaScript 语言的核心足够大,以至于很容易误解它的某些部分是如何工作的,对于空数组,无论回调函数是什么,都返回,因为从不调用该回调函数。请考虑以下几点:

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(() => true);           // true
[].every(() => false);          // true

这可能更令人惊讶:返回true或false的回调具有相同的结果。发生这种情况的唯一原因是回调没有被调用,并且every()的默认值为true。但是,当没有值可以运行回调函数时,为什么空数组对every()返回true ?

要了解原因,请务必查看规范如何描述此方法。

Implementing 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()假设结果为真,并且只有当回调函数对数组中的任何项返回假时才返回假。如果数组中没有任何项,那么就没有机会执行回调函数,因此,该方法就不可能返回false。

数学和JavaScript中的“for all”量词 

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

every就像数学中的“for all”量词。特别是,对于空数组,它返回true。(空集合的所有元素都满足任何给定条件,这是完全正确的。) 

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

“for all”量词是数学中一个更大的主题“通用量化”的一部分,它允许你对数据集进行推理。考虑到JavaScript数组对于执行数学计算的重要性,特别是对于类型化数组,为此类操作提供内置支持是有意义的。every()并不是唯一的例子。

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

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

 其他语言的量化

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

  • Python: all()函数实现了" for all " 而any()函数实现了" there exists " 。 
  •  Iterator: all()方法实现了“for all”,而any()函数实现了“there exists”。

所以JavaScript与every()和some()都是很好的伙伴。 

“for all” 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()读成“这个数组中的每个项目都匹配这个条件吗?”读取它为,“这个数组中是否有不符合此条件的项?”这种思维的转变可以帮助您避免JavaScript代码中的错误。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.怪兽

希望大家能够多多支持,我会继续

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值