为什么 [1,2,3] 在 Javascript 中不等于自身?

IT技术 javascript
2021-01-14 18:02:38

我今天在 Javascript 中使用数组并注意到这个小宝石:

alert([1, 2, 3] == [1, 2, 3]); //alerts false

令我感到奇怪的是,数组不等于自身。

但后来我注意到了这一点,这更奇怪:

alert([1, 2, 3] == "1,2,3");  //alerts true

?!?!?!?!!!?

为什么在世界上[1, 2, 3]不是==对自己而是==对字符串?

我认识到,==不一样===即便如此,是什么邪恶导致Javascript先生做出如此奇怪的事情?

6个回答

好的,首先您需要了解 javascript 如何处理程序中的值。您创建的所有变量都将只是存储该对象的内存位置的引用因此,当您这样做时:

alert( [1,2,3] == [1,2,3] );

...它做了三件事:

  1. 将数组 ([1,2,3]) 放到堆上
  2. 将另一个数组 ([1,2,3]) 放到堆上(注意它会有不同的内存位置)
  3. 比较两个参考。它们指向内存中不同位置的不同对象,因此被认为是不相等的。

您可以通过运行以下代码来检查一些正常的行为:

var a = [1,2,3];
var b = a;
alert (a == b)   // Result is true. Both point to the same object.

现在关于字符串的问题

当您使用==运算符尝试将两个操作数转换为相同类型时(邪恶行为......我知道......)

执行此操作时,它决定在进行比较之前将两者都转换为字符串(因此结果为 real "1,2,3" === "1,2,3",其计算结果为真。

我不能给你一个完整的画面,因为很少有人了解 JavaScript 疯狂的每一个细微差别,但希望这能消除一些迷雾。

@nnnnnn,等等,复制了错误的例子(深夜)。打破传递性的例子是这样的:'0'==0是真的,'' == 0是真的,但是'0'==''是假的。惊人的反直觉。
2021-03-14 18:02:38
==没有邪恶的行为,它具有明确定义的行为,在这种情况下,这种行为不如您从中获得的帮助===或者它可能更有帮助:这实际上取决于您要尝试做什么。
2021-03-25 18:02:38
@Dave,当然,从编写解释器的角度来看,这可能是有道理的,但是检查两个数组是否存储在内存中的同一位置真的那么有用吗?换句话说,与比较数组中的元素相比,您多久需要一次当前行为?JavaScript 认为前者更有可能。Python 认为后者更有可能。我同意 Python。
2021-04-03 18:02:38
==但是,关于数组的行为并不是那么违反直觉,是吗?它们是不同的数组,碰巧包含相同的元素。
2021-04-10 18:02:38
@nnnnnn,它有违反直觉的行为,因此它是邪恶的。上面的例子揭示了 的一些邪恶本质==,但不是全部。例如,false == undefined是假的,false==null是假的,但是null==undefined是真的。它定义明确,但完全违反直觉(如果传递性是“正确”的基础,那么结果完全是错误的。)
2021-04-12 18:02:38

== 操作员

[..] 如果任一操作数是字符串,则如果可能,另一个操作数将转换为字符串。[..] 如果两个操作数都是对象,那么当操作数引用内存中的同一对象时,JavaScript 会比较相等的内部引用。

https://developer.mozilla.org/en/JavaScript/Reference/Operators/Comparison_Operators

也就是说,[1, 2, 3]转换为字符串equals "1,2,3"但是,一个数组对象不等于另一个数组对象。

对于第一部分,您将创建两个不同的对象,因为数组只是对象,并且由于您创建了其中的两个,因此它们都是唯一的。

因为==只有必须获得相同类型时才会强制执行(例如,仅当操作数的类型不同时)。做的时候

alert([1, 2, 3] == [1, 2, 3]); //alerts false

...不需要强制;两者都是对象。它们不是同一个对象,所以它是false. 人们认为的==是“胁迫”平等操作符,它是,但关键的是,它只是强制转换,如果它有

但是做

alert([1, 2, 3] == "1,2,3");  //alerts true

...涉及不同类型的操作数:字符串和对象。这样强制就完成了。在这种情况下,对象被强制为字符串,就像 with 一样String(obj),这会调用其默认toString行为,对于数组来说是.join()join默认为","作为分隔符,因此结果字符串匹配"1,2,3". (您可以在规范中找到对象被强制转换为字符串的完整逻辑。)

第一次比较失败,因为两个对象的基本比较将检查它们是否实际上是同一个引用对象,而不是两个对象是否具有相同的值。如果您想比较两个数组,则必须遍历这些值。

function arrayCompare(arr1, arr2) {
  if (arr1.length !== arr2.length) return false;
  for (var i = 0, len = arr1.length; i < len; i++) {
    if (arr1[i] !== arr2[i]) return false;
  }
  return true;
}

请记住,这不是递归比较,因此它仅适用于原始值数组。

第二个比较有效,因为 == 将尝试强制参数的类型,当您将数组转换为字符串时,这就是结果。

[1,2,3].toString() === '1,2,3'