Попробуйте себя в топовых IT-профессиях и соберите первое портфолио. Бесплатный курс Попробуйте себя в топовых IT-профессиях и соберите первое портфолио. Бесплатный курс Учиться
Код Справочник по фронтенду
#статьи

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

Разбираемся, как JavaScript видит числа, строки и все остальное, пытаемся разобраться в его логике и не сойти с ума.

Иллюстрация: Оля Ежак для Skillbox Media

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

console.log("5" - 1); // 4
console.log("5" + 1); // 51
// Живите теперь с этим

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

Числа, строки, булевы значения, объекты и так далее — это разные типы данных. Каждый тип ведёт себя по-своему, и JavaScript обрабатывает их по-разному. Если не учитывать это, жизнь ваша будет полна странностей и неожиданностей.

В этой статье мы расскажем, какие типы данных есть в JavaScript, как они работают и как их правильно использовать на практике.

Содержание:


Какие типы данных есть в JavaScript

В JavaScript всего восемь типов данных. Все они делятся на две большие группы — примитивные и ссылочные.

Знать разницу между ними очень важно. Именно на этом строится логика JavaScript, и большинство неожиданных багов происходит из-за путаницы между этими типами.

Примитивы

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

Представьте себе: вы записали чей-то номер телефона на листок бумаги. Друг сделал себе копию, но потом зачем-то стёр последние цифры. Его копия изменилась, а на вашем листе всё осталось как было.

Вот пример:

let a = 10;
let b = a; // b -- это просто копия значения a
b = 20;
console.log(a); // 10
console.log(b); // 20

Значения a и b независимы. Изменения одной переменной никак не влияют на другую.

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

А вот ссылочные типы передаются по ссылке. Это как поделиться доступом к «Google Документу»: если кто-то внесет правки, их увидят сразу все.

let obj1 = { name: "Вася" };
let obj2 = obj1;

obj2.name = "Ася";

console.log(obj1.name); // "Ася"

Переменные obj1 и obj2 ссылаются на один и тот же объект в памяти. Изменили один — изменился и другой.

Семь примитивных типов данных

Семь из восьми типов данных в JavaScript — это примитивы. Они так называются, потому что это простые, базовые и неизменные единицы данных.

Вот список:

  • string — строки,
  • number — числа,
  • bigint — большие числа,
  • boolean — логические значения,
  • undefined — переменная есть, но значения нет,
  • null — значение должно быть пустым,
  • symbol — уникальный идентификатор.

String (строка)

Строка — это набор символов, идущих друг за другом. Например: 'Привет', '123', 'аbс!'. Строки неизменяемы. На практике это значит, что если вы меняете строку, на самом деле создаётся новая, а старая остаётся такой же, какой была.

Пример:

let str = "Привет";
str.toUpperCase(); // переводит текст в верхний регистр и возвращает "ПРИВЕТ"
console.log(str);  // всё равно "Привет"

Метод toUpperCase() вернул новую строку, но в переменную str мы её не записали — и строка осталась прежней.

Если вы хотите сохранить изменённую строку, нужно явно присвоить результат переменной:

str = str.toUpperCase(); // теперь str -- это "ПРИВЕТ"

Читайте также:

Строки в JavaScript

Number (число)

В JavaScript все числа, кроме очень больших, относятся к одному типу данных: number. Неважно, пишете вы 10, 3.14 или -500, всё это — один тип. В других языках вроде C++ или Java есть разные типы для целых и дробных чисел, но в JavaScript один number на все случаи.

При этом в JavaScript числа хранятся в двоичном виде. Из-за этого далеко не каждое десятичное число можно точно записать. Например, 0.1 и 0.2 в двоичном виде бесконечные и не вполне совпадают с десятичными значениями. Поэтому иногда происходят странности:

console.log(0.1 + 0.2); // 0.30000000000000004

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

У number есть свои ограничения. Все числа внутри него лежат в пределах от ‑(2⁵³ - 1) до 2⁵³ - 1 — это примерно от минус 9 квадриллионов до 9 квадриллионов. Если выйти за этот предел, число может начать терять точность. Проверить границы можно так:

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

Если вы работаете с числами больше этих значений (например, в финансовых расчётах или криптографии), то лучше использовать другой тип — BigInt (о нём — позже).

Есть ещё три особых числовых значения, с которыми вы точно столкнётесь:

Infinity — бесконечность. Получается, например, если делить на ноль:

console.log(10 / 0); // Infinity

-Infinity — минус бесконечность. То же самое, но с отрицательным числом:

console.log(-10 / 0); // -Infinity

NaN — это «Not a Number», то есть «не число». Возникает, когда результат операции нельзя выразить числом:

console.log(0 / 0);           // NaN
console.log(Math.sqrt(-1));   // NaN
console.log(parseInt("abc")); // NaN

Тут есть одна ловушка: NaN не равен даже самому себе. Это кажется странным, но так и должно быть:

console.log(NaN === NaN); // false

Чтобы проверить, NaN ли перед вами, используйте функцию:

console.log(Number.isNaN(value)); // это правильный способ

BigInt (большие целые числа)

BigInt покрывает диапазон, который не может охватить number. Чаще всего такие огромные числа используются в криптографии, и иногда это могут быть идентификаторы в базах данных.

Такое число пишется почти как обычное, только с буквой n на конце:

let bigNumber = 123456789012345678901234567890n;
console.log(bigNumber); // 123456789012345678901234567890n

С BigInt можно делать всё то же, что и с обычными числами: складывать, вычитать, умножать, делить, делить с остатком:

let a = 7n;
let b = 3n;
console.log(a + b); // 10n

Но нельзя смешивать BigInt и обычный number в одной операции. Такой код вызовет ошибку:

let num = 10;
let big = 20n;

console.log(num + big); // TypeError: нельзя складывать number и BigInt

Если очень нужно, одно из чисел можно преобразовать:

BigInt(10) + 20n; // 30n

Ещё один момент: при делении BigInt отбрасывает дробную часть. Результат всегда будет целым:

console.log(5n / 2n); // 2n, а не 2.5

Boolean (булев тип, логический)

Логический тип данных в JavaScript называется boolean. У него всего два значения: true (истина) и false (ложь). Звучит просто — и это действительно так. Булевы значения лежат в основе всей логики в коде: они используются в условиях, проверках, циклах и функциях.

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

let isRaining = true;

if (isRaining) {
  console.log("Возьми зонт!");
} else {
  console.log("Можно идти без зонта.");
}

Переменная isRaining содержит булево значение. Условие в if проверяет, истинно ли оно, — и от этого зависит, какой код выполнится.

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

Например, условие if (value) работает даже если value — это число, строка или объект. JavaScript попробует «перевести» это значение в true или false и уже потом примет решение.

if ("Привет") {
  console.log("Это выполнится");
}

if (0) {
  console.log("А это -- нет");
}

В первом случае строка "Привет" считается истинной, потому что она не пустая. Во втором — 0 считается ложным значением, поэтому блок if не срабатывает. Таких правил немного, и они всегда одинаковые.

Что считается false:

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

Всё остальное — true. Даже строка "0" или "false" (это всё равно true, потому что строка не пуста).

К true также относятся:

  • массивы [];
  • объекты {};
  • Infinity и -Infinity.

Проверить, как значение ведёт себя в логике, можно через функцию Boolean():

Boolean("");       // false
Boolean("Привет"); // true
Boolean(0);        // false
Boolean(42);       // true

Тип boolean управляет всей логикой: что показать пользователю, когда что-то выполнять, на что реагировать. Чем раньше вы к нему привыкнете, тем легче станет читать и писать код.

Значение undefined («неопределённый»)

Примитив undefined означает, что переменная уже существует, но её значение пока не определено. То есть переменная есть, а данных в ней нет.

Такое часто случается, если переменную объявили, но забыли присвоить ей значение:

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

Переменная name есть, но мы не задали ей значение — поэтому JavaScript возвращает undefined.

Такое значение может появиться и в других случаях. Например, если функция ничего не возвращает:

function sayHello() {
  console.log("Привет!");
}

console.log(sayHello()); // undefined

Функция sayHello что-то делает, но не возвращает результат — и поэтому по умолчанию возвращается undefined.

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

let user = {};
console.log(user.age); // undefined -- такого свойства нет

Проверить, действительно ли значение undefined, можно через строгое сравнение:

let value;
if (value === undefined) {
  console.log("Переменная не определена!");
}

Чтобы undefined не появлялся случайно, очень желательно:

  • сразу присваивать значения при создании переменных;
  • проверять, есть ли нужное свойство у объекта, перед использованием.

Значение null (пустая переменная)

В JavaScript есть особый тип данных — null. Он про то, что значения нет и это сделано специально. То есть вы как бы говорите: «Сейчас здесь ничего нет, но это нормально».

Чаще всего null используют, когда нужно явно указать, что переменная пуста:

let user = null; // данных пока нет

Или, например, в объекте:

let profile = {
  name: null, // имя пока не задано
  age: 25
};

Это не то же самое, что undefined. Разница в намерении: null — это явное указание на отсутствие значения. А undefined — это про то, что переменной не присвоено никакого значения. Это неявное отсутствие значения.

let a = null; // явно сказали: пусто
let b;        // переменная есть, но без значения

console.log(a === b); // false -- это разные вещи

Проверить, null ли у переменной, можно с помощью строгого сравнения:

let data = null;

if (data === null) {
  console.log("Значение пустое!");
}

Symbol (уникальный идентификатор)

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

Символ — это уникальное значение, которое можно использовать как ключ в объекте. Даже если вы создадите два символа с одинаковыми описаниями, они всё равно будут разными:

let a = Symbol("id");
let b = Symbol("id");

console.log(a === b); // false -- это два разных символа

То есть Symbol гарантирует, что ключ, созданный с его помощью, не повторится ни случайно, ни по ошибке.

Это удобно, если вы, например, пишете модуль, который добавляет дополнительную информацию в объект, но не хотите мешать чужим свойствам:

const privateKey = Symbol("private");

let user = {
  name: "Лена",
  [privateKey]: "секретный токен"
};

console.log(user[privateKey]); // "секретный токен"

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

Ещё есть так называемые глобальные символы. Их создают через Symbol.for("имя"). Если вызвать Symbol.for() с одним и тем же именем в разных частях программы, вернётся один и тот же символ:

let global1 = Symbol.for("shared");
let global2 = Symbol.for("shared");

console.log(global1 === global2); // true

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

Ссылочный тип данных — object

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

let user = {
  name: "Катя",
  age: 25,
  email: "katya@example.com"
};

Объекты — это основа большинства структур в JavaScript. К ним относятся не только обычные объекты, но и массивы, функции, даты и многое другое. Всё это — ссылочные типы.

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

let obj1 = { name: "Вася" };
let obj2 = obj1;

obj2.name = "Ася";

console.log(obj1.name); // "Ася"

В этом примере obj1 и obj2 указывают на один и тот же объект в памяти. Изменение через obj2 автоматически видно и через obj1, потому что это одна и та же сущность, просто с двумя именами.

Работать с объектами можно по-разному — добавлять и удалять свойства, перебирать их, а также вкладывать одни объекты в другие.

Как узнать, какой тип у переменной

В JavaScript есть специальный оператор typeof, который позволяет узнать, к какому типу относится значение. Он возвращает результат в виде строки.

Примеры:

typeof 10;            // "number"
typeof "Привет";      // "string"
typeof undefined;     // "undefined"
typeof {};            // "object"
typeof function() {}; // "function"

Обратите внимание на последний пример. Хотя функция — это объект, оператор typeof специально выделяет её в отдельный тип "function". Это сделано намеренно, чтобы было проще отличать функции от других объектов. Ведь функции в JavaScript могут вызываться (()) и имеют особые свойства, такие как call и apply. Поэтому typeof был специально адаптирован, чтобы выделять функции.

Оператор typeof можно писать со скобками и без — разницы нет:

typeof 15;      // "number"
typeof(15);     // "number"

Но скобки могут пригодиться, если вы определяете тип целого выражения, особенно если оно выглядит неоднозначно:

typeof 38 + " попугаев";      // "number попугаев"
typeof (38 + " попугаев");    // "string"

В первом случае JavaScript сначала вычисляет typeof 38, получая строку "number", и потом прибавляет к ней " попугаев". Во втором — сначала складываются число и строка (что даёт строку), а потом уже typeof возвращает "string".

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

Неочевидности typeof: NaN и null

Иногда typeof может вернуть результат, который выглядит странно. Один из таких случаев — NaN. Это специальное значение, которое означает «не число», но при этом:

console.log(typeof NaN); // "number"

Да, NaN — это технически число. Просто «неправильное». Например, оно может появиться в результате некорректной арифметики:

let result = 0 / 0; // NaN

Проверить, является ли значение именно NaN, можно несколькими способами — но не все из них надёжны.

Вот пример:

let value1 = NaN;
let value2 = 42;
let value3 = "hello";

// Плохой способ
console.log(isNaN(value1)); // true
console.log(isNaN(value2)); // false
console.log(isNaN(value3)); // true (!) -- хотя это строка, не NaN

// Тоже не подойдёт:
console.log(value1 === NaN); // false -- NaN не равен даже самому себе

// Правильный способ:
console.log(Number.isNaN(value1)); // true
console.log(Number.isNaN(value2)); // false
console.log(Number.isNaN(value3)); // false

Вывод: используйте Number.isNaN() — это единственный способ, который выдает корректный результат.

Если вы попробуете проверить typeof null, то получите:

console.log(typeof null); // "object"

Это, мягко говоря, неожиданно. Значение null — это не объект, а отдельное специальное значение, которое означает «здесь точно ничего нет».

Почему тогда typeof null возвращает "object"? Это баг, который появился ещё в самой первой версии JavaScript. Его не исправляют до сих пор, потому что слишком много кода во всём мире полагается на это поведение.

Так что просто запомните: typeof null всегда будет "object", но сам null объектом не является.

Если нужно проверить значение именно на null, используйте строгое сравнение:

if (value === null) {
  // Да, это null
}

Такие особенности — часть «характера» JavaScript. Главное — знать, где подвох, и использовать правильные способы проверки.

Как JavaScript сам меняет типы и как этим управлять

В JavaScript всё гибко — даже слишком. Значения одного типа могут неожиданно превратиться в другой. Это называется приведение типов. Иногда оно происходит автоматически, иногда — только если вы сами этого хотите.

Например, пользователь ввёл число в форму. На экране видно «10», но в коде это не число, а строка:

const a = "10";
const b = "5";

console.log(a + b); // "105"

JavaScript увидел строки и решил: раз плюс, то, наверное, нужно их склеить. И получил строку "105" вместо ожидаемой суммы.

Чтобы всё сработало правильно, строки нужно явно превратить в числа:

const sum = Number(a) + Number(b);
console.log(sum); // 15

Это и есть явное преобразование — когда вы точно говорите: «Преобразуй вот это значение в число, строку или логическое выражение».

String(123);     // "123"
Number("123");   // 123
Boolean(0);      // false

А вот неявное преобразование происходит, когда JavaScript сам решает, что и к чему надо привести. Например:

console.log(5 + "5");    // "55" -- число стало строкой
console.log("10" - 5);   // 5 -- строка стала числом
console.log(true + false); // 1 -- true → 1, false → 0

Даже такие сравнения работают, хотя выглядят странно:

null == undefined; // true

JavaScript решил, что null и undefined — примерно одно и то же, и вернул true. Именно из-за таких особенностей часто возникают ошибки.

Чтобы избежать путаницы, можно использовать строгое сравнение (===). Оно не приводит типы и возвращает false, если типы не совпадают:

"5" == 5;   // true
"5" === 5;  // false

Есть всего три типа, к которым можно привести значение:

  • к строкеString(value);
  • к числуNumber(value);
  • к логическому значениюBoolean(value).

Примитивные типы (числа, строки и так далее) приводятся по простым правилам. Но если вы попытаетесь преобразовать объект, JavaScript сначала попытается превратить его в примитив и уже потом — в нужный тип. Это работает по сложным внутренним алгоритмам (valueOf, toString и так далее), и на начальном этапе их можно просто избегать.

Преобразование в строку (String)

Чтобы сделать число, null, undefined, true или false строкой, используют функцию String().

String(123);       // '123'
String(null);      // 'null'
String(true);      // 'true'

Есть и неявный способ — прибавить к значению пустую строку. Это сработает почти так же:

123 + "";        // "123"
false + "";      // "false"

Но с Symbol так не сработает — его можно преобразовать только явно.

String(Symbol('test'));  // 'Symbol(test)'
'' + Symbol('test');     // Ошибка TypeError

Преобразование в Boolean

Чтобы получить логическое значение (true или false), используйте Boolean():

Boolean(2);      // true
Boolean(0);      // false
Boolean("");     // false
Boolean("hi");   // true

Неявное преобразование происходит в логических выражениях — например, в if или при использовании операторов !, ||, &&:

if (2) { ... }     // 2 → true
!!2;               // true
2 || "hello";      // 2 (первый истинный)
"hi" && 123;       // 123 (последний истинный)

Мы уже писали выше, что значения вроде 0, "", null, undefined и NaN считаются ложными (false). Всё остальное — истинными (true).

Преобразование в Number

Чтобы явно превратить значение в число, используйте Number():

Number("123");       // 123
Number(" 12 ");      // 12
Number("12s");       // NaN
Number(null);        // 0
Number(true);        // 1
Number(false);       // 0
Number(undefined);   // NaN

Неявное преобразование в число происходит при использовании операторов +, -, *, /, >, <, != и других:

+"123";        // 123 (унарный +)
4 > "5";       // true ("5"5)
5 / null;      // Infinity (null → 0)
true | 0;      // 1 (true1, 00)

Как строки становятся числами:

  • пробелы, \n, \t в начале и в конце удаляются;
  • если остаётся только число — преобразование успешно;
  • если есть лишние символы — результатом будет значение NaN.
Number(" 42 ");      // 42
Number("\n");        // 0
Number("42px");      // NaN

Особые случаи:

  • null0,
  • undefinedNaN,
  • Symbol нельзя преобразовать в число.
Number(Symbol("test")); // Ошибка TypeError
+"Symbol()";            // Ошибка TypeError

Значение null не превращается в 0 при сравнении через ==. Это частая ловушка:

null == 0;           // false
null == null;        // true
null == undefined;   // true

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

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

Логическое преобразование

Всегда даёт true, даже для пустых объектов и массивов:

Boolean({});    // true
Boolean([]);    // true

Число и строка

JavaScript использует специальные методы объекта:

  • при преобразовании к числу сначала вызывается valueOf(), потом toString();
  • при преобразовании к строке — наоборот, то есть сначала toString(), потом valueOf().

Если оба метода вернут объект, а не примитив, будет ошибка:

let obj = {
  toString() { return {}; },
  valueOf() { return {}; }
};

console.log(+obj); // TypeError

Особенности операторов

  • + и == используют стандартное преобразование (обычно к числу, но у Date — к строке);
  • -, *, / и другие арифметические операторы — всегда к числу.

Symbol.toPrimitive

С помощью специального метода Symbol.toPrimitive можно точно задать, как объект должен вести себя при преобразовании:

let user = {
  [Symbol.toPrimitive](hint) {
    if (hint === "string") return "Пользователь";
    if (hint === "number") return 1;
    return null;
  }
};

console.log(String(user)); // "Пользователь"
console.log(+user);        // 1

Интересные факты

Ноль в JavaScript имеет два представления — -0 и +0. В большинстве случаев это не имеет какого-то практического значения, за исключением деления на ноль:

42 / +0
Infinity
42 / -0
-Infinity

Практика: как работают типы данных

Попробуйте выполнить эти задания. Чтобы проверить себя, запустите код в консоли браузера (F12 → Вкладка "Console") на сайте вроде CodePen или JSFiddle или в Node.js.


Задание: определите тип каждой переменной

let a = 42;
let b = "Hello, world!";
let c = true;
let d = null;
let e = undefined;
let f = { name: "Alice" };
let g = [1, 2, 3];
let h = function() { return "Hi!"; };

console.log(typeof a); // ?
console.log(typeof b); // ?
console.log(typeof c); // ?
console.log(typeof d); // ?
console.log(typeof e); // ?
console.log(typeof f); // ?
console.log(typeof g); // ?
console.log(typeof h); // ?

Подумайте, что выведет typeof для каждой переменной, — и только потом запускайте. Обратите внимание на особые случаи, они тут есть.

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

Приведите строку к числу и наоборот, затем проверьте результат с помощью typeof:

let str = "123";
let num = 456;

// Преобразование строки в число
let strToNum = Number(str);

// Преобразование числа в строку
let numToStr = String(num);

console.log(typeof strToNum); // ?
console.log(typeof numToStr); // ?

Поиск ошибок в коде

В приведённом коде есть ошибки, связанные с неверным приведением типов. Исправьте код так, чтобы он работал правильно и выдавал ожидаемые результаты.

function calculateTotal(price, discount) {
  return price - discount + " рублей";
}

let total = calculateTotal("100", 20); 

console.log("Итоговая цена:", total); // Ожидаемый результат: "80 рублей", но что-то не так...
  • Почему результат получается не числом, а строкой?
  • Как правильно преобразовать строку `"100"` в число?
  • Как исправить функцию, чтобы она корректно считала итоговую цену?

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

Подумайте, что выведет консоль, а затем проверьте себя:

console.log("5" + 3);   // ?
console.log("5" - 3);   // ?
console.log("5" * "2"); // ?
console.log("5" / "2"); // ?
console.log(10 + true); // ?
console.log(10 + false);// ?
console.log(10 + null); // ?
console.log(10 + undefined); // ?

Операторы и типы данных

Заполните пропущенные значения:

console.log(typeof NaN);      // ?
console.log(typeof null);     // ?
console.log(typeof []);       // ?
console.log(typeof {});       // ?
console.log(typeof function(){}); // ?
console.log([] + {});         // ?
console.log({} + []);         // ?
console.log([] + 1);          // ?
console.log({} + 1);          // ?

Чуть сложнее

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

18 / "6"
!!"false" == !!"true"
'a' == ['a']
false == 'false'
"number" + 1 + 2
1 + 2 + "number"
[1] > null
"foo" + + "bar"
'true' == true
null == ''
{}+[]+{}+[2]
!+[]+[]+![]
[] + null + 1
0 || "0" && {}
false + true
[1,2,3] == [1,2,3]
new Date(0) - 0
new Date(0) + 0



Курс с помощью в трудоустройстве

Профессия Фронтенд-разработчик

Освойте фронтенд без опыта в IT. Практикуйтесь на реальных задачах и находите первых заказчиков в комьюнити Skillbox.

Узнать о курсе →

Курс с трудоустройством: «Профессия Фронтенд-разработчик» Узнать о курсе
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована