Перейти к основному содержимому

Типы данных в JavaScript

Разработчику Архитектору

Дальше: Работа с объектами и прототипами · Справочник JavaScript · Преобразования типов (ниже в этой статье)


Типы данных в JS​

Типы данных и типизация​

Общие определения — типы данных и типизация в базовом разделе энциклопедии.

Переменные и константы в процессе использования получают значение. Для возраста (age) ожидают число; для имени (name) — последовательность символов. Тип подсказывает среде выполнения, как интерпретировать байты в памяти и какие операции допустимы.

★ JavaScript — язык с динамической и слабой типизацией: тип связывается со значением в момент присваивания, а не при объявлении имени; одно имя в разных местах программы может указывать на значения разных типов. Неявные преобразования (coercion) допускают, например, "5" + 2 → "52" и "5" - 2 → 3. Для сравнения значения и типа используют === (см. таблицу =, ==, ===). Статическая проверка возможна поверх JS через TypeScript (постепенная типизация).

Объявление без аннотации типа: let x; при x = 10 объект имеет тип Number, при x = "текст" — String.

Типы в JS делятся на примитивные (одно значение) и ссылочные (объекты, массивы, функции).

Play ITЗагрузка интерактивного демо…


Play ITЗагрузка интерактивного демо…


Примитивные типы​

ТипОписаниеПример
NumberЧисла (целые и дробные)let age = 25;
StringСтроки (текст)let name = "Анна";
BooleanЛогический (true / false)let isStudent = true;
UndefinedЗначение не присвоеноlet x;
NullПустое значениеlet y = null;
SymbolУникальный идентификаторconst id = Symbol("id");

Как можно понять, примитивные используются для простых данных, когда это какое-то имя, число, текст, или просто "флажок" вроде "истина" или "ложь".


Число​

Число (Number). Тип Number представляет как целые, так и вещественные числа. Внутренне используется формат IEEE 754 (64-битное число с плавающей точкой). Поддерживает специальные значения — Infinity, -Infinity, NaN.

<переменная> = <числовое_значение>;
<переменная> = <арифметическое_выражение>;
<переменная> = parseFloat(<строка>);
<переменная> = parseInt(<строка>, <основание>);
<переменная> = Math.<функция>(<аргументы>);

Простой пример:

let age = 30;

Разбор:

  • let объявляет переменную с блочной областью видимости.
  • age получает числовое значение 30, то есть тип number определяется автоматически.
  • В JavaScript не требуется явно писать тип переменной, это делает движок во время выполнения.
  • Значение можно позже изменить, потому что используется let, а не const.
  • Такая форма подходит для простых скалярных данных, которые участвуют в арифметике и сравнениях.
  • Сам фрагмент минимален, но демонстрирует базовую идею динамической типизации.

Сложный пример:

const totalPrice = items.reduce((sum, item) => sum + (item.price * item.quantity), 0)
.toFixed(2);
const finalPrice = parseFloat(totalPrice) + (hasDiscount ? -totalPrice * 0.1 : 0);

Здесь значение переменной finalPrice формируется через цепочку операций — агрегация массива объектов (reduce), округление до двух знаков после запятой (toFixed — возвращает строку), преобразование строки обратно в число (parseFloat) и условная скидка. Результат — число, но его получение требует нескольких этапов обработки.


Булево​

Булево (Boolean) принимает два значения: true и false. Также может быть получен неявно через приведение типов (например, !!"text" → true).

<переменная> = true | false;
<переменная> = <логическое_выражение>;
<переменная> = Boolean(<значение>);
<переменная> = !<значение>;

Простой пример:

let isActive = true;

Сложный пример:

const isEligible = user.age >= 18 &&
user.hasVerifiedEmail &&
(user.permissions.includes('read') || user.role === 'admin');

Разбор:

  • Объявляется константа isEligible, в которой сохраняется итог логической проверки доступа.
  • Оператор >= проверяет возрастной порог, затем && объединяет обязательные условия.
  • user.permissions.includes('read') проверяет наличие права в массиве разрешений.
  • Через || задаётся альтернативный путь: роль admin также даёт доступ.
  • Круглые скобки фиксируют приоритет вычисления для прав и роли, делая выражение читаемым.
  • Результат всей цепочки всегда булев — true, если все требования выполнены, иначе false.
  • Это типичный паттерн guard-условия для авторизации в приложениях.

Переменная isEligible принимает булево значение, вычисленное на основе нескольких условий: возраст, статус верификации и права доступа. Это типичный случай использования логических выражений для контроля доступа.


Строки​

Строка (String). Тип String представляет собой последовательность символов UTF-16. Может создаваться как строковый литерал или через конструктор String(). Строки неизменяемы: любая "правка" возвращает новую строку, исходный литерал остаётся прежним. Частые методы (includes, slice, replace, split, trim и др.) — в разделе методы строк ниже; полный перечень — в встроенных функциях и справочнике.

<переменная> = "<текст>";
<переменная> = `<шаблонная_строка с ${выражением}>`;
<переменная> = String(<значение>);
<переменная> = <строка>.<метод>(<аргументы>);

Простой пример:

let name = "Тимур";

Сложный пример:

const greeting = `Добро пожаловать, ${user.name || 'Гость'}!
Вы вошли ${new Date().toLocaleDateString('ru-RU')} в ${new Date().getHours()}:${String(new Date().getMinutes()).padStart(2, '0')}.
Ваш баланс: ${(user.balance ?? 0).toFixed(2)} ₽.`;

Выше используется шаблонная строка с интерполяцией значений из объекта, операторами нулевого слияния (??) и логического ИЛИ (||), форматированием времени и числовым округлением. Результат — многострочная строка, собранная динамически.

Методы для работы со строками​

У примитива string и объекта-обёртки String десятки методов на прототипе String.prototype. В повседневном коде чаще всего нужны поиск и сравнение, срезы и разбиение, замена, регистр и обрезка пробелов, дополнение длины. Вызов через точку: "Hello".includes("ell"). Методы возвращают новую строку (или число / булево / массив), оригинал не меняется.

Три метода на каждый день

charAt(4) — символ по индексу ("Hello".charAt(4) → "o"). includes("ell") — есть ли подстрока (true / false). slice(1, 3) — фрагмент без изменения исходной строки ("el"). Ниже — шпаргалка по группам задач; полный перечень — в встроенных функциях и справочнике String.

Для шаблонов с регулярными выражениями (match, search, replace с RegExp) — отдельно регулярные выражения.

Доступ по индексу​

Индексация с нуля: "Hello"[0] → "H". Удобные методы:

СпособПримерРезультат
charAt(index)"Hello".charAt(4)"o"
at(index)"Hello".at(-1)"o" (отрицательные индексы с конца, ES2022)
codePointAt(pos)"Hello".codePointAt(1)101 (код символа e)

charAt за пределами строки вернёт пустую строку; at — undefined.

Шпаргалка — частые методы (на "Hello")​

Поиск и проверка​
МетодНазначениеПримерРезультат
includes(search)Есть ли подстрока"Hello".includes("x")false
startsWith(search)Начинается с…"Hello".startsWith("H")true
endsWith(search)Заканчивается на…"Hello".endsWith("o")true
indexOf(search)Индекс первого вхождения"Hello".indexOf("l")2
lastIndexOf(search)Индекс последнего вхождения"Hello".lastIndexOf("l")3
search(regexp)Индекс по шаблону"Hello".search("e")1
match(regexp)Совпадения RegExp"Hello".match(/[A-Z]/g)["H"]
Изменение, регистр и склейка​
МетодНазначениеПримерРезультат
concat(...str)Склеить строки"Hello".concat(" ", "world")"Hello world"
repeat(count)Повторить N раз"Hello".repeat(3)"HelloHelloHello"
replace(search, new)Замена первого совпадения"Hello".replace("llo", "y")"Hey"
replaceAll(search, new)Замена всех совпадений"Hello".replaceAll("l", "*")"He**o"
toLowerCase()Нижний регистр"Hello".toLowerCase()"hello"
toUpperCase()Верхний регистр"Hello".toUpperCase()"HELLO"
Дополнение длины и обрезка пробелов​
МетодНазначениеПримерРезультат
padStart(len, pad)Дополнить слева"Hello".padStart(8, "*")"***Hello"
padEnd(len, pad)Дополнить справа"Hello".padEnd(8, "*")"Hello***"
trim()Убрать пробелы с краёв" Hello ".trim()"Hello"
trimStart()Убрать пробелы в начале" Hello ".trimStart()"Hello "
trimEnd()Убрать пробелы в конце" Hello ".trimEnd()" Hello"
Срезы и разбиение​
МетодНазначениеПримерРезультат
slice(start, end)Срез (конец не входит)"Hello".slice(1, 3)"el"
substring(start, end)Подстрока между индексами"Hello".substring(2, 4)"ll"
split(separator)Разбить в массив"Hello".split("")["H","e","l","l","o"]
Сравнение​
МетодНазначениеПримерРезультат
localeCompare(other)Порядок с учётом локали"Hello".localeCompare("Héllo")-1

Оператор in для подстрок нет (он для ключей объектов). Проверка вхождения — includes или indexOf(...) !== -1.

Поиск и сравнение​

const text = "Hello";

console.log(text.includes("ell")); // true
console.log(text.startsWith("He")); // true
console.log(text.indexOf("l")); // 2
console.log(text.lastIndexOf("l")); // 3
console.log(text.search("e")); // 1 — как indexOf, но аргумент часто RegExp

indexOf и lastIndexOf возвращают -1, если подстрока не найдена. Для сортировки массивов строк по алфавиту с учётом языка — localeCompare в функции сравнения: (a, b) => a.localeCompare(b, "ru").

Срезы и разбиение​

slice(start, end) поддерживает отрицательные индексы; end не включается. substring при отрицательных аргументах ведёт себя иначе (их обнуляет) — для срезов чаще берут slice.

const word = "Hello";

console.log(word.slice(1, 3)); // "el"
console.log(word.slice(-2)); // "lo" — последние два символа
console.log(word.split("")); // массив символов
console.log("a,b,c".split(",")); // ["a", "b", "c"]

Собрать массив обратно в строку — arr.join(separator): ["a", "b"].join("-") → "a-b".

Замена и склейка​

replace с строкой в первом аргументе меняет только первое совпадение; для всех — replaceAll (ES2021) или replace с RegExp и флагом g.

console.log("Hello".replace("l", "L")); // "HeLlo"
console.log("Hello".replaceAll("l", "L")); // "HeLLo"
console.log("file.txt".replace(/\.txt$/, ".md")); // "file.md"

Для простой склейки удобнее шаблонные строки `${a}${b}`; concat остаётся в старом коде и цепочках вызовов.

Регистр, пробелы, длина​

const raw = " Hello World ";

console.log(raw.trim()); // "Hello World"
console.log(raw.trimStart()); // "Hello World "
console.log("42".padStart(5, "0")); // "00042"
console.log("hi".repeat(3)); // "hihihi"

Свойство length — число кодовых единиц UTF-16, а не всегда "видимых" символов: эмодзи и некоторые буквы могут занимать два индекса. Для перебора по символам Unicode — for (const ch of str) или Array.from(str).

Регулярные выражения на строках​

МетодНазначение
match(regexp)Первое совпадение или все при флаге g
matchAll(regexp)Итератор всех совпадений (нужен флаг g)
search(regexp)Индекс первого совпадения
console.log("Hello".match(/[A-Z]/g)); // ["H"]

Подробнее — RegExp в JavaScript.


Undefined, Null, Symbol​

Отдельно важно выделить symbol - в JS они особые, и не относятся к "тексту" вроде строки. Это ярлык, который можно использовать как ключ для свойств объекта, как наклейка на шкаф. Каждый Symbol — уникален, даже если у них одинаковое описание. Даже если два символа называются "id", они не равны. Это как два разных браслета с одинаковой гравировкой — выглядят одинаково, но физически разные.


Undefined​

<переменная>; // без присваивания
<переменная> = undefined;

Null​

<переменная> = null;

Symbol​

<переменная> = Symbol(<описание>);
<объект>[<символ>] = <значение>;

Представим наглядно.

У нас есть объект user, и мы ему решили добавить свойство, сразу в формате строки:

user.id = "12345";

И в дальнейшем, эту переменную id можно перезаписать, заменив значение - если кто-то укажет user.id = значение, то наше изначальное значение будет перезаписано:

user.id = 23456;

И если мы не хотим, чтобы другие скрипты перезаписали случайно наше значение, можно использовать отдельный ключ типа Symbol (не путать со строковым свойством "id"):

const id = Symbol("id");

const user = {};
user[id] = 12345;

// user.id = 999; // это другое свойство (строка "id"), Symbol не затронет
// user[id] = 999; // перезапишет только тот, кто знает ссылку id

console.log(user[id]); // 12345

Символ не появится в for...in и Object.keys(), но доступен по ссылке id. Его нельзя неявно привести к строке (String(id) выбросит ошибку).


Дата и время (Date)​

Объект Date хранит момент времени (миллисекунды с 01.01.1970 UTC). Это ссылочный тип, хотя часто передают как значение через ISO-строки в JSON.

const now = new Date();
const birthday = new Date(2000, 4, 15); // месяц 4 = май (0-based)
const parsed = new Date('2026-05-30T12:00:00Z');

console.log(now.toISOString()); // UTC для API
console.log(now.toLocaleDateString('ru-RU')); // локальный вывод
console.log(now.getFullYear(), now.getMonth() + 1, now.getDate());
ЗадачаAPI
Текущий моментnew Date()
Компоненты (локальные)getFullYear(), getMonth(), getDate(), getHours(), …
Изменить компонентsetFullYear(), setMonth(), …
Разница в мсdate2 - date1
Формат для APItoISOString()
Локальный форматtoLocaleString('ru-RU', options)

Таймеры (браузер и Node.js):

const timerId = setTimeout(() => console.log('раз'), 1000);
clearTimeout(timerId);

const intervalId = setInterval(() => console.log('тик'), 500);
clearInterval(intervalId);

Замер длительности — console.time('label') / console.timeEnd('label') или performance.now() (высокая точность, браузер и Node.js).

Temporal API

У Date есть исторические огрехи (месяцы с нуля, мутабельность, часовые пояса). Стандарт Temporal — будущая замена

обзор — в ES-модули и Temporal.

Практика форматирования дат в CLI — простые приложения.

Но в ряде случаев можно ссылаться к каким-то сложным объектам, как мы ранее уже упоминали – это ссылочные типы.


Ссылочные типы​

ТипОписаниеПример
ObjectОбъект (ключ: значение). JSON (JavaScript Object Notation) — текстовый формат обмена, синтаксически похожий на литералы объектов JS, но это не "тот же язык" (нет функций, undefined и т.д.).const user = { name: "Анна" };
MapКоллекция пар "ключ–значение"; ключ любого типа, порядок вставки сохраняетсяconst m = new Map([["a", 1]]);
ArrayМассив (список значений)const nums = [1, 2, 3];
FunctionФункцияfunction greet() { ... }

Объект имеет какие-то свойства и значения, допустим name – свойство объекта user. Объект, как мы ранее выяснили, может иметь и свои функции – это будут методы.


Play ITЗагрузка интерактивного демо…


Объект​

Объект — это коллекция пар "ключ-значение". Является ссылочным типом. Все не-примитивы в JS являются объектами (включая массивы и функции). Свойства можно добавлять динамически — после создания объекта в него допускается записывать новые пары ключ-значение.

<переменная> = { <ключ>: <значение>, ... };
<переменная> = new Object();
<переменная> = Object.<статический_метод>(<аргументы>);
<переменная>.<свойство> = <значение>;
<переменная>.<метод>(<аргументы>);

Простой пример — создание объекта с вложенным объектом и методом:

const person = {
name: "Иван",
age: 30,
address: {
city: "Москва",
zip: "101000"
},
sayHello: function() {
console.log("Привет!");
}
};

Значение свойства может быть примитивом, другим объектом или функцией. Функция внутри объекта становится методом — её вызывают через person.sayHello().

Сложный пример:

const config = Object.freeze({
apiUrl: process.env.API_URL || 'https://api.example.com',
timeout: parseInt(process.env.TIMEOUT || '5000', 10),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
retryCount: maxRetries > 0 ? Math.min(maxRetries, 5) : 0
});

Выше в примере мы видим, как создаётся объект конфигурации с использованием переменных окружения, преобразованием типов, условной логикой и вложенностью. Затем он замораживается (Object.freeze) для предотвращения изменений — типичная практика в продвинутых приложениях.

Операции с обычным объектом (литерал / Object):

ДействиеСинтаксис
Добавить или заменить свойствоobj[key] = value, Object.assign(obj, { ... })
Прочитатьobj.key, obj[key]
Удалитьdelete obj.key
Проверить ключkey in obj, Object.hasOwn(obj, key)
Ключи / значенияObject.keys(obj), Object.values(obj), Object.entries(obj)

Для частых операций "добавить / прочитать / удалить по ключу" с произвольными ключами и предсказуемым порядком перебора удобнее встроенный Map — см. раздел Map ниже.

Доступ к свойствам​

Существуют две основные нотации чтения и записи.

Точечная нотация (dot notation) — когда имя свойства известно заранее и является валидным идентификатором:

console.log(person.name); // "Иван"
console.log(person.address.city); // "Москва"

Скобочная нотация (bracket notation) — когда ключ хранится в переменной, содержит пробелы или спецсимволы:

console.log(person["age"]); // 30

const field = "name";
console.log(person[field]); // "Иван"

Скобочная форма обязательна для динамических ключей; точечная короче для фиксированных имён.

Изменение и добавление свойств​

Существующее значение перезаписывается присваиванием:

person.age = 31;

Новое поле добавляется тем же синтаксисом — если ключа ещё нет, он создаётся:

person.job = "Разработчик";

Деструктуризация объекта​

Деструктуризация извлекает свойства объекта в отдельные переменные. Подробнее о синтаксисе — в операторах и выражениях; для массивов — в переменных и областях видимости.

const { name, age } = person;
console.log(name); // "Иван"
console.log(age); // 31

Переименование и значения по умолчанию:

const { name: userName, country = "Россия" } = person;

Методы объекта​

Функция, записанная как свойство объекта, — метод. Внутри обычной функции-метода ключевое слово this ссылается на сам объект:

const greeter = {
name: "Иван",
sayHello: function() {
console.log("Привет, " + this.name + "!");
}
};

greeter.sayHello(); // "Привет, Иван!"

Для создания метода (функции, являющейся свойством объекта) в JavaScript существует несколько стандартных синтаксических конструкций.

Object Literal:

Код ITЗагрузка примера кода…

Важно про this: Внутри методов, объявленных как function() или через короткую запись name(), значение this динамически привязывается к вызывающему объекту (user). Это позволяет обращаться к другим свойствам того же объекта.

Если объект был создан ранее, метод можно добавить, присвоив ему функцию.

Код ITЗагрузка примера кода…

Использование конструктора (Function Constructor) - ещё один способ добавления методов к объектам. Этот подход используется при создании множества одинаковых объектов. Методы определяются внутри конструктора или на прототипе.

function Car(brand) {
this.brand = brand;

// Метод, созданный внутри конструктора (неэффективно для больших объемов,
// так как создается новая копия функции для каждого экземпляра)
this.start = function() {
console.log(`${this.brand} едет`);
};
}

const myCar = new Car("BMW");
myCar.start();

Лучшая практика: Для оптимизации памяти методы следует добавлять в прототип объекта, а не внутрь конструктора. Тогда один экземпляр функции будет использоваться всеми объектами.

function Animal(name) {
this.name = name;
}

// Добавляем метод в прототип
Animal.prototype.speak = function() {
console.log(`${this.name} говорит`);
};

const dog = new Animal("Собачка");
dog.speak(); // Собачка говорит

Использование классов (ES6 Classes) - синтаксический сахар над прототипами, обеспечивающий более чистый и понятный код для объектно-ориентированного программирования.

Код ITЗагрузка примера кода…

Объекты могут быть базовыми, DOM, пользовательскими и глобальным.


Map​

Map — встроенная структура данных для хранения пар "ключ–значение". Ключом может быть что угодно (число, объект, Symbol), порядок элементов совпадает с порядком добавления. Теория словаря и хеш-таблиц — в структурах данных; здесь — практика в JavaScript.

Когда выбирать Map​

  • ключи — объекты, DOM-узлы, массивы или другие ссылочные значения;
  • важен порядок вставки при обходе;
  • нужен быстрый доступ к количеству записей через map.size;
  • одни и те же ключи многократно добавляются и удаляются (кэш, индексы, метаданные сущностей).

Для JSON-подобных записей с фиксированным набором строковых полей достаточно литерала { ... } из раздела Объект.

Создание​

const empty = new Map();
const fromPairs = new Map([
["host", "api.example.com"],
[443, "https"],
]);

const cacheKey = { id: 1001 };
const cache = new Map();
cache.set(cacheKey, { name: "Анна", role: "admin" });

Конструктор принимает итерируемое из пар [ключ, значение]. Литерал { a: 1 } в new Map(...) передать нельзя — нужен массив пар или Object.entries(obj).

Операции​

ДействиеAPI
Добавить или заменитьmap.set(key, value) — возвращает map (цепочка вызовов)
Прочитатьmap.get(key) — undefined, если ключа нет
Проверить ключmap.has(key)
Удалитьmap.delete(key) — true, если запись была
Очистить всёmap.clear()
Число записейmap.size (свойство, не метод)

Методы перебора​

МетодОписание
keys()итератор ключей в порядке вставки
values()итератор значений
entries()итератор пар [ключ, значение] — то же, что [Symbol.iterator]()
forEach(callback)обход; аргументы колбэка: (value, key, map)
const roles = new Map([
["admin", "full"],
["guest", "read"],
]);

for (const [role, level] of roles) {
console.log(role, level);
}

roles.forEach((level, role) => {
console.log(`${role}: ${level}`);
});

Map итерируем: его можно передать в for...of, развернуть через [...map] или собрать массив ключей через Array.from(map.keys()).

Справочник в энциклопедии

Сравнение ключей (NaN, +0 / -0), отличия от Object, статический Map.groupBy и родственный Set — в справочнике JavaScript.


Глобальный объект​

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

<глобальное_свойство> = <значение>; // без объявления (не рекомендуется)
window.<свойство> = <значение>; // в браузере
global.<свойство> = <значение>; // в Node.js
globalThis.<свойство> = <значение>; // универсально

Какой именно объект является глобальным, зависит от окружения:

ОкружениеГлобальный объект
Браузерwindow
Node.jsglobal
Современный JS (в любом окружении)globalThis

Как это работает? Такой глобальный объект содержит встроенные глобальные переменные, функции и объекты:

Свойства:

  • Infinity - бесконечность;
  • NaN - "не число";
  • undefined - значение undefined;
  • globalThis - ссылка на самого себя.

Встроенные функции:

  • isNaN() - проверяет, является ли значение NaN;
  • isFinite() - проверяет, является ли значение конечным числом;
  • parseFloat(), parseInt() - парсинг чисел;
  • encodeURI(), decodeURI() - кодировка URL;
  • eval() - выполнение строки как кода.

Встроенные конструкторы (которые на самом деле - функции):

  • Object - объект;
  • Array - массив;
  • String - строка;
  • Number - число;
  • Boolean - булево значение;
  • Function - функция;
  • Date - дата;
  • RegExp - регулярное выражение;
  • Symbol - уникальный идентификатор;
  • Error, TypeError, SyntaxError - ошибки.

Встроенные объекты:

  • Math - математические функции;
  • JSON - работа с JSON;
  • Promise - промисы (позже их изучим);
  • console - консоль.

Если объявить переменную без var, let, const в глобальной области - она станет свойством глобального объекта. То есть не пишите вроде x = 42, если не хотите создавать глобальное свойство - добавляйте ключевое слово let или const.

Важно — let, const, class не создают свойства в глобальном объекте, а var создаёт, как и вышеприведённый пример с x = 42. Полное сравнение трёх способов объявления — в переменных в JavaScript.


Play ITЗагрузка интерактивного демо…


Массив​

Массив же используется как набор данных, когда мы хотим расширить набор данных. К примеру, nums – набор чисел, включающий три значения – 1, 2 и 3. Каждый из этих элементов массива имеет индекс, что позволяет обращаться к конкретному значению, допустим, 2.

Массив (Array) — упорядоченная коллекция элементов. Является подтипом объекта, но имеет специфические методы (map, filter, reduce и т.д.).

<переменная> = [ <элемент>, ... ];
<переменная> = new Array(<размер> | <элементы>);
<переменная> = Array.<статический_метод>(<аргументы>);
<переменная>.<метод_массива>(<аргументы>);
<переменная>[<индекс>] = <значение>;

Простой пример:

let numbers = [1, 2, 3];

Сложный пример:

const filteredUsers = users
.filter(user => user.active)
.map(user => ({
...user,
fullName: `${user.firstName} ${user.lastName}`.trim(),
roleLabel: roleMap[user.role] || 'Неизвестно'
}))
.sort((a, b) => a.fullName.localeCompare(b.fullName));

Здесь выполняется фильтрация активных пользователей, маппинг с расширением данных (включая объединение полей и использование внешнего маппинга ролей), затем сортировка по алфавиту. Результат — новый массив объектов с расширенной информацией.

Операции Array:

ДействиеСинтаксис / метод
Добавить в конецpush(value), arr[arr.length] = value
Вставить по индексуsplice(index, 0, value)
Прочитатьarr[index]
Заменитьarr[index] = value
Удалить по индексуsplice(index, 1), pop(), shift()
Длинаarr.length

Методы в таблице выше (push, splice, присваивание по индексу) меняют тот же массив. В React, Redux и при передаче массива в дочерние функции это даёт неожиданные побочные эффекты: все ссылки на массив видят одно и то же изменение. Ниже — привычные иммутабельные варианты: исходный массив остаётся прежним, результат — новый.

Иммутабельные альтернативы мутирующим методам​

Девять частых операций и их безопасные аналоги. Слева — метод, который мутирует array; справа — выражение, которое возвращает новый массив (или копию для дальнейшей цепочки).

ЗадачаМутирует исходный массивИммутабельная альтернатива
Добавить в конецarray.push(item)[...array, item]
Удалить с концаarray.pop()array.slice(0, -1)
Удалить с началаarray.shift()array.slice(1)
Добавить в началоarray.unshift(item)[item, ...array]
Заполнить диапазонarray.fill(value, start?, end?)[...array].fill(value, start?, end?)
Вставить / удалить в серединеarray.splice(start, deleteCount?, ...items)array.toSpliced(start, deleteCount?, ...items) (ES2023)
Развернуть порядокarray.reverse()array.toReversed() (ES2023)
Сортироватьarray.sort(compareFn?)array.toSorted(compareFn?) (ES2023)
Скопировать участок внутри массиваarray.copyWithin(target, start, end?)[...array].copyWithin(target, start, end?)

Замена одного элемента по индексу без мутации — array.with(index, value) (ES2023) вместо array[index] = value:

const nums = [1, 2, 3];
const next = nums.with(1, 99); // [1, 99, 3]
// nums по-прежнему [1, 2, 3]
Поверхностная копия

Spread ([...array]) и slice копируют уровень массива: если внутри лежат объекты, ссылки на них общие. Для глубокого клонирования — ниже в статье или structuredClone в справочнике.

Методы map, filter, reduce, concat, slice изначально не меняют исходный массив — их удобно сочетать с иммутабельным обновлением state. Подробный разбор итерации и сортировки — в массивах и коллекциях; сравнение мутирующих и немутирующих API — в справочнике JavaScript.

Группировка по ключу​

Частая задача — разложить массив объектов по общему полю — задачи по проекту, заказы по статусу, пользователи по роли. Раньше для этого писали цикл или reduce. С ES2024 в стандарте есть Object.groupBy() — статический метод, который принимает любой итерируемый набор (массив, Set, генератор) и функцию-ключ.

const tasks = [
{ description: 'Task 1', tag: 'Tag 1', project: 'Project A' },
{ description: 'Task 2', tag: 'Tag 1', project: 'Project B' },
{ description: 'Task 3', tag: 'Tag 2', project: 'Project A' },
];

const tasksByProject = Object.groupBy(tasks, (task) => task.project);
console.log(tasksByProject);

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

{
'Project A': [
{ description: 'Task 1', tag: 'Tag 1', project: 'Project A' },
{ description: 'Task 3', tag: 'Tag 2', project: 'Project A' },
],
'Project B': [
{ description: 'Task 2', tag: 'Tag 1', project: 'Project B' },
],
}

Исходный tasks не меняется. Порядок групп совпадает с порядком первого появления ключа при обходе.

Сигнатура: Object.groupBy(items, callbackFn). Для каждого элемента вызывается callbackFn(element, index), возвращаемое значение приводится к строковому ключу свойства объекта (Symbol тоже допустим, но на практике чаще строки и числа, которые становятся строками "42").

Когда нужен не строковый ключ

Если ключ группы — объект, Map или что-то, что нельзя надёжно положить в обычный объект, используйте Map.groupBy() — тот же callback, но результат Map, где ключи сохраняют исходный тип. Подробнее — в справочнике (Object.groupBy, Map.groupBy).

Эквивалент на reduce (для старых сред или когда groupBy недоступен):

const tasksByProject = tasks.reduce((groups, task) => {
const key = task.project;
(groups[key] ??= []).push(task);
return groups;
}, {});

Object.groupBy и Map.groupBy поддерживаются в актуальных браузерах и в Node.js 21+; для legacy-сред есть полифилл в core-js.


Если нужно получить разницу между двумя массивами (что добавилось, что удалилось, что осталось), удобнее перевести их в Set:

function syncTags(oldTags, newTags) {
const prev = new Set(oldTags);
const curr = new Set(newTags);

return {
added: [...curr.difference(prev)],
removed: [...prev.difference(curr)],
unchanged: [...prev.intersection(curr)],
};
}

Такой подход лучше отражает логику множеств. Для старых окружений без Set.prototype.difference/intersection можно оставить тот же подход через filter и includes.


Функция​

Функция – это та сама функция, которые мы изучили ранее. Она тоже представляет собой тип данных.

Функция (Function) — это объект первого класса. Может быть передана как аргумент, возвращена из другой функции, храниться в переменной.

function <имя>(<параметры>) { <тело> }
<переменная> = function(<параметры>) { <тело> };
<переменная> = (<параметры>) => { <тело> };
<переменная> = new Function(<строковые_параметры>, <тело_в_виде_строки>);
<результат> = <функция>(<аргументы>);

Простой пример:

function greet(name) {
return `Привет, ${name}!`;
}

Сложный пример:

Код ITЗагрузка примера кода…

Здесь реализован паттерн каррирования: функция высшего порядка возвращает другую функцию. createValidator абстрагирует логику валидации, позволяя генерировать конкретные валидаторы. Это мощный инструмент функционального программирования.


Автоматические преобразования (coercion)​

JavaScript автоматически преобразует типы данных при выполнении операций. Это поведение называется приведением типов или автоматическим преобразованием.


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

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

Преобразование в число:

let str = "123";
let num = Number(str); // 123

let bool = true;
let numFromBool = Number(bool); // 1

let empty = "";
let numFromEmpty = Number(empty); // 0

Преобразование в строку:

let num = 456;
let str = String(num); // "456"

let bool = false;
let strFromBool = String(bool); // "false"

let obj = { name: "Джон" };
let strFromObj = String(obj); // "[object Object]"

Преобразование в булево значение:

let str = "текст";
let bool = Boolean(str); // true

let empty = "";
let boolEmpty = Boolean(empty); // false

let num = 0;
let boolNum = Boolean(num); // false

let obj = { name: "Джон" };
let boolObj = Boolean(obj); // true

Неявное преобразование типов​

Неявное преобразование происходит автоматически при выполнении операций.

Преобразование при сложении:

let result = 5 + "5"; // "55" - число преобразуется в строку
let another = "Цена: " + 100; // "Цена: 100"

Преобразование при сравнении (сводная таблица =, ==, === — выражения и операторы):

let isEqual = 5 == "5"; // true - неявное преобразование
let isStrictEqual = 5 === "5"; // false - строгое сравнение без преобразования

let isTrue = 0 == false; // true
let isFalse = 1 == true; // true

Преобразование при логических операциях:

let value = "привет" && 42; // 42 - первое истинное значение
let another = "" || "значение"; // "значение" - первое истинное значение

let result = 0 || null || undefined || "ок"; // "ок"

Таблица преобразований​

ЗначениеВ числоВ строкуВ булево
"" (пустая строка)0""false
"0"0"0"true
"123"123"123"true
00"0"false
11"1"true
NaNNaN"NaN"false
null0"null"false
undefinedNaN"undefined"false
true1"true"true
false0"false"false
{}NaN"[object Object]"true
[]0""true

Ложные значения (falsy values)​

В JavaScript существует шесть значений, которые преобразуются в false при булевом преобразовании:

  • false
  • 0
  • "" (пустая строка)
  • null
  • undefined
  • NaN

Все остальные значения преобразуются в true.

Примеры:

Код ITЗагрузка примера кода…


Практические примеры преобразований​

Проверка ввода пользователя:

Код ITЗагрузка примера кода…

Работа с формами:

Код ITЗагрузка примера кода…


Объекты как ссылочные типы​

В JavaScript объекты являются ссылочными типами данных. Это означает, что переменная хранит не само значение, а ссылку на место в памяти, где находится объект.


Разница между примитивными и ссылочными типами​

Примитивные типы хранят значение напрямую:

let a = 10;
let b = a;
b = 20;

console.log(a); // 10 - значение не изменилось
console.log(b); // 20

Ссылочные типы хранят ссылку на объект:

let obj1 = { name: "Джон" };
let obj2 = obj1;
obj2.name = "Мария";

console.log(obj1.name); // "Мария" - объект изменился
console.log(obj2.name); // "Мария"

Передача по ссылке​

При передаче объекта в функцию передаётся ссылка на объект, а не его копия:

function updateName(person) {
person.name = "Алекс";
}

const user = { name: "Джон" };
updateName(user);

console.log(user.name); // "Алекс" - объект изменился

Поверхностное копирование​

Поверхностное копирование создаёт новый объект, но вложенные объекты копируются по ссылке:

Код ITЗагрузка примера кода…


Глубокое копирование​

Глубокое копирование создаёт полностью независимую копию объекта со всеми вложенными объектами.

В современном JavaScript для обычных данных (объекты, массивы, Map, Set, Date) удобнее всего встроенный structuredClone (ES2022; Node.js 17+, актуальные браузеры). Подробный список поддерживаемых типов и опций — в справочнике JavaScript.

Код ITЗагрузка примера кода…

structuredClone копирует циклические ссылки и вложенные Map/Set, в отличие от JSON.parse(JSON.stringify(...)). Функции, Symbol, WeakMap/WeakSet и DOM-узлы через него клонировать нельзя — для таких случаев остаётся своя рекурсивная логика или поверхностное копирование отдельных полей.


Сравнение объектов​

Объекты сравниваются по ссылке, а не по содержимому:

Код ITЗагрузка примера кода…


Изменяемость объектов​

Объекты в JavaScript являются изменяемыми (mutable):

const user = {
name: "Джон",
age: 30
};

user.age = 31; // Можно изменить свойство
user.email = "john@example.com"; // Можно добавить новое свойство
delete user.age; // Можно удалить свойство

console.log(user);
// { name: "Джон", email: "john@example.com" }

Для создания неизменяемых объектов используется Object.freeze:

const frozenUser = Object.freeze({
name: "Джон",
age: 30
});

frozenUser.age = 31; // Не сработает в строгом режиме
frozenUser.email = "john@example.com"; // Не сработает

console.log(frozenUser.age); // 30 - значение не изменилось

Практические примеры работы со ссылками​

Клонирование массива объектов:

Код ITЗагрузка примера кода…

Функция, которая не изменяет исходный объект:

function updateUser(user, updates) {
return {
...user,
...updates
};
}

const originalUser = { id: 1, name: "Джон", age: 30 };
const updatedUser = updateUser(originalUser, { age: 31 });

console.log(originalUser.age); // 30 - оригинал не изменился
console.log(updatedUser.age); // 31 - новая версия с изменениями

Работа с вложенными структурами:

Код ITЗагрузка примера кода…


Как не путаться в типах на практике​

В учебных примерах типы выглядят очевидно, но в продукте значения часто приходят из формы, URL, localStorage и API. Там почти все сначала строка, и тип нужно привести явно.

Рабочий подход:

  1. Приняли данные.
  2. Привели типы в одном месте.
  3. Дальше в коде работаем уже с нормализованной структурой.
function normalizeUserInput(raw) {
return {
name: String(raw.name || "").trim(),
age: Number(raw.age),
isStudent: raw.isStudent === "true"
};
}

Почему это важно:

  • меньше скрытых багов из-за неявного coercion;
  • проще отлаживать и тестировать;
  • понятнее контракт между слоями приложения.

Читать дальше: