Примитивные типы данных JavaScript

Примитивные типы данных — это базовые, неструктурированные значения в JavaScript, которые не содержат свойств или методов сами по себе и не могут быть изменены после создания. Главная особенность примитивов — они передаются по значению: при присваивании или передаче в функцию копируется само значение, а не ссылка на него. Это означает, что если вы присвоите примитивную переменную другой переменной и измените одну из них — другая останется неизменной. Примитивы также не имеют внутреннего состояния — их нельзя расширить, добавить свойства или переопределить. Они являются фундаментом для всех операций в JavaScript и лежат в основе хранения и обработки любых данных — от чисел в расчётах до текста в интерфейсе мобильного приложения.

Типы:

number — целые и дробные числа, особенности представления (IEEE 754)

В JavaScript все числа — это числа с плавающей запятой по стандарту IEEE 754. Это значит, что даже целые числа хранятся как дробные, и из-за особенностей двоичного представления могут возникать небольшие погрешности при вычислениях (например, 0.1 + 0.2 !== 0.3). JavaScript не разделяет целые и вещественные числа — всё объединено в один тип number. Поддерживаются положительные, отрицательные, ноль, а также специальные значения Infinity, -Infinity и NaN (Not a Number).

string — последовательность символов, кавычки, шаблонные строки

Строка в JavaScript — это последовательность символов, заключённая в одинарные ('), двойные (") или обратные кавычки (`). Обратные кавычки позволяют использовать шаблонные строки — вставлять переменные через ${} и писать многострочный текст без экранирования. Например: `Привет, ${name}!`. Строки неизменяемы — любое изменение создаёт новую строку, а не модифицирует существующую.

boolean — true / false

Логический тип представляет два возможных значения: true и false. Используется для условий, сравнений, флагов состояний (например, загружено ли приложение, авторизован ли пользователь). В JavaScript есть понятие «ложных» и «истинных» значений — например, 0, "", null, undefined воспринимаются как false в булевом контексте, но сами по себе они не являются boolean.

null — явное отсутствие значения

null — это специальное значение, которое программист присваивает намеренно, чтобы указать: «здесь ничего нет». Это не ошибка и не неопределённость — это осознанное обозначение отсутствия данных. Например, если пользователь не выбрал аватар, можно установить avatarUrl: null. Несмотря на поведение typeof null === 'object' (историческая ошибка языка), null — это примитив.

undefined — неинициализированная переменная

undefined означает, что переменная была объявлена, но ей ещё не присвоено значение. Это автоматическое состояние, которое присваивается переменной при объявлении через let или const без инициализации. Также возвращается, если функция не возвращает значение, или свойство объекта не существует. Отличие от null: undefined — это результат отсутствия действия, а null — результат преднамеренного действия.

Pasted image 20240923235323.png

symbol — уникальный идентификатор

Symbol — это новый примитивный тип, появившийся в ES6, предназначенный для создания уникальных ключей свойств объектов. Каждый символ, созданный через Symbol(), гарантированно уникален, даже если у них одинаковое описание. Используется редко в базовой разработке, но важен для внутренней работы библиотек и предотвращения коллизий имён свойств. Не нужно запоминать детали — достаточно знать, что он существует и используется для скрытия и защиты ключей.

bigint — произвольной точности целые числа

bigint — это тип для работы с целыми числами, превышающими предел Number.MAX_SAFE_INTEGER (около 9 квадриллионов). Чтобы создать bigint, нужно добавить букву n в конец числа: 123456789012345678901234567890n. Он нужен для точных расчётов — например, при работе с финансовыми данными, хешами, криптографией или большими идентификаторами. С bigint нельзя выполнять операции с обычными числами — требуется явное приведение.

Определение типа: typeof

Оператор typeof в JavaScript используется для определения типа значения на момент выполнения. Он возвращает строку с названием типа данных. Это важный инструмент для отладки, валидации входных данных и безопасной работы с переменными, особенно когда тип неизвестен заранее — например, при получении данных из API или пользовательского ввода.

Примеры использования typeof:

typeof 42;                    // "number"
typeof 3.14;                  // "number"
typeof -0;                    // "number"
typeof Infinity;              // "number"
typeof NaN;                   // "number"

typeof "Привет";              // "string"
typeof '';                    // "string"
typeof `Шаблон ${1 + 2}`;     // "string"

typeof true;                  // "boolean"
typeof false;                 // "boolean"

typeof null;                  // "object" — историческая ошибка языка

typeof undefined;             // "undefined"

typeof Symbol('id');          // "symbol"

typeof 12345678901234567890n; // "bigint"

typeof function() {};         // "function"

typeof {};                    // "object"
typeof [];                    // "object" — массивы тоже объекты

Важные особенности:

  • typeof null возвращает "object" — это известная ошибка в спецификации JavaScript, которая осталась ради совместимости.
  • typeof всегда возвращает строку, даже если значение не существует (например, typeof undeclaredVariable вернёт "undefined" без ошибки).
  • Для проверки массивов или объектов typeof недостаточен — лучше использовать Array.isArray() или instanceof.

Пример практического применения:

При работе с JSON-ответами API часто приходят данные неизвестного типа. Например, поле userId может быть числом или строкой. Перед использованием его стоит проверить:

if (typeof userId === 'string') {
  // обработать как строку
} else if (typeof userId === 'number') {
  // обработать как число
}

Преобразование типов: явное и неявное

Преобразование типов — это процесс изменения значения одного типа данных в другой. В JavaScript, как в языке с динамической типизацией, это происходит постоянно — иногда намеренно, а иногда автоматически, что может приводить к неожиданным результатам. Различают два вида преобразований: явное (когда разработчик сам управляет процессом) и неявное (когда язык делает это автоматически на основе контекста).

Явное преобразование типов

Явное преобразование — это когда разработчик сознательно и намеренно превращает значение из одного типа в другой с помощью специальных функций или операторов. Это безопасный и предсказуемый способ, который рекомендуется использовать в production-коде.

Примеры явного преобразования:

// Число → строка
String(42);           // "42"
(42).toString();      // "42"
42 + "";              // "42" — это уже неявное, но можно использовать и явно

// Строка → число
Number("123");        // 123
Number("3.14");       // 3.14
Number("abc");        // NaN — некорректная строка
parseInt("42px");     // 42 — преобразует до первого нецифрового символа
parseFloat("3.14abc"); // 3.14

// Строка → булево
Boolean("hello");     // true
Boolean("");          // false — пустая строка — ложь
Boolean("0");         // true — любая непустая строка — истина, даже "false"

// Число → булево
Boolean(0);           // false
Boolean(-1);          // true
Boolean(1);           // true
Boolean(NaN);         // false
Boolean(Infinity);    // true

// Любое значение → булево через двойное отрицание
!!"text";             // true
!!0;                  // false
!!null;               // false

// Объект → строка
String({});           // "[object Object]"
JSON.stringify({});   // "{}" — для структурированного представления

Когда использовать явное преобразование:

  • При получении данных из формы (input.value всегда строка).
  • При работе с API, где типы могут быть неточными.
  • При необходимости гарантировать поведение — например, чтобы +"5" не вызывало путаницы.
  • В тестах и валидации входных данных.

✅ Правило: Если вы не уверены в типе — приведите его явно. Это делает код понятнее и надёжнее.


Неявное преобразование типов (коэрши́он)

Неявное преобразование — это когда JavaScript автоматически меняет тип значения, чтобы выполнить операцию. Оно происходит при использовании операторов +, -, *, /, ==, if(), while() и других в контекстах, требующих другого типа. Это мощный механизм, но он часто вызывает ошибки у новичков, потому что поведение не всегда очевидно.

Основные правила неявного преобразования:

1. Оператор + — сложение или конкатенация?

Если хотя бы один операнд — строка, то оба приводятся к строке:

"5" + 3;          // "53" — строка + число → конкатенация
5 + "3";          // "53"
"Привет" + 123;   // "Привет123"
true + "!"        // "true!"

Если оба операнда — числа — выполняется математическое сложение:

5 + 3;            // 8
0.1 + 0.2;        // 0.30000000000000004 — особенность IEEE 754

Математическое сложение так-же происходит если среди операндов нет строк, в таком случае операнды преобразуются в числа:

5 + true;            // 6 = 5 + 1
false + Infinity;        // Infinity - что тоже является числом // 0 + бесконечность
2. Операторы -, *, /, % — только числа

Эти операторы всегда пытаются привести операнды к числам:

"5" - "3";        // 2 — строки превратились в числа
"10" * "2";       // 20
"15" / "3";       // 5
"abc" - 1;        // NaN — нельзя преобразовать "abc" в число
true - 1;         // 0 — true → 1
false * 5;        // 0 — false → 0
3. Сравнение с == (абстрактное равенство)

Оператор == выполняет неявное приведение типов перед сравнением — это главный источник ошибок:

"" == 0;          // true — пустая строка → 0
"0" == 0;         // true — "0" → 0
false == 0;       // true — false → 0
null == undefined;// true — особое правило: они равны друг другу
0 == false;       // true — false → 0
"1" == true;      // true — "1" → 1, true → 1
[] == 0;          // true — пустой массив → "" → 0
[1] == 1;         // true — [1] → "1" → 1
[1,2] == "1,2";   // true — массив → строка "1,2"
{} == "[object Object]"; // false — объекты сравниваются по ссылке, но при приведении к строке — да, но тут логика сложнее

⚠️ Важно: null == undefined — единственное исключение, где два разных примитива считаются равными.

4. Условия в if, while, ? :

Любое значение приводится к булевому типу:

if ("0") { console.log("Это правда!"); }   // сработает — непустая строка → true
if (0) { console.log("Не сработает"); }    // не сработает — 0 → false
if ([]) { console.log("Это правда!"); }    // сработает — массив → true
if ({}) { console.log("Это правда!"); }    // сработает — объект → true
if (null) { console.log("Не сработает"); } // не сработает — null → false
if (undefined) { console.log("Не сработает"); } // не сработает

Таблица неявного приведения к булевому типу («ложные» значения):

Значение Результат
false false
0 false
-0 false
0n false
"" false
null false
undefined false
NaN false
Все остальное true

📌 Эти 6 значений называются falsy — всё остальное — truthy.


Почему неявное преобразование опасно?

// Начинающий разработчик ожидает:
"5" == 5;         // true — логично?
"5" === 5;        // false — теперь понятно!

// А вот так:
[] == ![];        // true — почему?
// Разбор:
// ![] → false (пустой массив → true → отрицание → false)
// [] == false → "" == false → 0 == 0 → true

// Или:
" \t\n" == 0;     // true — строка с пробелами → пустая после trim → 0

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