空数组似乎同时等于true和false


201

空数组为true,但也等于false。

var arr = [];
console.log('Array:', arr);
if (arr) console.log("It's true!");
if (arr == false) console.log("It's false!");
if (arr && arr == false) console.log("...what??");

我猜这是由于相等运算符进行隐式转换所致。

谁能解释幕后发生的事情?


1
这里有一个类似的线程应该摆脱对这个问题的一些光:stackoverflow.com/questions/4226101/...
利昂·威廉斯

2
请注意,这arr == true并不
等于

5
哇...就在您以为所有这些都消失了的时候。
哈波

3
为了避免强制使用Javascript类型的WTF,请使用严格相等运算符===。然后,如果您想测试数组的空度,请使用arr === []
DjebbZ

17
如果您要测试数组的空度,请不要使用arr === [],因为它将在右侧实例化一个新数组,而左侧的变量不能引用您刚刚创建的内容,所以它将始终返回false。测试空度应该通过查找来完成arr.length === 0
凯尔·贝克

Answers:


274

您正在这里测试不同的东西。

if (arr) 在对象上调用(数组是JS中Object的实例)将检查对象是否存在,并返回true / false。

当你打电话if (arr == false)你比较这个对象和原始的false价值。在内部arr.toString()被调用,它返回一个空字符串""

这是因为toString在Array上调用会返回Array.join(),并且空字符串是JavaScript中的虚假值之一。


2
你能解释为什么Boolean([])退货true吗?
Devy 2016年

11
按照惯例,在JS中,如果将对象强制转换为布尔值,则始终将它们强制转换为TRUE。请查看以下网址中的“布尔上下文”表:javascript.info/tutorial/object-conversion
Niki

2
@Devy JavaScript中的所有对象都是真实的,因此将任何对象转换为Boolean都是正确的。参见2ality.com/2013/08/objects-truthy.html
汤姆森

62

关于生产线:

if (arr == false) console.log("It's false!");

也许这些会有所帮助:

console.log(0 == false) // true
console.log([] == 0) // true
console.log([] == "") // true

我相信正在发生的是布尔值false被强制0与对象(左侧)进行比较。该对象被强制为字符串(空字符串)。然后,空字符串也被强制转换为数字,即零。因此,最终比较为0== 0,即true

编辑:请参阅规范的这一部分,以获取有关其工作原理的详细信息。

从规则1开始,这是正在发生的事情:

1.如果Type(x)与Type(y)不同,请转到步骤14。

下一条适用的规则是#19:

19.如果Type(y)为布尔型,则返回比较结果x == ToNumber(y)。

的结果ToNumber(false)0,因此我们现在有:

[] == 0

同样,规则1告诉我们跳至步骤14,但实际上适用的下一步是步骤21:

21.如果Type(x)是Object并且Type(y)是String或Number,则返回比较结果ToPrimitive(x)== y。

的结果ToPrimitive([])是空字符串,所以我们现在有:

"" == 0

同样,规则1告诉我们跳至步骤14,但实际上适用的下一步是步骤17:

17.如果Type(x)为String,Type(y)为Number,则返回比较结果ToNumber(x)== y。

的结果ToNumber("")0,这使我们拥有:

0 == 0

现在,两个值具有相同的类型,因此步骤从#1继续到#7,表示:

7.如果x与y相同,则返回true。

因此,我们返回true

简单来说:

ToNumber(ToPrimitive([])) == ToNumber(false)

2
很好的参考!为避免混淆,可能会有所帮助的是,即使规则#1说“转到步骤14”,“下一个适用的规则是#19”的原因是因为步骤14-18与匹配项的类型不匹配。值进行比较。
肖恩·宾恩

2
很好的解释。令我感到困惑的是,空数组被认为是真实的,0是假的,但仍然[] == 0是真实的。根据您对规范的解释,我了解到这种情况的发生,但是从逻辑的角度来看,这似乎是奇怪的语言行为。
bigh_29 '18

7

为了补充韦恩的答案并尝试解释为什么ToPrimitive([])返回"",值得考虑一下“为什么”问题的两种可能的答案。第一种答案是:“因为规范说这就是JavaScript的行为方式。” 在ES5规范的9.1节中,将ToPrimitive的结果描述为Object的默认值:

通过调用对象的[[DefaultValue]]内部方法并传递可选提示PreferredType来检索对象的默认值。

第8.12.8节介绍了该[[DefaultValue]]方法。此方法以“提示”作为参数,提示可以是String或Number。为了简化说明,如果提示为String,则[[DefaultValue]]返回toString()if 的值(如果存在),并返回原始值,否则返回的值valueOf()。如果提示为Number,则toString()和的优先级valueOf()将颠倒,因此valueOf()首先调用它,如果它是原始值,则返回其值。因此,是否[[DefaultValue]]返回结果toString()valueOf()取决于对象的指定PreferredType以及这些函数是否返回原始值。

默认的valueOf()Object方法仅返回对象本身,这意味着除非类重写默认方法,否则valueOf()仅返回Object本身。就是这种情况Array[].valueOf()返回对象[]本身。由于Array对象不是原始对象,因此[[DefaultValue]]提示无关紧要:数组的返回值将是的值toString()

引用David Flanagan的JavaScript:The Definitive Guide,顺便说一句,这是一本绝妙的书,应该成为每个人获得以下类型问题的答案的第一本书:

此对象到数字的转换的详细信息说明了为什么将空数组转换为数字0,以及为什么具有单个元素的数组也可能转换为数字。数组继承了默认的valueOf()方法,该方法返回一个对象而不是原始值,因此数组到数字的转换依赖于toString()方法。空数组转换为空字符串。空字符串将转换为数字0。具有单个元素的数组将转换为与一个元素相同的字符串。如果数组包含单个数字,则该数字将转换为字符串,然后再转换为数字。

对“为什么”问题的第二种答案,除了“因为规范说了”以外,从设计的角度给出了为什么这种行为有意义的解释。关于这个问题,我只能推测。首先,如何将数组转换为数字?我能想到的唯一明智的可能性是将一个空数组转换为0,将任何非空数组转换为1。但是,正如Wayne的答案所揭示的,无论如何,对于许多类型的比较,一个空数组都将转换为0。除此之外,很难想到Array.valueOf()的明智的原始返回值。因此,有人可能会说,Array.valueOf()使用默认值并返回Array本身更有意义,这就是toString()ToPrimitive使用的结果。将数组转换为字符串而不是数字更有意义。

此外,正如弗拉纳根(Flanagan)引言所暗示的那样,此设计决策确实支持某些类型的有益行为。例如:

var a = [17], b = 17, c=1;
console.log(a==b);      // <= true
console.log(a==c);      // <= false

此行为使您可以将单元素数组与数字进行比较,并获得预期的结果。


感谢您提供此答案,该详细说明缺少该问题。
Estus Flask,

3
console.log('-- types: undefined, boolean, number, string, object --');
console.log(typeof undefined);  // undefined
console.log(typeof null);       // object
console.log(typeof NaN);        // number
console.log(typeof false);      // boolean
console.log(typeof 0);          // number
console.log(typeof "");         // string
console.log(typeof []);         // object
console.log(typeof {});         // object

console.log('-- Different values: NotExist, Falsy, NaN, [], {} --');
console.log('-- 1. NotExist values: undefined, null have same value --');
console.log(undefined == null); // true

console.log('-- 2. Falsy values: false, 0, "" have same value --');
console.log(false == 0);        // true
console.log(false == "");       // true
console.log(0 == "");           // true

console.log('-- 3. !NotExist, !Falsy, and !NaN return true --');
console.log(!undefined);        // true
console.log(!null);             // true

console.log(!false);            // true
console.log(!"");               // true
console.log(!0);                // true

console.log(!NaN);              // true

console.log('-- 4. [] is not falsy, but [] == false because [].toString() returns "" --');
console.log(false == []);       // true
console.log([].toString());     // ""

console.log(![]);               // false

console.log('-- 5. {} is not falsy, and {} != false, because {}.toString() returns "[object Object]" --');
console.log(false == {});       // false
console.log({}.toString());     // [object Object]

console.log(!{});               // false

console.log('-- Comparing --');
console.log('-- 1. string will be converted to number or NaN when comparing with a number, and "" will be converted to 0 --');
console.log(12 < "2");          // false
console.log("12" < "2");        // true
console.log("" < 2);            // true

console.log('-- 2. NaN can not be compared with any value, even if NaN itself, always return false --');
console.log(NaN == NaN);        // false

console.log(NaN == null);       // false
console.log(NaN == undefined);  // false
console.log(0 <= NaN);          // false
console.log(0 >= NaN);          // false
console.log(undefined <= NaN);  // false
console.log(undefined >= NaN);  // false
console.log(null <= NaN);       // false
console.log(null >= NaN);       // false

console.log(2 <= "2a");         // false, since "2a" is converted to NaN
console.log(2 >= "2a");         // false, since "2a" is converted to NaN

console.log('-- 3. undefined can only == null and == undefined, and can not do any other comparing even if <= undefined --');
console.log(undefined == null);         // true
console.log(undefined == undefined);    // true

console.log(undefined == "");           // false
console.log(undefined == false);        // false
console.log(undefined <= undefined);    // false
console.log(undefined <= null);         // false
console.log(undefined >= null);         // false
console.log(0 <= undefined);            // false
console.log(0 >= undefined);            // false

console.log('-- 4. null will be converted to "" when <, >, <=, >= comparing --');
console.log(12 <= null);        // false
console.log(12 >= null);        // true
console.log("12" <= null);      // false
console.log("12" >= null);      // true

console.log(0 == null);         // false
console.log("" == null);        // false

console.log('-- 5. object, including {}, [], will be call toString() when comparing --');
console.log(12 < {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log(12 > {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log("[a" < {});         // true, since {}.toString() is "[object Object]"
console.log("[a" > {});         // false, since {}.toString() is "[object Object]"
console.log(12 < []);           // false, since {}.toString() is "", and then converted to 0
console.log(12 > []);           // true, since {}.toString() is "", and then converted to 0
console.log("[a" < []);         // false, since {}.toString() is ""
console.log("[a" > []);         // true, since {}.toString() is ""

console.log('-- 6. According to 4 and 5, we can get below weird result: --');
console.log(null < []);         // false
console.log(null > []);         // false
console.log(null == []);        // false
console.log(null <= []);        // true
console.log(null >= []);        // true

2

在if(arr)中,如果arr是对象,则始终将其(ToBoolean)评估为true,因为JavaScript中的所有对象都是true。(null不是对象!)

[] == false以迭代方式进行评估。首先,如果的一侧==是基本类型,另一侧是对象,则首先将对象转换为基本类型,然后如果两侧都不string是则将两侧都转换为Number(如果两侧都是字符串,则使用字符串比较)。因此,比较的迭代方式是[] == false-> '' == false-> 0 == 0-> true


2

例:

const array = []
const boolValueOfArray = !!array // true

这是因为

ToNumber(ToPrimitive([])) == ToNumber(false)  
  1. []是空Array对象→→ ToPrimitive([])“” ToNumber("")→→0
  2. ToNumber(false) →0
  3. 0 == 0→是

1

具有元素的数组(无论是否为0,false或另一个空数组),始终解析为true使用Abstract Equality Comparison ==

1. [] == false; // true, because an empty array has nothing to be truthy about
2. [2] == false; // false because it has at least 1 item
3. [false] == false; // also false because false is still an item
4. [[]] == false; // false, empty array is still an item

但是,使用严格相等比较===,您试图评估变量的内容以及数据类型,这就是为什么:

1. [] === false; // false, because an array (regardless of empty or not) is not strictly comparable to boolean `false`
2. [] === true; // false, same as above, cannot strictly compare [] to boolean `true`
3. [[]] === false; // true, because see #1

-1

您可以通过将JavaScript数组引用到新数组,使用list = []或删除当前引用的array的元素来清空JavaScript 数组list.length = 0

资料来源:JavaScript空数组


-2

在尝试使用基因敲除插件时,上述方法都没有帮助我,也许是因为“空数组”并不是真正的空。

我最终使用:达到data-bind="if: arr().length"了目的。

这特定于淘汰赛,而不是OP的问题,但这可能会帮助处于类似情况的其他人在这里浏览。


这个答案不相关
fauverism

仅切线,如免责声明:)
domoarigato
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.