空arrays在同一时间似乎是正确的和错误的
空数组是真的,但它们也等于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??");
我想这是由于平等运算符操作的隐式转换。
任何人都可以解释幕后发生了什么?
你在这里testing不同的东西。
if (arr)
调用对象(Array是JS中Object的实例)将检查对象是否存在,并返回true / false。
当你调用if (arr == false)
你比较了这个对象的值和原始的false
值。 在内部调用arr.toString()
,它返回一个空string""
。
这是因为在Array上调用的toString
返回Array.join()
,而空string是JavaScript中的一个falsy值。
关于该行:
if (arr == false) console.log("It's false!");
也许这些将有助于:
console.log(0 == false) // true console.log([] == 0) // true console.log([] == "") // true
我相信正在发生的事情是布尔的false
被强制为0
与对象(左侧)进行比较。 该对象被强制为一个string(空string)。 然后,空串被强制为一个数字,也就是零。 所以最后的比较是0
== 0
,这是true
。
编辑:请参阅规格的这一部分,了解如何工作的细节。
这是发生了什么,从规则#1开始:
1.如果types(x)与types(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([])
的结果是空string,所以我们现在有:
"" == 0
规则#1再一次告诉我们跳到步骤#14,但实际应用的下一步是#17:
17.如果Type(x)是String而Type(y)是Number,则返回比较结果ToNumber(x)== y。
ToNumber("")
的结果是0
,这留给我们:
0 == 0
现在,这两个值都是相同的types,所以步骤从#1继续到#7,它说:
7.如果x与y的数值相同,则返回true。
所以,我们回归true
。
简单来说:
ToNumber(ToPrimitive([])) == ToNumber(false)
在if(arr)中,如果arr是一个对象,则总是将其计算为ToBoolean,因为JavaScript中的所有对象都是真实的 。 (null不是一个对象!)
迭代方法评估[] == false
。 首先,如果==
一边是原始图像,另一边是对象,则首先将对象转换为图元,如果两边都不是string
,则将两边转换为数字(如果两边都是string,则使用string比较)。 所以比较是迭代的, [] == false
– > '' == false
– > 0 == 0
– > true
。
为了补充韦恩的答案,并试图解释为什么ToPrimitive([])
返回""
,值得考虑“为什么”问题的两种可能的答案types。 第一种答案是:“因为规范说这是JavaScript的行为。” 在ES5规范中, 第9.1节描述了ToPrimitive的结果作为对象的默认值:
通过调用对象的[[DefaultValue]]内部方法来检索对象的默认值,并传递可选的提示PreferredType。
8.12.8节描述了[[DefaultValue]]
方法。 该方法以“提示”作为参数,提示可以是string或数字。 为了简化问题,如果提示是String,那么[[DefaultValue]]
返回toString()
的值,如果它存在并返回一个原始值,否则返回valueOf()
的值。 如果提示是Number,那么toString()
和valueOf()
的优先级是颠倒过来的,所以valueOf()
首先被调用,如果它是一个基元,它的返回值。 因此, [[DefaultValue]]
是否返回toString()
或者valueOf()
取决于指定的对象的PreferredType以及这些函数是否返回原始值。
默认的valueOf()
Object方法只返回对象本身,这意味着除非一个类重写默认的方法,否则valueOf()
只返回Object本身。 Array
就是这种情况。 [].valueOf()
返回对象[]
本身。 由于Array
对象不是基元,所以[[DefaultValue]]
提示是不相关的:数组的返回值将是toString()
的值。
引用David Flanagan的JavaScript:“权威指南” ,顺便说一下,这本书是一本极好的书,应该是每个人都能得到这些types问题答案的第一位:
此对象到数字转换的详细信息解释了为什么空数组转换为数字0以及为什么具有单个元素的数组也可能转换为数字。 数组inheritance默认的valueOf()方法,该方法返回一个对象而不是原始值,所以数组到数字的转换依赖于toString()方法。 空数组转换为空string。 空string转换为数字0.带有单个元素的数组转换为与该元素相同的string。 如果数组包含单个数字,则该数字将转换为string,然后返回到数字。
“为什么”这个问题的第二种答案,除了“因为规范说”这个问题之外,给出了一些解释,为什么从devise的angular度来看这种行为是有意义的。 在这个问题上我只能推测。 首先,如何将数组转换为数字? 我能想到的唯一明智的可能性是将一个空数组转换为0,将任意非空数组转换为1.但是,正如Wayne的答案所显示的,无论如何,空数组将会转换为0。 除此之外,很难为Array.valueOf()想到一个明智的原始返回值。 所以人们可以争辩说,让Array.valueOf()
成为默认值并返回数组本身就更有意义,从而导致Array.valueOf()
成为ToPrimitive使用的结果。 将数组转换为string而不是数字更有意义。
而且,正如Flanagan所言,这种devise决定确实能够使某些types的有益行为成为可能。 例如:
var a = [17], b = 17, c=1; console.log(a==b); // <= true console.log(a==c); // <= false
这种行为允许您将单个元素数组与数字进行比较,并获得预期的结果。
在尝试使用knockout.js映射插件时,以上都没有帮助我,可能是因为“空数组”不是真的是空的。
我结束了使用: data-bind="if: arr().length"
这个技巧。
这是特定的淘汰赛,而不是OP的问题,但也许它会帮助其他人浏览类似的情况在这里。