在 JS 中短路空数组会产生意外结果:“[] ||真 == []'

Short-circuiting an empty array in JS has an unexpected outcome: `[] || true == []`

本文关键字:结果 短路 JS 数组 意外      更新时间:2023-09-26

在我的代码中,我假设以下短路||是安全的:

var $holidayExpandBarOrOpeningHours = 
                $(".expandBar + .holidayHours_c").prev() || $(".openingHours"); 

但令我惊讶的是,如果我们用 true 语句短路一个空数组,仍然返回一个空数组。我将在下面使用一些控制台代码进行演示,我的问题是为什么[] || true的计算结果为 [] .

false || "expected"
"expected"
false == []
true
[] || "expected"
[]
typeof([])
"object"
({}) || "expected"
Object {}
({}) == false
false
{} == false
SyntaxError: Unexpected token ==

我的一部分认为这是因为数组是一个计算结果为 true 的object,但是如果是这种情况,那么基于人们期望({}) == true [] == true

最后我想指出的是,使用use 'strict'模式时结果是相同的。

转换为布尔值时,[]为真。

> !![]
true
> ![]
false

转换为数字时,[]为 0。这就是为什么将其与 false 进行比较返回 true 的原因:当比较两个不同类型的值时,JavaScript 首先将两者转换为数字,然后比较数字。

> +[]
0
> +false
0
> +[] == +false
true

这是因为||和使用==不同的转换规则。

逻辑或使用ToBoolean而相等使用ToNumber/ToPrimitive


从 11.11 二进制逻辑运算符:

3) 如果 ToBoolean(lval) 为真,则返回 lval。

由于ToBoolean([])为真,因此[] || x会导致[]。这也是为什么 JavaScript 中的 if([]) { /* this runs */ } : 数组是"真实"值。

从 11.9.3 抽象相等比较算法

7) 如果 Type(y) 是布尔值,则返回比较结果 x == ToNumber(y)。

9) [..then] 如果 Type(x) 是 Object 且 Type(y) 是 String 或 Number, 返回比较 ToPrimitive(x) == y 的结果。

5) [..则] 如果 Type(x) 是字符串,Type(y) 是 Number, 返回比较结果 ToNumber(x) == y。

应用于[] == true的逻辑是ToNumber(ToPrimitive([])) == ToNumber(true)


转化价值:

  • ToBoolean([])是真的
  • ToNumber(true)为 1
  • ToPrimitive([])是一个字符串(来自DefaultValue/toString

所以:

ToNumber(ToPrimitive([])) == ToNumber(true)
ToNumber("") == 1
0 == 1
false

(要说约翰·库格尔曼的话,这还有很长的路要走。


此外,({}) == true通常是的,并且遵循与上述相同的转换。

在默认环境下,ToPrimtive({})返回一个非空字符串(即 "[object Object]"根据 Object.prototype.toString)。此字符串将在ToNumber后计算为 NaN,以便NaN == 1 ;或假。

请参阅为什么空数组类型转换为零?+[] 了解有关 ToPrimitive 转换的更多详细信息。

空数组是一个对象;强制转换为布尔值的对象true 。所以

({}) || true; // -> {}
[] || true; // -> []
"" || true; // -> true (empty strings are coerced to false)

旁注 - 需要{}周围的括号以避免将其解析为块。