【问题标题】:Using operators as variables gives odd results使用运算符作为变量会产生奇怪的结果
【发布时间】:2018-07-05 16:51:58
【问题描述】:
! + [] === true

有人能解释一下这里发生了什么吗?我知道这是不正常的,但是当在数学运算中使用运算符代替变量时,我很难理解编译器在做什么。

更多这种奇怪的例子:

(! + '') === (!'')

但是

(! + []) !== (![])

【问题讨论】:

  • 这样阅读:!(+[])。我建议你把所有的例子都添加到astexplorer.net,然后看看哪些操作符是按什么顺序使用的。
  • 这里发生的不是运算符被用作变量。像[] 和{} 这样的东西在任何表达式上下文中都是值,就像3 和true 一样。 ===、! 等运算符被用作运算符。
  • @ASDFGerte 的评论对我来说很有意义。我应该把它读成 (+[]) ,它强制一个对象 -> 字符串 -> 数字。

标签: javascript


【解决方案1】:

JavaScript 总是将输入转换为原始类型。因此,在您的! + [] 的情况下,它将尝试将+[] 转换为字符串。这将是"",然后由于!,它将转换为boolean。在 JavaScript 中,"" 被视为false。所以最终它将返回!false,即true。

编辑 1

正如@mhodges 在下面评论的那样。 [] 转换为字符串为"" 然后+ 将其转换为number,因此它将变为0。

您可以从此处了解有关从对象到基元的更多信息。 object to primitive

您可以在下面检查行为。

var emptyBrackets = [].toString();
// conversion to string.
console.log(emptyBrackets === '');
// conversion to number.
console.log(+emptyBrackets);
// conversion to boolean.
console.log(!0);

【讨论】:

  • 这里+不是用来转数字的吗? +"" === 0,然后是!0 === true
  • @Keatinge 我认为您部分正确。我认为 Karan 是对的,因为 [] 被转换为 "",然后被 + 强制转换为数字。
  • 这个答案的 非常 重要的缺失部分是 + 被用来将 "" 强制转换为数字 (0)。说“它将尝试将+[] 转换为字符串”是完全错误的。
  • @mhodges 这里讨论的所有强制转换都是因为+ 而发生的,但是是的,在内部,它将被强制转换为字符串,然后再被强制转换为数字。文本暗示+[] === "",但这是完全错误的(除了其他几个格式不那么好的部分,请参阅“JavaScript 总是将输入转换为原始类型” - 什么?)。我不明白为什么这甚至会得到支持。
  • @Karan Close,仍然不完全准确。 JavaScript 中没有 int 这样的东西——它是 Number 类型。强制数组(通过+)将在内部调用空数组上的.toPrimitive(),将其转换为default value(一个空字符串),然后用+将其强制为数字。这是一个语义问题,但是对于这样的问题,语义非常重要
【解决方案2】:

首先,重要的是要了解正在使用的运算符以及使用顺序。如果不清楚,一个简单的方法是查看语法解析器从中得到了什么,例如使用https://astexplorer.net/,第一个例子表明:

! + [] === true 使用一元逻辑非 (!)、一元加 (+)、空数组字面量 ([])、严格相等 (===) 和 true 关键字 (@ 987654331@).

执行顺序是(!(+[])) === true,或者看成一棵树:

   ===
 /     \
!     true
|
+
|
[]

接下来是要了解每个运算符将其操作数转换为什么。这有点乏味,当您想详细了解时。

我假设你知道空数组字面量或true 会产生什么。严格相等 === 也可能是众所周知的,因为它没有很多不直观的方面(与松散相等 == 相反,它有很多)。因此,相关只是一元 + 和一元逻辑非的步骤。

给定操作顺序,我们先看unary +,以[]为操作数。它在操作数上执行ToNumber,对于像我们的示例[] 这样的对象,它执行ToPrimitive,然后是另一个ToNumber。 ToPrimitive 将尝试 valueOf(它不会返回原语,因此不会被采用),然后是 toString。对于数组,后者搜索函数join 并在不带参数的情况下调用它(将数组设置为this)。对于一个空数组,这会产生空字符串"",它会被ToNumber 的第二次调用转换为0。

接下来是unary logical not,它首先用ToBoolean 转换任何操作数。这个中间步骤为我们的操作数0 生成false。然后,正如预期的那样,将返回相反的结果 (true)。

最后但最不重要的是,正如我们所知,true === true 的计算结果为 true。

这里一个有趣的旁注是在默认情况下使用Array.prototype.join。我希望很多人会感到惊讶,为什么更改 join 会影响结果:

console.log(! + [] === true);

let x = [];
x.join = () => "1";
console.log(! + x === true);

Array.prototype.join = () => "1";
console.log(! + [] === true);

其他示例的工作方式类似:

  • (! + '')几乎一样,只是ToNumber甚至不需要前面的ToPrimitive

  • 对于(!''),空字符串的ToBoolean是false,因此导致true

  • 对于(![]),任何对象的ToBoolean都是true,导致false

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-04
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 2017-03-16
    • 2019-04-13
    • 1970-01-01
    相关资源
    最近更新 更多