Почему 2 == [2] в JavaScript?

Недавно я обнаружил, что 2 == [2] в JavaScript. Как оказалось, эта причуда имеет несколько интересных последствий:

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

Аналогично, следующие работы:

var a = { "abc" : 1 };
a[["abc"]] === a["abc"]; // this is also true

Еще страннее, это тоже работает:

[[[[[[[2]]]]]]] == 2; // this is true too! WTF?

Эти поведения кажутся согласованными во всех браузерах.

Любая идея, почему это языковая функция?

Вот более сумасшедшие последствия этой "функции":

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

var a = [0];
a == a // true
a == !a // also true, WTF?

Эти примеры были найдены jimbojw http://jimbojw.com, а также walkingeyerobot.

Ответы

Ответ 1

Вы можете найти алгоритм сравнения в ECMA-spec (соответствующие разделы ECMA-262, 3-е издание для вашей проблемы: 11.9.3, 9.1, 8.6.2.6).

Если вы переводите задействованные абстрактные алгоритмы обратно в JS, что происходит при оценке 2 == [2], в основном это:

2 === Number([2].valueOf().toString())

где valueOf() для массивов возвращает сам массив, а строковое представление одноэлементного массива представляет собой строковое представление одного элемента.

Это также объясняет третий пример: [[[[[[[2]]]]]]].toString() по-прежнему остается только строкой 2.

Как вы можете видеть, существует довольно много магии за сценой, поэтому я обычно использую только оператор строгого равенства ===.

Первый и второй примеры легче выполнить, поскольку имена свойств всегда являются строками, поэтому

a[[2]]

эквивалентно

a[[2].toString()]

который просто

a["2"]

Имейте в виду, что даже числовые ключи рассматриваются как имена свойств (т.е. строки) до того, как произойдет какая-либо магия.

Ответ 2

Это связано с неявным преобразованием типа оператора ==.

[2] преобразуется в число 2 по сравнению с числом. Попробуйте унарный оператор + на [2].

> +[2]
2

Ответ 3

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

В правой части уравнения мы имеем a [2], который возвращает числовой тип со значением 2. Слева мы сначала создаем новый массив с одним объектом из 2. Затем мы вызываем a [(массив здесь)]. Я не уверен, оценивает ли это строку или число. 2 или "2". Давайте сначала возьмем строковый регистр. Я считаю, что [ "2" ] создаст новую переменную и вернет null. null! == 2. Поэтому давайте предположим, что оно фактически неявно преобразуется в число. a [2] вернет 2. 2 и 2 соответствует типу (так === works) и значение. Я думаю, что это неявно преобразование массива в число, потому что [значение] ожидает строку или число. Похоже, что число имеет более высокий приоритет.

На стороне примечания, мне интересно, кто определяет это приоритет. Потому что [2] имеет номер как первый элемент, поэтому он преобразуется в число? Или это то, что при передаче массива в [массив] он пытается сначала преобразовать массив в число, а затем строку. Кто знает?

var a = { "abc" : 1 };
a[["abc"]] === a["abc"];

В этом примере вы создаете объект с именем a с членом abc. Правая часть уравнения довольно проста; он эквивалентен a.abc. Это возвращает 1. В левой части сначала создается литеральный массив из [ "abc" ]. Затем вы ищите переменную для объекта путем передачи во вновь созданном массиве. Поскольку это ожидает строку, она преобразует массив в строку. Теперь это оценивается как [ "abc" ], что равно 1. 1 и 1 являются одним и тем же типом (именно поэтому === works) и равным значением.

[[[[[[[2]]]]]]] == 2; 

Это просто неявное преобразование. === не будет работать в этой ситуации, потому что существует несоответствие типа.

Ответ 4

Для случая ==, поэтому Дуг Крокфорд рекомендует всегда использовать ===. Он не выполняет никакого неявного преобразования типов.

Для примеров с === преобразование неявного типа выполняется до вызова оператора равенства.

Ответ 5

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

Это интересно, это не то, что [0] является истинным и ложным, на самом деле

[0] == true // false

Это javascript смешной способ обработки if().

Ответ 6

Массив из одного элемента можно рассматривать как сам элемент.

Это происходит из-за утиной печати. Поскольку "2" == 2 == [2] и, возможно, больше.

Ответ 7

Чтобы добавить немного деталей к другим ответам... при сравнении Array с Number, Javascript преобразует Array с parseFloat(array). Вы можете попробовать его самостоятельно в консоли (например, Firebug или Web Inspector), чтобы увидеть, как преобразуются значения Array.

parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN

Для Array s, parseFloat выполняет операцию с первым членом Array и отбрасывает остальные.

Изменить: по деталям Кристофа может быть, что он использует более длинную форму внутри, но результаты последовательно идентичны parseFloat, поэтому вы всегда можете использовать parseFloat(array) как стенографию, чтобы точно знать, как это будет преобразованный.

Ответ 8

Вы сравниваете 2 объекта в каждом случае. Не используйте ==, если вы думаете о сравнении, у вас есть === в виду, а не ==. == часто могут давать безумные эффекты. Ищите хорошие части на языке:)

Ответ 9

Объяснение для раздела EDIT вопроса:

1-й пример

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

Первый тип [0] к первообразному значению в соответствии с вышеприведенным Кристофом отвечает "0" ([0].valueOf().toString())

"0" == false

Теперь, typecast Boolean (false) на Number, а затем String ( "0" ) на Number

Number("0") == Number(false)
or  0 == 0 
so, [0] == false  // true

Как и для оператора if, если в условии if нет явного сравнения, условие оценивает значения правды.

Есть только 6 фальшивых значений: false, null, undefined, 0, NaN и пустая строка "". И все, что не является ложным значением, является правдой.

Поскольку [0] не является фальшивым значением, это истинное значение, оператор if оценивает значение true и выполняет оператор.


Второй пример

var a = [0];
a == a // true
a == !a // also true, WTF?

Снова введите значение для примитива,

    a = a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == "0" // true; same type, same value


a == !a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == !"0"
or  "0" == false
or  Number("0") == Number(false)
or  0 = 0   // true