Почему 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 к NumberJavascript преобразует Array с parseFloat(array), Вы можете попробовать это самостоятельно в консоли (например, Firebug или Web Inspector), чтобы увидеть, что отличается Array значения преобразуются в.

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

За Arrays, 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
Другие вопросы по тегам