Почему isNaN (null) == false в JS?

130

Этот код в JS дает мне всплывающее окно с сообщением «Я думаю, что null - это число», что меня немного беспокоит. Что мне не хватает?

if (isNaN(null)) {
  alert("null is not a number");
} else {
  alert("i think null is a number");
}

Я использую Firefox 3. Это ошибка браузера?

Другие тесты:

console.log(null == NaN);   // false
console.log(isNaN("text")); // true
console.log(NaN == "text"); // false

Значит, проблема не в точном сравнении с NaN?

Изменить: теперь на вопрос дан ответ, я очистил свой пост, чтобы получить лучшую версию для архива. Однако это делает некоторые комментарии и даже ответы немного непонятными. Не вините их авторов. Среди вещей, которые я изменил, было:

  • Удалено примечание о том, что я изначально напортачил с заголовком, вернув его значение.
  • Более ранние ответы показали, что я недостаточно четко указал, почему считаю такое поведение странным, поэтому я добавил примеры, которые проверяют строку и проводят сравнение вручную.
Ханно Фиц
источник
3
разве вы не имеете в виду "Почему isNaN (null) == false"?
Мэтт Рогиш,
Исходя из вашего кода, isnan (null) возвращает false (null - это не «не число»), если он говорит: «Я думаю, что null - это число».
devinmoore

Ответы:

114

Я полагаю, что код пытается спросить, " xчисловой?" с конкретным случаем здесь x = null. Для isNaN()ответа на этот вопрос можно использовать функцию , но семантически она относится именно к значению NaN. Из Википедии по запросу NaN:

NaN ( N ВЗ с N умбры) представляет собой значение типа цифровых данных , представляющий неопределенное или непредставимо значение, особенно в вычислениях с плавающей запятой.

В большинстве случаев мы думаем, что ответ на вопрос «является нулевым числом?» должно быть нет. Однако isNaN(null) == falseсемантически правильно, потому что nullэто не так NaN.

Вот алгоритмическое объяснение:

Функция isNaN(x)пытается преобразовать переданный параметр в число 1 (эквивалент Number(x)), а затем проверяет, равно ли значение NaN. Если параметр не может быть преобразован в число, Number(x)вернется NaN2 . Следовательно, если преобразование параметра xв число дает результат NaN, возвращается значение true; в противном случае возвращается false.

Таким образом , в данном конкретном случае x = null, nullпреобразуется в число 0, (попробуйте оценить Number(null)и увидеть , что она возвращает 0,) и isNaN(0)возвращает ложь. Строка, состоящая только из цифр, может быть преобразована в число, и isNaN также возвращает false. Строка (например 'abcd'), которая не может быть преобразована в число, приведет isNaN('abcd')к возврату истины, особенно потому, что Number('abcd')возвращает NaN.

В дополнение к этим очевидным граничным случаям есть стандартные числовые причины для возврата NaN, например 0/0.

Что касается кажущихся непоследовательными тестов на равенство, показанных в вопросе, поведение NaNзадается таким образом, что любое сравнение x == NaNявляется ложным, независимо от другого операнда, включая NaNсам 1 .

Гленн Мосс
источник
8
КСТАТИ NaN !== NaN. Так что, я думаю, это не совсем правильно говорить, Number('abcd') == NaNпотому что Number('abcd')есть, NaNно не равно NaN. Обожаю JavaScript.
nilfalse
Да. Я хотел сказать, что Number('abcd')это так, NaNно подразумевал, что это проверяет истинность на равенство, но это не так. Я его отредактирую.
Glenn Moss
Интересно , почему isNaNи Numberпредназначены для себя таким образом?
timidboy
1
Преобразование , nullчтобы 0только ( по крайней мере , в данном контексте) происходит в пределах isNaN()функции, которая принуждает ее аргумент.
Гленн Мосс
3
Number (null) == 0 но parseInt (null) == NaN love JS
JoshBerke
31

Я сам столкнулся с этой проблемой.

Для меня лучший способ использовать isNaN - это так

isNaN(parseInt(myInt))

взяв пример физомы сверху,

var x = [undefined, NaN,     'blah', 0/0,  null, 0,     '0',   1,     1/0, -1/0,  Number(5)]
x.map( function(n){ return isNaN(parseInt(n))})
        [true,      true,    true,   true, true, false, false, false, true, true, false]

(Я выровнял результат в соответствии с вводными данными, надеюсь, это облегчит чтение.)

Мне так кажется лучше.

парень мограби
источник
1
Не будет работать, если myInt= "123d". parseIntпреобразует «123d» в 123, которое затем не проходит isNaNпроверку.
divesh premdeep
действительно, если вы хотите уловить и этот сценарий, я предлагаю объединить мой ответ и ответ Гленна. который будет выглядеть так isNaN(parseInt(str,10)) || isNaN(Number()). btw - для меня, поскольку мне нужно запустить parseInt, чтобы использовать числовое значение строки, допустить, чтобы "123d" считалось действительным числом, это нормально. Однако я вижу необходимость обнаружить и этот сценарий.
парень мограби
8

(Мой другой комментарий основан на практическом подходе. Вот теоретическая сторона.)

Я просмотрел стандарт ECMA 262 , который реализует Javascript. Их спецификация для isNan:

Применяет ToNumber к своему аргументу, затем возвращает true, если результат равен NaN, в противном случае возвращает false.

Раздел 9.3 определяет поведение ToNumber(которое является не вызываемой функцией, а скорее компонентом системы преобразования типов). Подводя итог таблице, можно сказать, что некоторые типы ввода могут давать NaN. Это тип undefined, тип number(но только значение NaN), любой объект, чье примитивное представление есть NaN, и все, stringчто не может быть проанализировано. Это листья undefined, NaN, new Number(NaN), и большинство строк.

Любой такой вход , который производит в NaNкачестве выходного сигнала при передаче ToNumberбудет производить trueпри скармливании isNaN. Поскольку nullможно успешно преобразовать в число, он не производит true.

И вот почему.

относитесь к своим модам хорошо
источник
7

Это действительно беспокоит. Вот массив значений, которые я тестировал:

var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]

Он оценивает (в консоли Firebug) как:

,NaN,blah,NaN,,0,0,1,Infinity,-Infinity,5

Когда я звоню x.map(isNaN)(чтобы вызвать isNaN для каждого значения), я получаю:

true,true,true,true,false,false,false,false,false,false,false

В заключение isNaNвыглядит довольно бесполезным! ( Изменить : за исключением того, что isNaN определяется только для Number, и в этом случае он работает нормально - только с вводящим в заблуждение именем.)

Между прочим, вот типы этих значений:

x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number
относитесь к своим модам хорошо
источник
Как вы думаете, что должно означать NaN? Как вы думаете, что вводит в заблуждение относительно NaN или его тестирования? Почему вы обеспокоены?
Bekim Bacaj
Что ж, это было 8 лет назад, но, похоже, я был обеспокоен тем, что 1) он дает несогласованные результаты для значений, не относящихся к типу Number, и 2) он всегда возвращает true для чего-то, что не относится к типу Number. Потому что на самом деле строка не является NaN. (Также см. Мой другой ответ, который объясняет, почему это происходит.)
к своим модам хорошо
5

Null не является NaN, как и строка не является NaN. isNaN () просто проверьте, действительно ли у вас есть объект NaN.

штуковина
источник
Но тогда, по крайней мере, строка преобразуется в объект NaN, поскольку isNaN ("текст") возвращает истину.
Ханно Фиц,
1

Я не совсем уверен, когда дело доходит до JS, но я видел похожие вещи на других языках, и обычно это потому, что функция только проверяет, точно ли null равно NaN (т.е. null === NaN будет ложным). Другими словами, он не считает, что null на самом деле является числом, а скорее, что null не является NaN. Вероятно, это связано с тем, что оба они представлены в JS по-разному, поэтому они не будут точно равны, точно так же, как 9! == '9'.

Джейсон Тенньер
источник
1

В ES5 он определен как isNaN (number)возвращает true, если аргумент приводится к NaN, а в противном случае возвращает false.

  • Если ToNumber (число) - NaN, вернуть истину.
  • В противном случае верните false.

И посмотрите таблицу преобразования ToNumber абстрактной операции . Так что внутри JS двигатель оценивать ToNumber(Null)это +0, то в конечном итоге isNaN(null)являетсяfalse

Rab
источник
0

Примечание:

"1" == 1 // true
"1" === 1 // false

Оператор == выполняет преобразование типов, а === - нет.

Сайт Дугласа Крокфорда , Yahoo! Евангелист JavaScript - отличный ресурс для подобных вещей.

cllpse
источник