// codeart.ru / Работаем с кодом / JavaScript. Грабли с NaN Форум

JavaScript. Грабли с NaN rss подписка

Автор: Evgeniy Sergeev

Программируя на javascript никогда не знаешь когда встанешь на очередную граблю. Иногда складывается впечатление, что в языке больше плохого чем, хорошего.

Например, в JS есть такое значение — NaN расшифровывается как Not A Number. Обычно это значение возвращается при ошибке выполнения операций с числами.


parseInt('AAAA'); // NaN
parseInt('1'); // 1
parseInt('16 somthing else'); // 16 — неожиданно, правда?

В данном примере, при выполнении первой операции функция parseInt не смогла преобразовать строку в число и поэтому вернула значения NaN. А вот в 3-ей строке, все прошло успешно, что кажется мне не совсем логичным.

Другие фокусы, которые выкидывает JavaScript хорошо демонстрирует следующий пример:


NaN == NaN; // false
NaN != NaN; // true
NaN > NaN; // false
NaN < NaN; // false

typeof NaN === 'number';  // true

Получается, что NaN не равен сам себе, одновременно NaN не больше и не меньше самого себя. А если запросить его тип, то оказывается, что это число. Непонятно, почему тогда не работают операции сравнения.

Плохо в данной ситуации то, что код который многим кажется вполне рабочим, на самом деле таковым не является:



function sum(a, b) {
	a = parseInt(a, 10);
	b = parseInt(b, 10);
	if (a == NaN || b == NaN) {
		alert('Ошибка');
		return 0; 
	}
	return a+b;
}

sum('1', 'bbbb');  // NaN


Причина, в том, что сравнение a == NaN || b == NaN — всегда будет возвращать false, даже если a или b будут иметь значение NaN.

Для того, чтобы узнать содержит ли переменная значение NaN используется функция isNaN:


isNaN(NaN); // true
isNaN('AAAA'); // true
isNaN('0'); // false

Чтобы избежать проблем со значением NaN я использую собственную функцию определения является ли переменная числом:


function isNum(v) {
	return typeof v === 'number' && isFinite(v);
}

    Лучшие комментарии

  1. Вот еще один забавный пример:
    parseInt(‘Five Dollars’,16);
    Кто догадается что выдаст функция?

  2. А, по-моему, всё предельно ясно если знать основы языка.
    Просто нужно понимать, что такое NaN и знать о существовании функции isNaN, и тогда включается свет в этой тёмной комнате! :)

    Куда интереснее пример:
    var f = function()
    {
    return
    {
    a:1
    };
    }
    alert(typeof f());

    что вернёт?
    тут нужно куда глубже знания особенностей языка :)

  1. > Получается, что NaN не равен сам себе, одновременно NaN не больше и не меньше самого себя. А если запросить его тип, то оказывается, что это число. Непонятно, почему тогда не работают операции сравнения.

    Они, как раз, работают. А возвращают false потому что нет способа определить, что один NaN больше другого. Тут бы, конечно, следовало ошибку кинуть или вернуть null какой-нибудь. false это у них просто «дефолтное значение» как бы. Получается, что на false в сравнениях чисел нельзя ориентироваться, можно только на true.

  2. Сергей, мне кажется, что все же лучше сначала убедиться, что функция не вернуло значение NaN, а уже потом сравнивать.
    Хотя, конечно, в большинстве случаев «истина» более информативно чем «ложь».

  3. parseInt(’16 somthing else’); // 16 — неожиданно, правда?

    А что здесь неожиданное? функция для этого и предназначена, преобразовать строку в число.
    Логика простая, берём все цифры в строке, от начала и до первого символа «не цифра».

  4. Тоже сейчас столкнулся с этим, причём вообще непонятно откуда это NaN взялось, число там! Странно, странно. С гугла к тебе пришёл ) Кое-что прояснилось, спасибо.

  5. if(parseInt($(this).val())+»_» != «NaN_»)

  6. автор пишет: parseInt(’16 somthing else’); // 16 — неожиданно, правда?

    Автор, вы как рах ХОРОШЕЕ приняли тут за «плохое».
    лично мне кажется прозрачным то… что метод выполняется. Потому что JS создан для работы с HTML. А в HTML у нас какие цифровые значения? Это всегда — ПИКСЕЛИ. Так вот… если мы в качестве аргумента в функцию передадим строчку «10px»… чтобы затем выделывать с ней мат.преобразования… то не нужно заморачиваться на отсечение «px». Функция всё сделала за вас и уменьшила Вам работку.

  7. Вот еще один забавный пример:
    parseInt(‘Five Dollars’,16);
    Кто догадается что выдаст функция?

  8. to Evgeny Sergeev:
    Забавно. Стало интересно — полез смотреть =)
    Возвращает 0xF в десятичной системе исчисления.

  9. 2 Solovyov A.A.
    0xF — это не десятичная, а шестнадцатиричная. Все правильно возвращает — перекодирует первый символ (F) по основанию 16.

  10. А, по-моему, всё предельно ясно если знать основы языка.
    Просто нужно понимать, что такое NaN и знать о существовании функции isNaN, и тогда включается свет в этой тёмной комнате! :)

    Куда интереснее пример:
    var f = function()
    {
    return
    {
    a:1
    };
    }
    alert(typeof f());

    что вернёт?
    тут нужно куда глубже знания особенностей языка :)

  11. Nick, про Semicolon insertion я уже писал — http://www.codeart.ru/2010/01/25/podvodnye-kamni-javascript-ili-opasnoe-svojstvo-semicolon-insertion/

    По поводу «если знать основы языка», язык должен быть максимально прозрачным, так как изучение языка — это не цель, а средство.

  12. А почему никто не упоминает о фонкции isNaN() ? все прекрасно работает

    function sum(a, b) {
    a = parseInt(a, 10);
    b = parseInt(b, 10);
    if (isNaN(a)||isNaN(b)) { //ключевое изменение
    alert('Ошибка');
    return 0;
    }
    return a+b;
    }

    sum('1', 'bbbb'); // NaN

  13. Так последний абзац статьи как раз и содержит упоминание о isNan. Только это не меняет того факта, что нерабочий код будет казаться разработчику вполне нормальным.
    Плюс ко всему в общем случае задача состоит не в том чтобы определить является ли переменная NaN, а в том чтобы определить является ли она числом.

  14. Такое поведение NaN, как и функции parseInt является конвенциональным для всех известных мне языков. В Си, например, такое значение тоже существует, и, наряду с inf, образует штатный набор. Сишные atof(), atod() тоже пройдут по строке, начиная с первого символа до тех пор, пока считанный символ не станет [^\d]. Это традиционное поведение и для интерпретируемых языков вроде Lua, Python, Perl и прочих Ruby. Лучшее, что тут можно ожидать — warning «лишние символы на хвосте».

    Вот что действительно меня удивило применительно к JS, так это False == (Number.NaN === Number.NaN), и кроме isNan(), способа проверить данные на NaN’овость не существует.

Leave a Reply

« »