空数组似乎同时为真和假

时间:2011-03-30 19:59:31

标签: javascript

空数组是真的,但它们也等于假。

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??");

我想这是由于等于运算符的隐式转换。

任何人都可以解释幕后发生的事情吗?

9 个答案:

答案 0 :(得分:248)

你在这里测试不同的东西。

调用对象的

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

当您致电if (arr == false)时,您会比较此对象的和原始false值。在内部,调用arr.toString(),返回空字符串""

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

答案 1 :(得分:56)

关于这一行:

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

修改:有关具体方法的详细信息,请参阅this section of the spec

从规则#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 :(得分:4)

要补充Wayne's答案并尝试解释ToPrimitive([])返回""的原因,请考虑两种可能的“为什么”问题的答案类型。第一种答案是:“因为规范说这是JavaScript的行为方式。”在ES5规范section 9.1中,它将ToPrimitive的结果描述为Object的默认值:

  

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

Section 8.12.8描述了[[DefaultValue]]方法。此方法将“提示”作为参数,并且提示可以是String或Number。为了通过省略一些细节简化问题,如果提示是String,那么[[DefaultValue]]返回toString()的值(如果存在)并返回原始值,否则返回valueOf()的值。如果提示为Number,则toString()valueOf()的优先级会反转,以便首先调用valueOf(),如果它是基元,则返回其值。因此,[[DefaultValue]]是否返回toString()valueOf()的结果取决于对象的指定PreferredType以及这些函数是否返回原始值。

默认的valueOf() Object方法只返回对象本身,这意味着除非类重写默认方法,valueOf()才会返回Object本身。这是Array的情况。 [].valueOf()返回对象[]本身。由于Array对象不是基元,[[DefaultValue]]提示无关紧要:数组的返回值将是toString()的值。

引用David Flanagan's JavaScript: The Definitive Guide,顺便提一下,这本书应该是每个人首先获得这些类型问题答案的好书:

  

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

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

此外,正如弗拉纳根所引述的那样,这一设计决定确实能够实现某些类型的有益行为。例如:

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

此行为允许您将单个元素数组与数字进行比较并获得预期结果。

答案 3 :(得分:2)

在if(arr)中,如果arr是一个对象,它总是被评估(ToBoolean)为真,因为all objects in JavaScript are truthy。 (null不是对象!)

[] == false以迭代方法进行评估。首先,如果==的一侧是原始的而另一侧是对象,则它首先将对象转换为基元,然后如果两边都不是string则将两边转换为数字(如果使用字符串比较,则使用字符串比较)双方都是字符串)。因此,比较被重复,如[] == false - &gt; '' == false - &gt; 0 == 0 - &gt; true

答案 4 :(得分:1)

示例:

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

这是因为

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

答案 5 :(得分:1)

带有元素的数组(无论是0,false还是另一个空数组),始终使用抽象平等比较true解析为==

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

答案 6 :(得分:0)

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

来源:JavaScript Empty Array

答案 7 :(得分:0)

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

答案 8 :(得分:-1)

当尝试使用knockout.js映射插件时,上述所有内容都没有帮助我,可能因为“空数组”并非真空。

我最终使用了data-bind="if: arr().length"来完成这个技巧。

这是特定的淘汰赛,而不是OP的问题,但也许它会帮助其他人在类似的情况下浏览这里。