Menu

Примитивные типы в JavaScript: 7 базовых значений

Разбираем семь примитивных типов JavaScript - string, number, bigint, boolean, null, undefined и symbol - и показываем, чем они отличаются от объектов.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Семь примитивов и всё остальное

В JavaScript все значения делятся на два лагеря. С одной стороны - семь примитивных типов: простые неизменяемые значения. С другой - объекты: всё составное, изменяемое и вызываемое. По сути, это и есть вся система типов данных JavaScript на уровне значений.

Семь примитивных типов JavaScript:

Всё, чего нет в этом списке, - массивы, функции, даты, регулярки, обычные {} - это объекты. typeof показывает тип во время выполнения, и в последней строке вас ждёт знаменитая бородавка языка. typeof null возвращает 'object' аж с 1995 года, и чинить это уже никто не будет: слишком много кода на этом держится.

Примитив - это значение, а не коробка

Самая полезная ментальная модель такая: примитив - это и есть само значение. Число 3 - это не коробочка, внутри которой лежит тройка, это просто 3. Две переменные со значением 3 хранят одно и то же значение, а не две копии, указывающие на что-то общее:

Примитивы сравниваются по значению, а объекты - по ссылке. Именно из-за этой разницы потом возникает куча недоумений в духе «почему тут false?», особенно когда пытаешься сравнить массивы или объекты через ===.

Неизменяемость примитивов в JS

Примитив изменить нельзя. Любая операция, которая выглядит как изменение значения, на самом деле возвращает новое:

Первый вызов создаёт новую строку и тут же её выбрасывает - возвращаемое значение никто не подхватил. Второй перезаписывает name. Исходная "ada" не изменилась - да и не могла. С числами та же история: x + 1 даёт новое число, а не мутирует x.

Именно поэтому const для строки или числа - реально безопасная штука. Само значение поменять нельзя, а const не даёт переприсвоить переменную. В этом и суть неизменяемости примитивов в JS.

Числа, BigInt и зачем их два

Тип number в JavaScript - это 64-битное число с плавающей точкой. Отсюда и быстрая арифметика, и потолок: целые представляются точно только до Number.MAX_SAFE_INTEGER (2^53 - 1):

За этой границей целые числа начинают «сталкиваться». Для чисел, которые должны оставаться точными при любом размере, есть bigint. Записывается он с суффиксом n:

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

Строки - это тоже примитивы

Строка в JavaScript - это примитивный тип, а не объект, хотя у неё и есть методы вроде .length, .slice, .toUpperCase:

Под капотом, когда вы вызываете метод у строки, JavaScript на мгновение оборачивает её в объект String, чтобы вызов метода сработал, а затем выбрасывает обёртку. Думать об этой обёртке не нужно - достаточно помнить, что строки ведут себя как значения (неизменяемы, сравниваются по значению), даже несмотря на богатый набор методов.

Одинарные кавычки, двойные кавычки и обратные кавычки создают один и тот же тип. Обратные кавычки дополнительно поддерживают интерполяцию и многострочные литералы - об этом в следующей статье.

Разница между null и undefined

Два примитива обозначают «нет значения», но взаимозаменяемыми их считать нельзя.

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

null - это то, что пишете вы сами, когда хотите явно сказать «здесь намеренно пусто»:

Примерно так сложилось по соглашению: undefined - это способ самого языка сказать «тут ничего нет», а null - способ самого программиста. Оба значения ложные (falsy), оба проваливают сравнение с обычными значениями, и каждому из них ниже будет посвящена отдельная страница.

Symbol: гарантированно уникальные значения

symbol - самый редко используемый примитив в JavaScript. Каждый созданный символ уникален, даже если два из них описаны одной и той же строкой:

Символы удобны в роли ключей объекта, которые гарантированно ни с чем не столкнутся: библиотека может повесить на ваш объект метаданные через символ и быть уверенной, что никакой другой код их не перезапишет. Вы ещё встретите их, когда дойдёте до итераторов и well-known символов вроде Symbol.iterator.

Проверка типов во время выполнения

В большинстве случаев выручает typeof. Но есть пара особенностей, о которых стоит помнить:

Для null сравнивайте напрямую: value === null. Для массивов - Array.isArray(value). Универсального способа «а это вообще примитив?» в языке нет, но привычная идиома выглядит достаточно понятно:

Примитивы и объекты: подвох при присваивании

Прежде чем двигаться дальше, стоит разобрать ещё один момент. Примитивы - это значения, а объекты - ссылки, поэтому присваивание у них работает по-разному:

С примитивами b = a копирует значение. А вот с объектами y = x копирует ссылку - оба имени указывают на один и тот же объект в памяти. Изменяешь через одно имя - меняется и то, что видно через другое. Именно отсюда растут ноги у большинства багов вида «стоп, а почему оно поменялось?» в JavaScript.

Что важно запомнить

  • Семь примитивных типов: string, number, bigint, boolean, null, undefined, symbol. Всё остальное - объекты.
  • Примитивы неизменяемы и сравниваются по значению; объекты изменяемы и сравниваются по ссылке.
  • typeof возвращает тип во время выполнения, и тут есть две особенности, которые стоит заучить: typeof null === "object" и typeof function === "function".
  • number - это 64-битное число с плавающей точкой с ограничением по безопасным целым; для точных целых за этой границей есть bigint.

Дальше: строки и шаблонные литералы

Строки - тот примитив, с которым вы будете работать чаще всего, а шаблонные литералы (те самые строки в обратных кавычках) делают сборку строк безболезненной: интерполяция, многострочный текст, теговые шаблоны. Об этом - на следующей странице.

Часто задаваемые вопросы

Сколько примитивных типов в JavaScript?

Семь: string, number, bigint, boolean, null, undefined и symbol. Всё остальное - массивы, функции, даты, обычные объекты - это объекты. Тип значения в рантайме можно узнать через typeof, но помните про историческую особенность: typeof null возвращает 'object'.

В чём разница между примитивом и объектом в JavaScript?

Примитивы неизменяемы и сравниваются по значению - две тройки 3 это одна и та же тройка. Объекты изменяемы и сравниваются по ссылке - два {} это два разных объекта, даже если выглядят одинаково. При присваивании примитив копируется по значению, а объект - по ссылке на те же данные.

Примитивы в JavaScript действительно неизменяемы?

Да. Изменить примитив на месте нельзя: 'hello'.toUpperCase() возвращает новую строку, а не меняет исходную. Когда вы пишете x = x + 1, переменная просто начинает указывать на другой примитив - само значение не трогается. Именно поэтому const name = 'Ada' не мешает создавать новые строки на основе name.

Почему typeof null возвращает 'object'?

Это баг из самой первой реализации 1995 года. Чинить его не стали - слишком много кода уже полагалось на такое поведение. Чтобы проверить на null, используйте строгое сравнение: value === null. Для undefined подойдёт value === undefined или typeof value === 'undefined'.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ