- Подводные камни JavaScript
- Типы
- Магические значения: undefined, null, NaN
- Работа с дробными числами
- Вывод
- Number.NaN
- Try it
- Value
- Description
- Examples
- Checking whether values are numeric
- Specifications
- Browser compatibility
- See also
- Found a content problem with this page?
- MDN
- Support
- Our communities
- Developers
- Javascript nan что такое
- Интерактивный пример
- Описание
- Проверка на равенство NaN
- Спецификации
- Совместимость с браузерами
- Смотрите также
- Found a content problem with this page?
- MDN
- Support
- Our communities
- Developers
Подводные камни JavaScript
Мне очень нравится JavaScript и я считаю его мощным и удобным. Но для большинства начинающих JS-программистов, много проблем создаёт недопонимание аспектов языка. Часто конструкции языка ведут себя «нелогично». В данной статье я хочу привести примеры «граблей», на которые я наступил; объяснить поведение языка и дать пару советов.
Типы
typeof 5; //"number", ок, похоже на правду typeof "hello"; //"string" typeof true; //"boolean" typeof undefined; //"undefined" typeof <>; //"object". Пока 5 из 5 typeof null; //"object". WTF? typeof function()<>; //"function". Разве у нас есть тип function?
Проблема: несмотря на то, что тип у null — Null, оператор возвращает ‘object’; а тип у функции — Object, оператор возвращает ‘function’, а такого типа нет.
Объяснение: typeof возвращает не тип, а строку, которая зависит от аргумента и не является именем типа.
Совет: забудьте про типы. Серьезно, я считаю что знание 6 типов JS не даст вам пользы, а оператор typeof используется довольно часто, поэтому лучше запомнить результаты его работы:
Тип аргумента | Результат |
Undefined | undefined |
Null | object |
Boolean | boolean |
Number | number |
String | string |
Object (результаты оператора new, inline-объекты ()) | object |
Object (функции) | function |
Магические значения: undefined, null, NaN
- undefined value — primitive value used when a variable has not been assigned a value
- Undefined type — type whose sole value is the undefined value
- null value — primitive value that represents the intentional absence of any object value
- Null type — type whose sole value is the null value
- NaN — number value that is a IEEE 754 “Not-a-Number” value
- undefined — значение переменной, которая не была инициализирована. Единственное значение типа Undefined.
- null — умышленно созданный «пустой» объект. Единственное значение типа Null.
- NaN — специальное значение типа Number, для выражения «не чисел», «неопределенности». Может быть получено, например, как результат деления 0 на 0 (из курса матанализа помним, что это неопределенность, а деление других чисел на 0 — это бесконечность, для которой в JS есть значения Infinity).
!!undefined; //false !!NaN; //false !!null; //false //как видим, все 3 значения при приведении к boolean дают false null == undefined; //true undefined === undefined; //true null === null; //true NaN == undefined; //false NaN == null; //false NaN === NaN; //false! NaN == NaN; //false!
Проблема: с чем бы мы ни сравнивали NaN, результатом сравнения всегда будет false.
Объяснение: NaN может возникать в результате множества операций: 0/0, parseInt(‘неприводимая к числу строка’), Math.sqrt(-1) и было бы странно, если корень из -1 равнялся 0/0. Именно поэтому NaN !== NaN.
Совет: не использовать булевы операторы с NaN. Для проверки нужно использовать функцию isNaN.
typeof a; //'undefined' a; //ReferenceError: a is not defined
Проблема: оператор typeof говорит нам, что тип необъявленной переменной — undefined, но при обращении к ней происходит ошибка.
Объяснение: на самом деле, есть 2 понятия — Undefined и Undeclared. Так вот, необъявленная переменная является Undeclared-переменной и обращение к ней вызывает ошибку. Объявленная, но не инициализированная переменная принимает значение undefined и при обращении к ней ошибок не возникает.
Совет: перед обращением к переменной, вы должны быть уверенны, что она объявлена. Если вы обратитесь к Undeclared-переменной, то код, следующий за обращением, не будет выполнен.
var a; //вновь объявленная переменная, для которой не указано значение, принимает значение undefined console.log(undefined); //undefined console.log(a); // undefined a === undefined; //true undefined = 1; console.log(undefined); //1 a === undefined; //false
- Вы можете сравнивать не значение переменной, а её тип: «typeof a === ‘undefined’».
- Использовать паттерн immediately-invoked function:
(function(window, undefined)< //т.к. второй аргумент не был передан, значение переменной undefined будет «правильным». >(this));
- Для получения реального «undefined»-значения можно использовать оператор void (кстати, я не знаю другого применения этому оператору):
typeof void(0) === 'undefined' // true
Теперь попробуем совершить аналогичные действия с null:
console.log(null); //null null = 1; //ReferenceError: Invalid left-hand side in assignment
Проблема: несмотря на некоторые сходства между null и undefined, null мы перезаписать не можем. На самом деле проблема не в этом, а в том, что язык ведёт себя нелогично: даёт перезаписать undefined, но не даёт перезаписать null.
Объяснение: null — это не глобальная переменная и вы не можете её создать, т. к. null — зарезервированное слово.
Совет: в JavaScript не так много зарезервированных слов, проще их запомнить и не использовать как имена переменных, чем вникать, в чём проблема, когда она возникнет.
И теперь сделаем тоже самое с NaN:
console.log(NaN); //NaN NaN = 1; console.log(NaN); //NaN isNaN(NaN); //true
Проблема: при переопределении undefined всё прошло успешно, при переопределении null возникла ошибка, а при переопределении NaN операция не вызвала ошибки, но свойство не было переопределено.
Объяснение: нужно понимать, что NaN — переменная глобального контекста (объекта window). Помимо этого, к NaN можно «достучаться» через Number.NaN. Но это неважно, ниодно из этих свойств вы не сможете переопределить, т. к. NaN — not writable property:
Object.getOwnPropertyDescriptor(window, NaN).writable; //false Object.getOwnPropertyDescriptor(Number, NaN).writable; //false
Совет: как JS-программисту, вам нужно знать об атрибутах свойств:
Атрибут | Тип | Смысл |
enumerable | Boolean | Если true, то данное свойство будет участвовать в циклах for-in |
writable | Boolean | Если false, то значение этого свойства нельзя будет изменить |
configurable | Boolean | Если false, то значение этого свойства нельзя изменить, удалить и изменить атрибуты свойства тоже нельзя |
value | Любой | Значение свойства при его чтении |
get | Object (или Undefined) | функция-геттер |
set | Object (или Undefined) | функция-сеттер |
Вы можете объявлять неудаляемые или read-only свойства и для созданных вами объектов, используя метод Object.defineProperty:
var obj = <>; Object.defineProperty(obj, 'a', ); Object.defineProperty(obj, 'b', ); Object.defineProperty(obj, 'c', ); console.log(obj.a); //a obj.a = 'b'; console.log(obj.a); //b delete obj.a; //true console.log(obj.b); //b obj.b = 'a'; console.log(obj.b); //b delete obj.b; //true console.log(obj.c); //c obj.b = 'a'; console.log(obj.c); //c delete obj.b; //false
Работа с дробными числами
Давайте вспомним 3-й класс и сложим несколько десятичных дробей. Результаты сложения в уме проверим в консоли JS:
0.5 + 0.5; //1 0.5 + 0.7; //1.2 0.1 + 0.2; //0.30000000000000004; 0.1 + 0.7; //0.7999999999999999; 0.1 + 0.2 - 0.2; //0.10000000000000003
Проблема: при сложении некоторых дробных чисел, выдаётся арифметически неверный результат.
Объяснение: такие результаты получаются из-за особенностей работы c числами с плавающей точкой. Это не является особенностью JavaScript, другие языки работают также (я проверил в PHP, Python и Ruby).
Совет: во-первых, вы, как программист, обязаны знать об особенностях работы компьютера с числами с плавающей точкой. Во-вторых, в большинстве случаев достаточно просто округлять результаты. Но, если вдруг необходимо выдавать пользователю точный результат, например, при работе с данными о деньгах, вы можете просто умножать все аргументы на 10 и результат делить обратно на 10, например так:
function sum() < var result = 0; for (var i = 0, max = arguments.length; i< max; i++ ) < result += arguments[i]*10; >return result / 10; > sum(0.5, 0.5); //1 sum(0.5, 0.7); //1.2 sum(0.1, 0.2); //0.3 sum(0.1, 0.7); //0.8 sum(0.1, 0.2, -0.2); //0.1
Вывод
Это только несколько необычных примеров с непредсказуемым результатом. Если помнить о них, то получится не наступить на те же грабли и быстро понять, в чём проблема. Если найдёте новый «нелогичный» кусок кода, то попробуйте осознать, что происходит с точки зрения языка, почитав спецификацию или MDN.
Number.NaN
The Number.NaN static data property represents Not-A-Number, which is equivalent to NaN . For more information about the behaviors of NaN , see the description for the global property.
Try it
Value
Property attributes of Number.NaN | |
---|---|
Writable | no |
Enumerable | no |
Configurable | no |
Description
Because NaN is a static property of Number , you always use it as Number.NaN , rather than as a property of a number value.
Examples
Checking whether values are numeric
function sanitize(x) if (isNaN(x)) return Number.NaN; > return x; >
Specifications
Browser compatibility
BCD tables only load in the browser
See also
Found a content problem with this page?
This page was last modified on Feb 21, 2023 by MDN contributors.
Your blueprint for a better internet.
MDN
Support
Our communities
Developers
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.
Javascript nan что такое
Глобальное свойство NaN является значением, представляющим не-число (Not-A-Number).
Атрибуты свойства NaN | |
---|---|
Записываемое | нет |
Перечисляемое | нет |
Настраиваемое | нет |
Интерактивный пример
Описание
NaN является свойством глобального объекта.
Начальным значением NaN является Not-A-Number (не-число) — то же самое значение, что и у Number.NaN . В современных браузерах NaN является ненастраиваемым и незаписываемым свойством. Даже когда это не так, избегайте его переопределения.
В программах NaN используется довольно редко. Это возвращаемое значение в ситуациях, когда математические ( Math ) функции не срабатывают должным образом (например, при вызове Math.sqrt(-1) ) или когда функция, пытающаяся считать число из строки, терпит неудачу по причине того, что в строке не число ( parseInt(‘blabla’) ).
Проверка на равенство NaN
NaN является неравным (посредством сравнения через == , != , === , and !== ) любому другому значению, включая другое значение NaN. Используйте Number.isNaN() или isNaN() , чтобы наиболее понятным образом определить является ли значение значением NaN. Или выполните само-сравнение: NaN, и только NaN, в результате такого сравнения будет неравным самому себе.
NaN === NaN; // false Number.NaN === NaN; // false isNaN(NaN); // true isNaN(Number.NaN); // true function valueIsNaN(v) return v !== v; > valueIsNaN(1); // false valueIsNaN(NaN); // true valueIsNaN(Number.NaN); // true
Тем не менее, обратите внимание на разницу между функцией isNaN() и методом Number.isNaN() : первая вернёт true , если значение в настоящий момент является NaN , или если оно станет NaN после того, как преобразуется в число, в то время как последний вернёт true , только если текущим значением является NaN :
isNaN('hello world'); // true Number.isNaN('hello world'); // false
Спецификации
Совместимость с браузерами
BCD tables only load in the browser
Смотрите также
Found a content problem with this page?
This page was last modified on 2 янв. 2023 г. by MDN contributors.
Your blueprint for a better internet.
MDN
Support
Our communities
Developers
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2023 by individual mozilla.org contributors. Content available under a Creative Commons license.