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

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

В чем причина того, что

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

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

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

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

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

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

Редактировать

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

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


person 6502    schedule 11.05.2012    source источник
comment
это смешно! Может проблема с [].toString()?   -  person dmp    schedule 11.05.2012
comment
stackoverflow.com/questions/5491605/   -  person    schedule 11.05.2012
comment
forums.whirlpool.net.au/archive/966449#r15310322   -  person    schedule 11.05.2012
comment
![] == false также возвращает true. :-)   -  person Elliot Bonneville    schedule 11.05.2012
comment
@ElliotBonneville еще одна забавная вещь: ![].toString() == false возвращает false.   -  person    schedule 11.05.2012
comment
@BenniKa, это потому что [].toString() == false это true. Дох. :п   -  person Elliot Bonneville    schedule 11.05.2012
comment
@ElliotBonneville, я знаю, мой комментарий не был серьезным xD. (мой английский звучит ужасно оО)   -  person    schedule 11.05.2012
comment
@BenniKa: Хех, у меня тоже, отсюда и Дох. Я бы никогда так серьезно не сказал. И эй, не беспокойся о своем английском. Я понимаю, о чем вы говорите, так что не беспокойтесь. XD   -  person Elliot Bonneville    schedule 11.05.2012
comment
На любом сложном языке вы должны уметь говорить вещи, которые кажутся нелогичными. Обычно это не проблема, потому что люди автоматически избегают таких ситуаций. Чтобы все было просто, должен быть легко предсказуемый результат, а не интуитивный результат для всех возможных выражений. Поэтому, когда вы создаете язык, вы стараетесь иметь как можно меньше правил, начиная с того, что имеет для вас смысл. Например, при сравнении комбинации строки/логического значения/числа вы принуждаете их к числам. Пустая строка должна равняться false, поэтому и "", и false становятся 0.   -  person Robert    schedule 11.11.2014


Ответы (2)


Давайте разбираться в технике. Я объясню логику цитатами из стандарта ECMAScript. 262.

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

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

  • Пусть lref будет результатом вычисления LogicalOREExpression.
  • If ToBoolean(GetValue(lref)) is true, then
    • Let trueRef be the result of evaluating the first AssignmentExpression.
    • Вернуть GetValue(trueRef).
  • Else
    • Let falseRef be the result of evaluating the second AssignmentExpression.
    • Вернуть GetValue (falseRef)

9.2 ToBoolean

  • Не определено: ложь
  • Нуль: ложь
  • Логический: результат равен входному аргументу (без преобразования).
  • Число: результат ложный, если аргумент равен +0, -0 или NaN; в противном случае результат верен.
  • Строка: результат ложный, если аргументом является пустая строка (ее длина равна нулю); в противном случае результат верен.
  • Объект: правда

Так что это правда.


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

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

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

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

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

9.3 ToNumber

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

Теперь у нас есть [] == 0

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

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

9.1 ToPrimitive

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

8.12.8 Значение по умолчанию:

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

  • Пусть valueOf будет результатом вызова внутреннего метода [[Get]] объекта O с аргументом «valueOf».
  • If IsCallable(valueOf) is true then,
    • Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and an empty argument list.
    • Если val является примитивным значением, вернуть val
  • Пусть toString будет результатом вызова внутреннего метода [[Get]] объекта O с аргументом "toString".
  • If IsCallable(toString) is true then,
    • Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.
    • Если str является примитивным значением, возвратить str.

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

Теперь у нас есть '' == 0

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

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

9.3.1 ToNumber, применяемый к типу String

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

Теперь у нас есть 0 == 0

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

Если x является тем же числовым значением, что и y, вернуть true

Потрясающий. Это верно. Однако довольно запутанный способ добраться сюда.

person Nick Retallack    schedule 11.05.2012

Путаница здесь связана с определением «falsy» в JavaScript, которое (вопреки распространенному мнению) не совпадает с == false.

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

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

person Philip Walton    schedule 11.05.2012
comment
Отличный стол! Однако стоит отметить, что путаница здесь, по-видимому, связана с тем фактом, что [] == false оценивается как false, потому что это то же самое, что и [].toString() == false, поэтому в конечном итоге это '' == false. - person jmar777; 11.05.2012
comment
@ jmar777 ты уверен? Если я запускаю [] === "" в консоли, я получаю false, так как же [] может быть таким же, как [].toString(). Можете ли вы привести что-нибудь, показывающее, что [] == false эквивалентно [].toString == false - person Philip Walton; 11.05.2012
comment
@Philip, разница в === и ==. Когда вы просто используете двойное равенство, допускается приведение типов. Что технически происходит (с ==), когда движок JavaScript говорит, что [] равно ''?. Ответ, очевидно, нет, поэтому он говорит: «Хорошо, позвольте мне попробовать с некоторым приведением типов…». Теперь вопрос в том, что '' равно ''?, что теперь верно. Однако при использовании оператора === этот второй шаг никогда не выполняется, поэтому он оценивается как false. - person jmar777; 11.05.2012
comment
Вы можете прочитать, как это работает, здесь: developer.mozilla.org/en/ JavaScript/Справочник/Операторы/. В частности, ... если один из операндов является строкой, другой операнд преобразуется в строку, если это возможно. - person jmar777; 11.05.2012
comment
@ jmar777, я понимаю разницу между == и ===, я в основном искал доказательства того факта, что интерпретатор JavaScript на самом деле вызывает toString на []. Похоже, вот объяснение того, что происходит под капотом: bclary .com/2004/11/07/#a-11.9.3 - person Philip Walton; 11.05.2012
comment
@PhilipWalton Проверьте мой ответ. Это показывает, где в спецификации это сказано. - person Nick Retallack; 11.05.2012
comment
@PhilipWalton Правильно - если вы не проверили ссылку Mozilla, которую я разместил, там это объясняется. - person jmar777; 11.05.2012
comment
Дох, у меня как-то там психическое расстройство произошло, и я думал, что твое начальное выражение лица было [] == ''. Мой комментарий был бы действителен в этом сценарии, но, учитывая, что это было действительно [] == false, ответ @NickRetallack представляет собой абсолютно выдающуюся разбивку того, что происходит. - person jmar777; 11.05.2012
comment
@NickRetallack да, я сам работал над этой спецификацией, пока вы обновляли свой ответ. Это довольно сложно :) - person Philip Walton; 12.05.2012