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

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

163 голосов
/ 12 ноября 2009

Я недавно обнаружил, что 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 fame, а также walkeyerobot .

Ответы [ 9 ]

133 голосов
/ 12 ноября 2009

Вы можете найти алгоритм сравнения в спецификации 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"]

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

10 голосов
/ 12 ноября 2009
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; 

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

10 голосов
/ 12 ноября 2009

Это из-за неявного преобразования типа оператора ==.

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

> +[2]
2
8 голосов
/ 12 ноября 2009

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

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

7 голосов
/ 25 ноября 2009
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

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

[0] == true // false

Это забавный способ обработки оператора if () в javascript.

6 голосов
/ 12 ноября 2009

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

Это из-за утки. Так как "2" == 2 == [2] и, возможно, больше.

3 голосов
/ 12 ноября 2009

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

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

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

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

2 голосов
/ 23 августа 2011

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

0 голосов
/ 25 июля 2016

Объяснение для РЕДАКТИРОВАТЬ раздела вопроса:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...