Почему 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/ а также walkeyerobot.
9 ответов
Вы можете найти алгоритм сравнения в спецификации ECMA (соответствующие разделы 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"]
Имейте в виду, что даже числовые ключи обрабатываются как имена свойств (то есть строки), прежде чем произойдет какое-либо волшебство массива.
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true
В правой части уравнения мы имеем a[2], который возвращает числовой тип со значением 2. Слева мы сначала создаем новый массив с единственным объектом 2. Затем мы вызываем [(массив находится здесь)]. Я не уверен, оценивает ли это строку или число. 2 или "2". Давайте сначала возьмем строковый регистр. Я считаю, что ["2"] создаст новую переменную и вернет ноль. null!== 2. Итак, давайте предположим, что он фактически неявно преобразуется в число. [2] вернет 2. 2 и 2 совпадут по типу (так === работает) и значению. Я думаю, что это неявное преобразование массива в число, потому что [значение] ожидает строку или число. Похоже, число имеет более высокий приоритет.
Кстати, мне интересно, кто определяет этот приоритет? Это потому, что [2] имеет число в качестве первого элемента, поэтому он преобразуется в число? Или же при передаче массива в [массив] он пытается сначала превратить массив в число, а затем в строку. Кто знает?
var a = { "abc" : 1 };
a[["abc"]] === a["abc"];
В этом примере вы создаете объект с именем a и членом с именем abc. Правая часть уравнения довольно проста; это эквивалентно a.abc. Это возвращает 1. Левая сторона сначала создает буквенный массив ["abc"]. Затем вы ищете переменную в объекте, передавая только что созданный массив. Поскольку это ожидает строку, он преобразует массив в строку. Теперь это оценивается как ["abc"], что равно 1. 1 и 1 имеют одинаковый тип (поэтому === работает) и равное значение.
[[[[[[[2]]]]]]] == 2;
Это просто неявное преобразование. === не будет работать в этой ситуации, потому что есть несоответствие типов.
Это из-за неявного преобразования типа ==
оператор.
[2] преобразуется в число 2 при сравнении с числом. Попробуй одинарный +
оператор на [2].
> +[2]
2
Для ==
случай, именно поэтому Дуг Крокфорд рекомендует всегда использовать ===
, Это не делает никакого неявного преобразования типов.
Для примеров с ===
неявное преобразование типов выполняется до вызова оператора равенства.
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
Это интересно, это не так, что [0] является истинным и ложным, на самом деле
[0] == true // false
Это забавный способ обработки оператора if () в javascript.
Массив из одного элемента может рассматриваться как сам элемент.
Это из-за утки. Так как "2" == 2 == [2] и возможно больше.
Чтобы добавить немного деталей к другим ответам... при сравнении 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)
как стенограмма, чтобы знать наверняка, как он будет преобразован.
В каждом случае вы сравниваете 2 объекта. Не используйте ==, если вы думаете о сравнении, вы имеете в виду ===, а не ==. == часто может дать безумные эффекты. Ищите хорошие части на языке:)
Пояснение к разделу РЕДАКТИРОВАТЬ вопроса:
1-й пример
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
Первое приведение типа [0] к примитивному значению в соответствии с ответом Кристофа выше, мы имеем "0" ([0].valueOf().toString()
)
"0" == false
Теперь введите тип 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 и выполняет оператор.
2-й пример
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