NaN === NaN // false
И что операции, невозможные арифметически, вернут NaN.
'BlaBlaBla'/0 // NaN
Но у NaN есть одно мало известное(?), и, как мне кажется, весьма полезное применение.
TL;DR Все дело в Date
В двух словах:
new Date(NaN) // Invalid Date
Чем полезно? Invalid Date все равно Date. И все операции с Date все ещё на месте.
Любые операции с Date, кроме прямой установки timestamp вернут NaN, оставив Date как Invalid Date.
const x = new Date(NaN) // Invalid Date
x.setTime(12345) // 12345
x.toString() // "Thu Jan 01 1970 00:00:12 GMT+0000 (+00)"
При этом, проверка на валидность даты становится проще некуда
const x = new Date(NaN) // Invalid Date
isNaN(x) // true
x.setTime(0)
isNaN(x) // false
Заметьте, преобразование в timestamp здесь не требуется, valueOf() делает это под капотом.
Все операции с Date — мутабельные. Тем не менее, клонирование через конструктор прекрасно работает и с Invalid Date.
const x = new Date(NaN) // Invalid Date
const y = new Date(x) // Invalid Date
Сравнение двух дат напрямую в Date не реализовано и сравнивать даты можно только через timestamp. NaN гарантирует что Invalid Date точно не будет равно никакой другой дате. Думаю, это весьма полезное свойство.
const x = new Date(NaN) // Invalid Date
const y = new Date(NaN) // Invalid Date
x.getTime() === y.getTime() // false
К моему сожалению, конструктор Date ведёт себя несколько странно по отношению к входному параметру.
new Date(null) // Thu Jan 01 1970 00:00:00 GMT+0000 (+00)
Было бы намного логичнее конструировать Invalid Date, ведь null — это не совсем ноль. Оставим это на совести Javascript-а.
Однако, если насильственно передать в конструктор undefined, то результат выглядит ожидаемым. Так что будьте осторожны.
new Date(undefined) // Invalid Date
Статья получилась больше о Date чем о NaN, но, в целом, именно об этой связке я хотел рассказать.
Комментарии (29)
awMinor
22.08.2017 11:44+5Все же я так и не понял полезность данной конструкции в реальной жизни и чем полезен NaN
tenbits
22.08.2017 12:12+2Например, проверить или распарсенная строка действительно была датой.
let date = new Date('foo'); let isInvalidDate= isNaN(date); console.log(isInvalidDate ? "Yes, it was invalid" : "Ooops, was OK");
torbasow
23.08.2017 10:11Только NaN тут не при чём, на самом деле. Это функция isNaN выдаёт true на всё, что не приводится к числу, в частности, на строку «Invalid Date». А Number.isNaN(«Invalid Date»)===false.
Изложение в статье крайне сумбурное и запутывает простые вещи.Zenitchik
23.08.2017 13:54isNaN использует метод valueOf, а он у Invalid Date возвращает NaN. Ни при чём как раз строка.
Poccomaxa_zt
22.08.2017 14:53+2Я задам возможно неправильный вопрос — но если Вы принимаете ко вниманию ситуации, когда Вам необходимо создавать даты с вероятно невалидными данными, возможно стоит все-таки изначально делать соответствующие проверки? Мне кажется это будет более правильная логика чем работать далее с Invalid Date…
ua9msn Автор
22.08.2017 14:57Ситуация возникает когда необходимо иметь невалидную дату изначально.
Например для input field. Можно же ничего туда не вводить. Наилучшее представление для отсутствующей даты — Invalid Date. То есть дата, но и в тоже время не дата.Aingis
22.08.2017 16:16Для такого случая можно подать просто пустую строку. Можно не добавлять сложности с
NaN
.
new Date('') // > Invalid Date
P.S. Сnull
всё логично, просто напростоisNaN(null) === false
. Отличиеnull
отundefined
именно в том, что он приводится к нулю, когда идёт приведение к числу.ua9msn Автор
22.08.2017 22:50Логично с точки зрения Number. Я прекрасно понимаю что вы имеете ввиду. Но вот с точки зрения даты — какая дата у никакого времени? Внутри конструктора Date можно было бы обойти такое «нативное» приведение типов. Из за этого приходится делать дополнительную проверку что то, что прилетело, это не null. Все остальное вернет правильный результат: либо Date либо Invalid Date.
Aingis
22.08.2017 22:51+1null
во многом неприятный объект, Крокфорд вон говорит, что вообще от него отказался.
dagen
22.08.2017 14:58+4Интересное применение, не знал об этом :) Есть только один нюанс: в рамках ECMAScript 6 многие функции работы с числами, которые в предыдущих версиях были глобальными функциями, постепенно переезжают в статические методы Number. Чтобы в светлом будущем упорядочить хаос в JavaScript и пометить как устаревшие глобальные isNaN, parseInt, ...you name it.
Number.isNaN при этом не повторяет точь-в-точь поведение window.isNaN (в новой версии отсутствует неявное приведение типов), так что трюк из статьи не прокатит :( Но спасибо, всё равно интересно было почитать.ua9msn Автор
22.08.2017 15:00Там все немножко по другому. Да, есть свой isNaN в Number, но это тема для отдельной статьи.
valis
22.08.2017 16:02При этом, проверка на валидность даты становится проще некуда
Ну не знаю, для меня было потрясением когда конструкция типа
if (!date instanceof Date) throw new Error('Invalid Date')
не работалаReklatsMasters
22.08.2017 17:30+1Вы здесь конвертируете переменную date в bool и проверяете её instance. Прочитайте про приоритеты операций. Правильно будет так:
if (!(date instanceof Date)) // ...
staticlab
22.08.2017 19:30Тем не менее, это не поможет, так как объект Invalid Date из статьи — это всё равно Date. (Ранее на Хабре уже негодовали, что NaN — это Number).
kemsky
22.08.2017 22:41Не то что бы
NaN
был такой полезный, скорее в JS дату на валидность не проверить по-другому. Ну неявно еще можно вот такx.getTime() === x.getTime()
.
Akuma
Вы забыли упомянуть одну важную деталь: Не нужно использовать это в реальных проектах :)
Такое «магическое» поведение может зависеть от браузера, может поменяться внезапно в каком-то браузере или еще чего.
xel
да нет здесь никакой магии, конструктор принимает число, а
NaN
— числоприведение типов также работает предсказуемо и ожиданно
в исходниках momentjs используют подобную конструкцию
статья лишь напоминает, переходящим с php и других языков, с чуть другой логикой, что в javascript есть особые числа —
Infinity
,NaN
и методы для работы с числамиisFinite()
,isNaN()
Aingis
Вообще-то, никакое это не «магическое» поведение, это стандарт, причём старше чем сам Javascript. Браузеры ему строго следуют, и никогда не перестанут, так как иначе сломается обратная совместимость.
Может быть, это неожиданное поведение лично для вас в силу невежества, но настоящий разработчик отличается от кодера тем, что исследует предметную область.
Zenitchik
Тяжела и неказиста жизнь JavaScript-ера… Каждый, не читавший спеку, думает, что он умнее, потому что "в большинстве языков" спека другая.
Aingis
Только в данном случае спеки как раз те же. Стандарт представления чисел, включая
NaN
, IEEE 754 появился в 1985, а объектDate
очень нетипичен для JS и пришёл из Java. То есть это те особенности, которые были переняты из общепринятой практики. В восклицании «WTF JS» буквы «JS» лишние. Тут скорее в том, что приходит полные новички.ua9msn Автор
Это не магия. NaN в JS это данность, Date работает так как работает. Никаких изменений тут не будет. Даже в черновиках следующих версий JS ничего про дату нет, хотя очень очень хочется добавить установку произвольной таймзоны. Нет и не будет.
Large
Есть на stage 1 github.com/tc39/proposal-temporal
Vovaka
Как и упомянул выше ua9msn изменений поведения типа Date не предвидется. По указанной Вами ссылке говорится о исправлении API в его использовании за счёт введения в язык новых типов — CivilDate, CivilTime etc.
Или я что-то упустил?
Aingis
Я думаю, это была ссылка в ответ на желание установки часовых поясов.
staticlab
20.3 Date Objects
20.3.1 Overview of Date Objects and Definitions of Abstract Operations
The following functions are abstract operations that operate on time values (defined in 20.3.1.1). Note that, in every case, if any argument to one of these functions is NaN, the result will be NaN.
20.3.1.1 Time Values and Time Range
A Date object contains a Number indicating a particular instant in time to within a millisecond. Such a Number is called a time value. A time value may also be NaN, indicating that the Date object does not represent a specific instant of time.