Почему 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