Menu
Русский

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

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

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

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

index.js
Output
Click Run to see the output here.

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

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

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

index.js
Output
Click Run to see the output here.

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

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

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

index.js
Output
Click Run to see the output here.

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

index.js
Output
Click Run to see the output here.

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

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

index.js
Output
Click Run to see the output here.

С примитивами 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

НАЧАТЬ