Явное безумие Javascript

Возможный дубликат:
Конфликт булевых значений пустого массива JavaScript

В чем обоснование того факта, что

[ ([] == false), ([] ? 1 : 2) ]

возвращает [true, 1]?

Другими словами, пустой список логически истинен в булевом контексте, но равен false.

Я знаю, что использование === решает проблему, но каково объяснение этого, по-видимому, совершенно нелогичного выбора?

Другими словами, это считается ошибкой на языке, что-то непреднамеренное, что только что произошло, и которое невозможно исправить, потому что слишком поздно или действительно в дизайне языка кто-то подумал, что было здорово иметь такое кажущееся безумие, что Я уверен, что это довольно запутанно для многих программистов?

Техническое объяснение того, как это происходит, в то же время удивительно и пугает, меня больше интересовало то, что стоит за этим дизайном.

Изменить

Я принял очень подробное объяснение Ника Реталлака, даже если это касается только технических причин, почему []==false истинно: на удивление достаточно, что это происходит потому, что [], преобразованный в строку, является пустой строкой, а пустым строковым числовым значением является специальным обведенным как 0 вместо более логичного NaN. С пустым объектом, например, сравнение ({}) == false возвращает false, потому что строковое представление пустого объекта не является пустой строкой.

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

Ответ 1

Позвольте получить техническую информацию. Я объясню логику цитатами из ECMAScript Standard 262.

Выражение [] ? 1 : 2 очень просто:

11.12 Условный оператор (?:)

  • Пусть lref является результатом оценки LogicalORExpression.
  • Если ToBoolean (GetValue (lref)) - true, тогда
    • Пусть trueRef будет результатом вычисления первого выражения AssignmentExpression.
    • Возвращает GetValue (trueRef).
  • Else
    • Пусть falseRef является результатом вычисления второго выражения AssignmentExpression.
    • Возврат GetValue (falseRef)

9.2 ToBoolean

  • Undefined: false
  • Null: false
  • Boolean: результат равен входному аргументу (без преобразования).
  • Число: результат равен false, если аргумент равен +0, 0 или NaN; иначе результат будет правдой.
  • String: результат неверен, если аргументом является пустая строка (ее длина равна нулю); в противном случае результат верен.
  • Объект: true

Итак, это правда.


Теперь для дикой езды, что происходит, когда вы используете оператор double equals. Возможно, это поможет объяснить, почему вы никогда не должны этого делать.

Поведение == объясняется в разделе 11.9.3: Алгоритм сравнения абстрактного равенства.

Для x == y, где x = [] и y = false, это происходит:

11.9.3: Алгоритм сравнения абстрактного равенства

Если тип (y) является логическим, верните результат сравнения x == ToNumber (y)

9.3 ToNumber

Результат +0, если аргумент равен ложь.

Теперь мы имеем [] == 0

11.9.3: Алгоритм сравнения абстрактного равенства

Если Type (x) - это Object и Type (y) - либо String или Number, return результат сравнения ToPrimitive (x) == y.

9.1 ToPrimitive

Возвращает значение по умолчанию для объекта. Значение по умолчанию для объекта извлекается путем вызова внутреннего метода [[DefaultValue]]объект, передавая необязательный подсказку PreferredType. Поведение внутренний метод [[DefaultValue]] определяется этой спецификацией для всех собственных объектов ECMAScript в 8.12.8.

8.12.8 DefaultValue:

Когда внутренний метод O [[DefaultValue]] вызван без намек, тогда он ведет себя так, как будто подсказка Число

  • Пусть valueOf является результатом вызова внутреннего метода [[Get]] объекта O с аргументом "valueOf".
  • Если IsCallable (valueOf) истинно, тогда,
    • Пусть val является результатом вызова внутреннего метода [[Call]] значенияOf, с O в качестве этого значения и пустым списком аргументов.
    • Если val является примитивным значением, верните val
  • Пусть toString является результатом вызова внутреннего метода объекта [[Get]] объекта O с аргументом "toString".
  • Если IsCallable (toString) истинно, тогда,
    • Пусть str является результатом вызова внутреннего метода [[Call]] toString, с O как это значение и пустым списком аргументов.
    • Если str является примитивным значением, return str.

Я предполагаю, что это первые попытки valueOf, а затем отклоняет его, потому что результатом является тот же массив, с которого вы начали. Затем он вызывает toString в массиве, который, как представляется, универсально реализуется как список значений, разделенных запятыми. Для пустых массивов, подобных этой, это приводит к пустой строке.

Теперь мы имеем '' == 0

11.9.3: Алгоритм сравнения абстрактного равенства

Если Type (x) - String, а Type (y) - Number, верните результат сравнение ToNumber (x) == y

9.3.1 ToNumber Применяется к типу строки

StringNumericLiteral, пустой или содержит только пробел, является преобразован в +0.

Теперь мы имеем 0 == 0

11.9.3: Алгоритм сравнения абстрактного равенства

Если x - это то же числовое значение, что и y, верните true

Высокий. Это правда. Довольно запутанный способ добраться сюда.

Ответ 2

Путаница здесь связана с определением "фальши" в JavaScript, который (вопреки распространенному мнению) не совпадает с == false.

Falsy фактически ссылается на значение, имеющее логический эквивалент false, а не выражение, результат которого == false. Единственными значениями Falsy в JavaScript являются: false, 0, "", null, undefined и NaN. Таким образом, любое из этих значений - или любое выражение, которое вычисляется для одного из этих значений (например, в статусе if или с использованием тернарного оператора) - является ложным.

Вот таблица, в которой я собрал фальшивые/правдивые значения в JavaScript, которые должны помочь объяснить всю эту проблему. http://jsfiddle.net/philipwalton/QjSYG/