Какие типы данных существует в JavaScript?

Какие типы данных существует в JavaScript?

Теория: какие типы данных есть в JavaScript

В спецификации ECMAScript значения делятся на примитивные типы и тип object. Примитивы представляют собой неделимые значения (число, строка и т.д.), а объекты — контейнеры со свойствами и поведением.

Термин “тип данных” в JavaScript обычно означает именно эти 8 категорий значений: undefined, null, boolean, number, bigint, string, symbol, object.

Схема: примитивы и объекты

Значения JavaScript
├─ Примитивы (7)
│  ├─ undefined
│  ├─ null
│  ├─ boolean
│  ├─ number
│  ├─ bigint
│  ├─ string
│  └─ symbol
└─ Object (1, непримитивный)
   ├─ plain object {}
   ├─ array []
   ├─ function () {}
   ├─ date new Date()
   ├─ map/set new Map(), new Set()
   └─ и другие встроенные/пользовательские объекты

Короткая таблица: тип, typeof, пример

ТипРезультат typeofПример значенияВажная особенность
undefinedundefinedundefined“Значение отсутствует” (часто у неинициализированных переменных)
nullobjectnullИсторическая особенность: typeof null возвращает object
booleanbooleantrueТолько true и false
numbernumber42, 3.14, NaNВсе обычные числа, включая NaN, Infinity, -0
bigintbigint9007199254740993nЦелые числа произвольной длины, литерал с n
stringstring"привет"Строки в Unicode, отдельного char нет
symbolsymbolSymbol("id")Уникальные идентификаторы, часто для “скрытых” ключей
objectobject (или function){}, [], () => {}Объекты и функции (функции — вызываемые объекты)

Разбор каждого типа с примерами

undefined

undefined означает, что значение не задано. Часто появляется у объявленной, но не инициализированной переменной, у отсутствующего свойства, а также как результат функции без return.

Примеры: let a; и typeof a.

let a;
console.log(a);           // undefined
console.log(typeof a);    // "undefined"

const obj = {};
console.log(obj.missing); // undefined

function f() {}
console.log(f());         // undefined
undefined и “не существует” — не одно и то же: переменная может не существовать (ReferenceError), а может существовать и быть равной undefined.
// Пример различия (в модуле/строгом режиме будет ReferenceError)
console.log(notDeclared); // ReferenceError: notDeclared is not defined

let declared;
console.log(declared);    // undefined

null

null обычно означает “пустое значение по намерению”: объект отсутствует, ссылка очищена и т.п.

Примеры: const x = null; и x === null.

const x = null;
console.log(x);             // null
console.log(x === null);    // true
console.log(typeof x);      // "object" (историческая особенность)

Практическая проверка на “null или undefined” часто делается через x == null, потому что нестрогое равенство в этом месте намеренно устроено так, что null == undefined истинно.

const a = null;
const b = undefined;

console.log(a == null);      // true
console.log(b == null);      // true
console.log(a === null);     // true
console.log(b === null);     // false

boolean

boolean содержит только два значения: true и false. В условиях выполняется приведение к булеву значению (truthy/falsy).

Пример: Boolean("x").

console.log(typeof true);         // "boolean"
console.log(Boolean(0));          // false
console.log(Boolean(""));         // false
console.log(Boolean("text"));     // true
console.log(Boolean([]));         // true
console.log(Boolean({}));         // true
Объект-обертка new Boolean(false) является объектом и ведет себя как truthy в условиях, что часто вызывает ошибки.
const b = new Boolean(false);

console.log(typeof b); // "object"
if (b) {
  console.log("Сработает, потому что это объект"); 
}

number

number — единственный “обычный” числовой тип: целые и дробные значения, а также специальные значения NaN, Infinity, -Infinity. В основе лежит формат IEEE 754 двойной точности, что объясняет ошибки округления.

Примеры: Number.isNaN(NaN) и 0.1 + 0.2.

console.log(typeof 123);        // "number"
console.log(typeof 3.14);       // "number"

console.log(0.1 + 0.2);         // 0.30000000000000004
console.log(Number.isNaN(NaN)); // true
console.log(NaN === NaN);       // false

console.log(1 / 0);             // Infinity
console.log(-1 / 0);            // -Infinity
console.log(Object.is(-0, 0));  // false (в JS существуют +0 и -0)

Для денежных расчетов часто выбирается подход “хранить в целых минимальных единицах” (например, копейки) или использовать bigint там, где нужна целочисленная точность и нет дробной части.

bigint

bigint предназначен для целых чисел произвольной длины. Литералы пишутся с суффиксом n. bigint не смешивается с number в арифметике без явного преобразования.

Примеры: 9007199254740993n и typeof 1n.

const safeLimit = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(safeLimit + 1);                // 9007199254740992
console.log(safeLimit + 2);                // 9007199254740992 (теряется точность)

const big = 9007199254740993n;
console.log(big + 2n);                     // 9007199254740995n
console.log(typeof big);                   // "bigint"
Операции между number и bigint выбрасывают ошибку TypeError.
const a = 10n;
const b = 2;

// console.log(a + b); // TypeError: Cannot mix BigInt and other types

console.log(a + BigInt(b)); // 12n
console.log(Number(a) + b); // 12 (возможна потеря точности при больших значениях)

string

string хранит текст. Отдельного типа “символ” нет: один символ — это строка длины 1. Строки являются неизменяемыми (immutable): изменение символа “внутри” строки невозможно, создается новая строка.

Примеры: "a".length и "a"[0].

const s = "hello";
console.log(typeof s);     // "string"
console.log(s.length);     // 5
console.log(s[0]);         // "h"

const t = "h" + "i";
console.log(t);            // "hi"
Работа с “символами” Unicode может быть нетривиальной: некоторые визуальные символы занимают два 16-битных элемента (суррогатные пары), поэтому length не всегда равен числу видимых символов.

symbol

symbol — уникальный примитив, чаще всего применяемый как ключ свойства, чтобы избежать конфликтов имен. Символы уникальны даже при одинаковом описании.

Примеры: Symbol("id") и sym1 === sym2.

const sym1 = Symbol("id");
const sym2 = Symbol("id");

console.log(typeof sym1);       // "symbol"
console.log(sym1 === sym2);     // false

const user = {
  name: "Ann",
  [sym1]: 123
};

console.log(user.name);         // "Ann"
console.log(user[sym1]);        // 123
console.log(Object.keys(user)); // ["name"] (символьный ключ не перечисляется через keys)

Существует глобальный реестр символов через Symbol.for("key"), в котором одинаковый ключ возвращает один и тот же символ.

const a = Symbol.for("shared");
const b = Symbol.for("shared");
console.log(a === b); // true

object

object — непримитивный тип: значения имеют идентичность (ссылку), свойства и прототип. Сюда относятся обычные объекты, массивы, функции, даты, коллекции и пользовательские экземпляры классов.

Примеры: typeof {} и Array.isArray([]).

const obj = { a: 1 };
const arr = [1, 2, 3];
function fn() {}

console.log(typeof obj); // "object"
console.log(typeof arr); // "object"
console.log(typeof fn);  // "function"

console.log(Array.isArray(arr)); // true
console.log(arr instanceof Array); // true

Ключевая особенность объектов — сравнение по ссылке, а не по содержимому.

const a = { x: 1 };
const b = { x: 1 };
const c = a;

console.log(a === b); // false (разные объекты)
console.log(a === c); // true  (одна и та же ссылка)

Важные проверки типов на практике

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

// typeof для примитивов
console.log(typeof undefined); // "undefined"
console.log(typeof null);      // "object" (нюанс)
console.log(typeof true);      // "boolean"
console.log(typeof 1);         // "number"
console.log(typeof 1n);        // "bigint"
console.log(typeof "x");       // "string"
console.log(typeof Symbol());  // "symbol"

// typeof для объектов
console.log(typeof {});        // "object"
console.log(typeof []);        // "object"
console.log(typeof (() => {})); // "function"

Для различения “обычный объект vs массив vs дата” часто применяются:

  • Array.isArray(value) для массивов
  • value instanceof Date для дат (в рамках одного глобального контекста)
  • Object.prototype.toString.call(value) как более универсальный способ
function tagOf(v) {
  return Object.prototype.toString.call(v);
}

console.log(tagOf([]));            // "[object Array]"
console.log(tagOf({}));            // "[object Object]"
console.log(tagOf(new Date()));    // "[object Date]"
console.log(tagOf(null));          // "[object Null]"
console.log(tagOf(undefined));     // "[object Undefined]"

Примитивы vs объекты: копирование и изменение

Примитивные значения копируются “как значение”, а объекты передаются как ссылка на один и тот же объект. Это особенно важно при присваивании и передаче в функции.

// Примитив: копия значения
let a = 10;
let b = a;
b = 20;

console.log(a); // 10
console.log(b); // 20

// Объект: копия ссылки
let o1 = { count: 1 };
let o2 = o1;
o2.count = 2;

console.log(o1.count); // 2
console.log(o2.count); // 2

Чтобы получить “поверхностную” копию объекта, часто используется structuredClone (глубокая копия, где поддерживается) или комбинации вроде Object.assign/spread для поверхностной копии.

Поверхностная копия не копирует вложенные объекты: вложенные ссылки остаются общими.
const original = { inner: { x: 1 } };
const shallow = { ...original };

shallow.inner.x = 999;

console.log(original.inner.x); // 999 (вложенный объект общий)

Частые ошибки начинающих

  • Ожидание, что null даст typeof null === "null", хотя фактически возвращается "object"
  • Попытка хранить “большие целые” в number без потери точности, хотя безопасный диапазон ограничен Number.MAX_SAFE_INTEGER
  • Смешивание bigint и number в арифметике без преобразования
  • Ожидание, что массив — отдельный тип, хотя он является объектом и проверяется через Array.isArray
  • Использование объектов-оберток new Number(1), new String("x"), new Boolean(false) вместо примитивов

Кратко: В JavaScript существует 8 типов данных: 7 примитивов (undefined, null, boolean, number, bigint, string, symbol) и 1 непримитивный (object). Массивы и функции относятся к object (функции имеют typeof === "function"), а typeof null — историческое исключение, возвращающее "object".