JS / Основы
JavaScript. Основы. Переменные. Операторы
Разбираем переменные, типы данных и операторы JavaScript на понятных примерах: от let и const до калькулятора заказа, отладки и заданий с ответами.
Содержание статьи
- 1. Что такое JavaScript и где выполнять код
- 2. Как читать первые строки программы
- 3. Переменная: имя для значения
- 4. Как выбирать имена переменных
- 5. let, const и var
- 6. Область видимости и обращение до объявления
- 7. Типы данных: что лежит в переменной
- 8. Преобразование типов
- 9. Выражения, операторы и операнды
- 10. Арифметические операторы
- 11. Присваивание, инкремент и декремент
- 12. Сравнения: задаём программе вопросы
- 13. Логические операторы
- 14. ??, ?. и логическое присваивание
- 15. Тернарный оператор
- 16. Приоритет и порядок вычислений
- 17. Мини-проект: калькулятор заказа
- 18. Типичные ошибки и приёмы отладки
- 19. Практические задания
- 20. Ответы и объяснения
- 21. Шпаргалка и дальнейшие шаги
Большая практическая статья для тех, кто начинает программировать. С понятными аналогиями, экспериментами, мини-проектом и заданиями с ответами.
Представьте, что вы открываете маленькую кофейню. Нужно помнить стоимость напитков, считать сдачу, учитывать скидки и решать, кому полагается бесплатная доставка. Пока посетителей мало, можно делать всё в голове. Но уже через час хочется поручить расчёты кому-нибудь другому.
Компьютер готов помочь. Есть только одна особенность: он не понимает фразу «посчитай как обычно». Ему нужны точные данные и точные инструкции.
Переменные позволяют дать данным имена. Операторы объясняют, что с этими данными делать. Вместе они превращают набор чисел и строк в работающую программу.
В этой статье мы разберёмся, почему "5" + 2 даёт "52", зачем нужны сразу let и const, чем присваивание отличается от сравнения и как из нескольких простых выражений получается калькулятор заказа.
Как читать статью
Не пытайтесь запомнить все операторы за один вечер. Прочитайте раздел, выполните пример, измените одно значение и попробуйте заранее предсказать результат. Именно такое маленькое «а что будет, если…» превращает чтение в навык.
Каждый блок JavaScript в статье — отдельный пример. Запускайте его отдельно: в разных блоках могут повторяться имена переменных. Строки с намеренными ошибками обычно закомментированы. Если захотите увидеть ошибку самостоятельно, уберите // только у нужной строки.
1. Что такое JavaScript и где выполнять код
JavaScript — язык программирования. В браузере он может реагировать на нажатия кнопок, проверять поля формы, пересчитывать стоимость заказа и обновлять страницу без полной перезагрузки.
Для первого знакомства удобно представить сайт как кафе:
- HTML — помещение и предметы: стойка, столики, меню, кнопка вызова официанта.
- CSS — оформление: цвета, шрифты, расстояния и расположение элементов.
- JavaScript — действия: нажали кнопку, изменили заказ, получили новую сумму.
Это учебная аналогия, а не строгое разделение возможностей: например, простые анимации доступны и в CSS. Но она помогает понять главную роль JavaScript — описывать поведение.
JavaScript работает не только в браузере. Например, среда Node.js позволяет выполнять его вне браузера. Однако для этой статьи достаточно браузерной консоли: устанавливать дополнительные программы не обязательно.
Первый запуск
Откройте инструменты разработчика браузера и найдите вкладку Console / Консоль. В настольных браузерах для этого часто используют F12 или пункт «Инструменты разработчика» в меню.
Введите вручную:
console.log("Привет! Я начинаю изучать JavaScript.");Вы увидите сообщение:
Привет! Я начинаю изучать JavaScript.console.log(...) выводит переданные значения в консоль. Пока воспринимайте его как диагностическое окошко: мы просим программу показать, что получилось.
После сообщения консоль может дополнительно показать undefined. Это результат самого вызова console.log, а не ошибка и не часть вашего текста.
Важно: не вставляйте в консоль непонятный код со случайных сайтов. Код в консоли может выполнять действия в контексте открытой страницы. Для учебных экспериментов используйте пустую вкладку или свою тестовую страницу.
Если удобнее запускать код из файла
Создайте файл index.html и вставьте в него:
<!doctype html><html lang="ru"> <head> <meta charset="UTF-8"> <title>Изучаю JavaScript</title> </head> <body> <h1>Мои первые эксперименты</h1> <script> "use strict"; const message = "JavaScript работает!"; console.log(message); </script> </body></html>Откройте файл в браузере и посмотрите в консоль. При изменении кода сохраните файл и обновите страницу.
"use strict" включает строгий режим для этого скрипта. Он, в частности, помогает обнаруживать случайное присваивание необъявленной переменной. В JavaScript-модулях строгий режим действует автоматически, но модули — тема следующего этапа обучения.
2. Как читать первые строки программы
Посмотрим на небольшую программу:
const coffeePrice = 180;const coffeeCount = 2;const totalPrice = coffeePrice * coffeeCount;console.log(totalPrice); // 360Читается она почти как инструкция для сотрудника кофейни:
- Цена кофе — 180.
- Покупатель заказал два кофе.
- Общая стоимость — цена, умноженная на количество.
- Покажи общую стоимость.
В наших простых примерах инструкции выполняются последовательно сверху вниз. Позже вы узнаете о функциях, событиях и асинхронности — они добавят к этой картине новые правила.
Комментарии: заметки для человека
Текст после // до конца строки — комментарий. Он не выполняется как код.
// Один стакан стоит 180 рублей.const price = 180;/* Здесь может быть пояснение из нескольких строк.*/console.log(price);Хороший комментарий объясняет причину решения, а не просто повторяет код:
// Скидка действует только на товары, доставка оплачивается отдельно.const discountedGoodsPrice = 2000 * 0.9;console.log(discountedGoodsPrice); // 1800Точки с запятой и пробелы
В примерах мы ставим ; в конце простых инструкций. JavaScript умеет автоматически вставлять точки с запятой по определённым правилам, но это не означает, что любой перенос строки безопасно заменяет ;.
Для начала проще придерживаться последовательного стиля: одна инструкция на строку, понятные отступы, точки с запятой там, где они стоят в примерах.
Пробелы вокруг операторов обычно не меняют результат, зато сильно помогают чтению:
const total = 180 * 2 + 50;console.log(total); // 410Код читают чаще, чем пишут. В том числе вы сами, когда вернётесь к нему через неделю.
3. Переменная: имя для значения
Переменная связывает имя со значением, чтобы к этому значению можно было обращаться в программе.
Для первого знакомства представим переменную как коробку с наклейкой:
- на наклейке написано
userName; - внутри лежит значение
"Анна"; - чтобы получить содержимое, мы обращаемся по имени
userName.
let userName = "Анна";console.log(userName); // АннаРазберём строку объявления:
| Часть | Значение |
|---|---|
let |
Объявляем переменную, которой позже можно присвоить другое значение |
userName |
Имя переменной |
= |
В этой записи задаём начальное значение |
"Анна" |
Строковое значение |
; |
Завершаем инструкцию |
Объявление, инициализация и присваивание
Эти слова описывают разные действия.
Объявить переменную — создать в программе имя. Инициализировать — задать начальное значение. Присвоить — записать значение в уже объявленную переменную.
let deliveryPrice;console.log(deliveryPrice); // undefineddeliveryPrice = 300;console.log(deliveryPrice); // 300deliveryPrice = 0;console.log(deliveryPrice); // 0После выполнения let deliveryPrice; переменная уже существует и имеет значение undefined. Затем мы дважды меняем её значение.
Чаще объявление и начальное значение записывают вместе:
let deliveryPrice = 300;console.log(deliveryPrice); // 300Присваивание — не математическое равенство
В математике запись x = x + 1 выглядит странно. В программировании она совершенно нормальна:
let visitorsCount = 10;visitorsCount = visitorsCount + 1;console.log(visitorsCount); // 11Программа сначала вычисляет правую часть: берёт прежнее значение 10, прибавляет 1 и получает 11. Затем записывает результат в переменную слева.
Представьте табло посетителей: сотрудник посмотрел на число, прибавил одного человека и заменил показание табло. Здесь нет утверждения, что десять равно одиннадцати. Есть команда обновить данные.
Копирование не создаёт вечную связь
let firstPrice = 500;let secondPrice = firstPrice;firstPrice = 700;console.log(firstPrice); // 700console.log(secondPrice); // 500В момент присваивания в secondPrice попало число 500. Это не формула, которая обязана пересчитываться при изменении firstPrice.
Аналогия: вы переписали цену с ценника в блокнот. Если магазин поменяет ценник, запись в блокноте сама не изменится.
С объектами есть важный нюанс: несколько переменных могут указывать на один объект. До этого мы скоро дойдём, и тогда уточним модель коробки.
4. Как выбирать имена переменных
Имя должно подсказывать, что означает значение.
Сравните:
const a = 1500;const b = 3;const c = a * b;console.log(c); // 4500И тот же расчёт:
const productPrice = 1500;const productCount = 3;const orderTotal = productPrice * productCount;console.log(orderTotal); // 4500Во втором примере смысл понятен без расшифровки. Имена — это встроенная документация программы.
Основные правила
Для привычных латинских имён действуют простые ориентиры:
- Начинайте имя с буквы,
_или$, но не с цифры. - Не используйте пробелы и дефисы внутри имени.
- Учитывайте регистр:
userNameиusername— разные имена. - Не используйте зарезервированные слова вроде
class,returnилиconstкак обычные имена переменных.
const userName = "Олег";const product2Price = 900;const _internalValue = 10;const $buttonLabel = "Купить";console.log(userName, product2Price, _internalValue, $buttonLabel);// const 2product = "Книга"; // Ошибка: имя начинается с цифры.// const user-name = "Олег"; // Ошибка: дефис не является частью имени.JavaScript допускает и многие Unicode-буквы, в том числе кириллицу. Но в учебном и командном коде обычно удобнее английские имена: их проще сочетать с названиями встроенных возможностей языка.
Стиль camelCase
Первое слово пишут со строчной буквы, каждое следующее начинают с заглавной:
const userEmail = "student@example.com";const cartTotalPrice = 2500;const isDeliveryAvailable = true;console.log(userEmail, cartTotalPrice, isDeliveryAvailable);Логические значения удобно называть как вопросы: isOpen — «открыто?», hasDiscount — «есть скидка?», canOrder — «можно заказать?».
Для заранее известных констант-настроек нередко используют другой стиль:
const MAX_PRODUCTS_PER_ORDER = 20;const FREE_DELIVERY_THRESHOLD = 5000;console.log(MAX_PRODUCTS_PER_ORDER, FREE_DELIVERY_THRESHOLD);Но объявление через const само по себе не требует заглавных букв. Например, const userName = "Анна" — обычное и хорошее имя.
Ещё полезно указывать единицы измерения: delayMs, distanceKm, priceKopecks. Ошибка «секунды приняли за миллисекунды» выглядит очень буднично, пока программа не начинает ждать в тысячу раз дольше.
5. let, const и var
В обычном прикладном коде вам постоянно будут встречаться три способа объявления переменных: let, const и var. Начнём с первых двух.
let: значение предполагается менять
let score = 0;score = 10;score = 25;console.log(score); // 25let подходит для счётчика, текущего состояния, промежуточного результата, который обновляется.
В одной области видимости нельзя повторно объявить это же имя через let:
let score = 0;// let score = 10; // SyntaxError: повторное объявление.score = 10; // А присвоить новое значение можно.console.log(score); // 10const: нельзя переприсвоить переменную
const shopName = "Кофе и код";console.log(shopName); // Кофе и код// shopName = "Другая кофейня"; // TypeError.У обычного объявления const сразу должно быть начальное значение:
const deliveryPrice = 250;console.log(deliveryPrice); // 250// const anotherPrice; // SyntaxError: нет начального значения.Важно: const не означает «значение известно заранее, до запуска программы». Оно может быть вычислено:
const price = 700;const quantity = 4;const total = price * quantity;console.log(total); // 2800Смысл const здесь такой: после создания переменной total мы не собираемся записывать в неё другой результат.
const не замораживает объекты
Объект объединяет связанные данные. Например, у пользователя есть имя и количество бонусов:
const user = { name: "Анна", bonusPoints: 100};user.bonusPoints = 150;console.log(user.bonusPoints); // 150// user = { name: "Игорь", bonusPoints: 0 }; // TypeError.Мы не заменили объект, на который указывает user. Мы изменили свойство существующего объекта. const запрещает первое, но не запрещает второе.
Здесь модель коробки нуждается в уточнении. Для объекта удобно представить, что в коробке лежит карточка с адресом дома. const запрещает заменить карточку на адрес другого дома. Но переставлять мебель в самом доме по-прежнему можно.
Из этого следует важный эффект:
const firstUser = { name: "Анна" };const secondUser = firstUser;secondUser.name = "Мария";console.log(firstUser.name); // МарияОбе переменные указывают на один объект. Они не получили два независимых дома: у них две карточки с одним адресом.
С массивами ситуация аналогична. Массив — упорядоченная коллекция значений:
const products = ["Кофе", "Чай"];products.push("Какао");console.log(products.length); // 3console.log(products[2]); // Какао// products = []; // TypeError.push добавляет элемент в существующий массив, length сообщает количество элементов, а [2] обращается к третьему элементу: индексация начинается с нуля.
var: нужно узнавать в чужом коде
var — более старый способ объявления. Он не удалён из языка, но имеет особенности, которые усложняют обучение: например, не ограничивается обычным блоком { ... } и допускает повторные объявления через var.
var message = "Первое сообщение";var message = "Второе сообщение";console.log(message); // Второе сообщениеДля нового учебного кода достаточно правила: используйте const по умолчанию, а let — когда действительно потребуется переприсваивание. var важно понимать, чтобы читать старые проекты.
| Свойство | let |
const |
var |
|---|---|---|---|
| Можно переприсвоить | Да | Нет | Да |
| Можно объявить без начального значения в обычной инструкции | Да | Нет | Да |
Ограничивается обычным блоком { ... } |
Да | Да | Нет |
| Повторное объявление тем же ключевым словом в одной области | Нет | Нет | Да |
| Учебный ориентир | Для изменяемой переменной | Основной выбор | Для понимания старого кода |
Формальные правила объявлений и областей видимости можно дополнительно посмотреть в руководстве MDN по грамматике и типам.
6. Область видимости и обращение до объявления
Область видимости — участок программы, в котором доступно имя переменной.
Представьте офис с комнатами. Объявление снаружи комнаты можно увидеть изнутри, если внутреннее имя его не заслоняет. Но записка, оставленная внутри закрытой комнаты, не становится доступной всему офису.
const shopName = "Кофе и код";{ const localDiscount = 10; console.log(shopName); // Кофе и код console.log(localDiscount); // 10}console.log(shopName); // Кофе и код// console.log(localDiscount); // ReferenceError: имя недоступно здесь.В данном примере фигурные скобки создают блок. Это не объект: контекст использования скобок имеет значение.
Одинаковые имена в разных областях
const city = "Казань";{ const city = "Москва"; console.log(city); // Москва}console.log(city); // КазаньВнутреннее объявление затеняет внешнее. Оно не меняет внешнюю переменную, а создаёт отдельную переменную с таким же именем.
Поначалу лучше не использовать одинаковые имена во вложенных блоках без необходимости: это законно, но заставляет читателя внимательнее отслеживать контекст.
Почему var ведёт себя иначе
{ var oldStyleValue = 10; let modernValue = 20;}console.log(oldStyleValue); // 10// console.log(modernValue); // ReferenceError.Обычный блок не ограничил var. Внутри функции var ограничивается функцией; на верхнем уровне действуют правила скрипта или модуля.
Можно ли прочитать переменную до объявления?
Для let и const — не до выполнения инициализации соответствующего объявления:
// console.log(price); // ReferenceError, если раскомментировать.let price = 300;console.log(price); // 300Участок от начала области видимости до инициализации называют временной мёртвой зоной, или TDZ. Не пугайтесь названия: практический вывод простой — объявляйте переменную до использования.
С var получится другой результат:
console.log(price); // undefinedvar price = 300;console.log(price); // 300Объявление var обрабатывается заранее, но присваивание 300 происходит только при выполнении соответствующей строки. Это связано с механизмом, который называют hoisting, или «поднятием».
Не нужно представлять, будто JavaScript буквально переносит строки файла. Это способ описать поведение объявлений. let и const тоже обрабатываются до выполнения тела области, но до инициализации недоступны.
7. Типы данных: что лежит в переменной
Операция зависит не только от значения, но и от его типа. Пять яблок и табличка с надписью «5» — не одно и то же. Первое можно прибавить к количеству яблок. Второе можно приклеить к другой надписи.
Числа: number
const age = 25;const temperature = -7;const rating = 4.8;console.log(age, temperature, rating);В обычных числовых литералах дробную часть отделяют точкой, не запятой. Целые и дробные значения чаще всего относятся к одному типу — number.
Для удобства чтения больших чисел можно использовать _ внутри записи:
const yearlyVisitors = 1_000_000;console.log(yearlyVisitors); // 1000000_ здесь не является частью значения: это разделитель для человека.
Строки: string
Строка — текст в кавычках:
const firstName = "Анна";const city = 'Казань';const greeting = `Здравствуйте, ${firstName}!`;console.log(city); // Казаньconsole.log(greeting); // Здравствуйте, Анна!Одинарные и двойные кавычки создают обычные строки. Обратные кавычки создают шаблонный литерал: внутри ${...} можно подставить результат выражения.
const price = 250;const quantity = 3;console.log(`За ${quantity} товара: ${price * quantity} ₽`);// За 3 товара: 750 ₽Не перепутайте обратные кавычки с одинарными. Строка 'Привет, ${firstName}' не выполнит подстановку.
Логические значения: boolean
У этого типа всего два значения: true и false.
const isShopOpen = true;const hasUnpaidOrder = false;console.log(isShopOpen); // trueconsole.log(hasUnpaidOrder); // falseАналогия — лампочка-индикатор: включена или выключена. Важно не путать логическое значение false со строкой "false". Строка содержит текст, а не ответ «нет».
undefined: значение не задано
let deliveryDate;console.log(deliveryDate); // undefinedПеременная объявлена, но мы не задали ей дату. Позже она может получить конкретное значение.
null: намеренно обозначенное отсутствие
const selectedProduct = null;console.log(selectedProduct); // nullТак можно показать: «товар пока не выбран». Различие удобно запоминать как анкету: undefined — ответ не получен, null — явно отмечено отсутствие. Это соглашение о смысле; технически программист может присвоить и undefined вручную.
Объекты и массивы
const product = { title: "Настольная лампа", price: 2500, inStock: true};const colors = ["Белый", "Чёрный", "Серый"];console.log(product.title); // Настольная лампаconsole.log(colors[0]); // БелыйОбъект похож на карточку товара с подписанными полями. Массив — на пронумерованный список. Массивы в JavaScript являются объектами.
Ещё два примитивных типа
Полная картина включает bigint и symbol:
const hugeInteger = 9_007_199_254_740_993n;const uniqueKey = Symbol("productId");console.log(typeof hugeInteger); // bigintconsole.log(typeof uniqueKey); // symbolbigint позволяет работать с целыми числами за пределами диапазона безопасных целых number. Литерал заканчивается буквой n. Это не инструмент для точных десятичных дробей.
symbol создаёт уникальные значения, которые, например, используют как ключи свойств. На первом этапе достаточно знать, что такой тип существует.
Всего примитивных типов семь: string, number, bigint, boolean, undefined, symbol, null. Помимо них есть объектный тип. У массивов нет отдельного типа на этом уровне классификации.
typeof: спросить у значения его тип
console.log(typeof 42); // numberconsole.log(typeof "42"); // stringconsole.log(typeof true); // booleanconsole.log(typeof undefined); // undefinedconsole.log(typeof {}); // objectconsole.log(typeof []); // objectconsole.log(typeof null); // objectРезультат typeof — строка. А typeof null === "object" — историческая особенность языка: сам null при этом остаётся примитивным значением, а не объектом.
Если нужно проверить именно null, используйте value === null. Для массива есть Array.isArray(value):
const products = ["Книга", "Лампа"];console.log(Array.isArray(products)); // trueДинамическая типизация
В JavaScript тип связан со значением. Переменной, объявленной через let, можно присвоить значение другого типа:
let answer = 42;console.log(typeof answer); // numberanswer = "Сорок два";console.log(typeof answer); // stringЭто называется динамической типизацией. Но возможность не всегда означает хорошую идею: переменная productPrice, которая сначала хранит число, а потом объект пользователя, быстро превратит код в загадку.
8. Преобразование типов
Иногда данные приходят не в том виде, который нужен для вычислений. Например, свойство .value обычного HTML-поля ввода содержит строку — даже если человек ввёл цифры.
Представьте записку с текстом «250». Прежде чем использовать её в расчёте, нужно прочитать текст как число.
Явное преобразование
const priceFromInput = "250";const quantityFromInput = "3";const price = Number(priceFromInput);const quantity = Number(quantityFromInput);console.log(price * quantity); // 750console.log(typeof price); // numberТри полезных инструмента:
| Запись | Действие | Пример результата |
|---|---|---|
Number(value) |
Преобразует в число | Number("25") → 25 |
String(value) |
Преобразует в строку | String(25) → "25" |
Boolean(value) |
Преобразует в логическое значение | Boolean(0) → false |
При этом преобразование не заменяет проверку данных:
console.log(Number("120")); // 120console.log(Number("12.5")); // 12.5console.log(Number("12,5")); // NaNconsole.log(Number("120 ₽")); // NaNconsole.log(Number("")); // 0console.log(Number(" ")); // 0console.log(Number(null)); // 0console.log(Number(undefined)); // NaNNumber("") возвращает ноль. Но пустое поле и введённый пользователем ноль часто означают разные вещи. Поэтому в форме нужно отдельно проверять, заполнено ли поле.
NaN: не удалось получить нормальный числовой результат
NaN расшифровывается как Not a Number. Это специальное значение типа number, а не отдельный тип.
const result = Number("кофе");console.log(result); // NaNconsole.log(typeof result); // numberconsole.log(Number.isNaN(result)); // trueconsole.log(result === NaN); // falseПоследняя строка — важная ловушка: NaN не равен самому себе при сравнении через ===. Проверяйте его с помощью Number.isNaN.
Если нужно обычное конечное число, полезнее проверка Number.isFinite:
console.log(Number.isFinite(120)); // trueconsole.log(Number.isFinite(Infinity)); // falseconsole.log(Number.isFinite(NaN)); // falseconsole.log(Number.isFinite("120")); // falseОна сама не превращает строку в число. Сначала преобразуйте ввод, потом проверяйте результат.
Number и parseInt — не одно и то же
console.log(Number("24px")); // NaNconsole.log(parseInt("24px", 10)); // 24console.log(parseInt("24.9", 10)); // 24console.log(parseFloat("24.9px")); // 24.9Number пытается преобразовать всю строку. parseInt и parseFloat могут прочитать начальную числовую часть и остановиться на неподходящем символе.
Число 10 в parseInt(..., 10) указывает десятичную систему счисления. Не используйте parseInt как универсальную проверку поля: запись "24яблока" может случайно пройти такую «проверку».
Неявное преобразование и загадка плюса
console.log(5 + 2); // 7console.log("5" + 2); // 52 (строка)console.log("5" - 2); // 3console.log("5" * 2); // 10console.log("10" / 2); // 5У бинарного + две знакомые нам роли: сложение чисел и соединение строк. Если один из операндов после преобразования к примитивному значению является строкой, происходит строковое соединение.
Поэтому "5" + 2 превращается в "5" + "2" и даёт "52". А -, * и / не склеивают текст: для показанных строк они выполняют числовое преобразование.
Здесь легко получить цепную реакцию:
console.log(1 + 2 + "3"); // 33 (строка)console.log("1" + 2 + 3); // 123 (строка)В первом случае сначала складываются числа 1 + 2. Во втором склеивание начинается сразу и продолжается дальше.
Практическое правило: на входе в расчёт явно приводите данные к нужному типу. Чем меньше у программы поводов угадывать, тем проще вам понимать результат.
9. Выражения, операторы и операнды
Выражение — фрагмент кода, вычисление которого даёт значение.
console.log(2 + 3); // 5console.log(10 > 7); // trueconsole.log("Кофе" + "!"); // Кофе!В 2 + 3 знак + — оператор, а 2 и 3 — операнды, то есть то, над чем выполняется действие.
По количеству операндов операторы бывают:
- унарными — один операнд:
!isOpen,-price,typeof value; - бинарными — два:
price * quantity,age >= 18; - тернарными — три:
condition ? firstValue : secondValue.
Не всякая конструкция языка является оператором. Например, if — условная инструкция, а const используется в объявлении. Различие станет полезным, когда мы будем выбирать между if и тернарным оператором.
10. Арифметические операторы
Большинство арифметических знаков знакомы со школы:
| Оператор | Действие | Пример | Результат |
|---|---|---|---|
+ |
Сложение | 8 + 3 |
11 |
- |
Вычитание | 8 - 3 |
5 |
* |
Умножение | 8 * 3 |
24 |
/ |
Деление | 8 / 2 |
4 |
% |
Остаток от деления | 8 % 3 |
2 |
** |
Возведение в степень | 2 ** 3 |
8 |
Считаем заказ
const coffeePrice = 180;const coffeeCount = 2;const dessertPrice = 240;const orderPrice = coffeePrice * coffeeCount + dessertPrice;console.log(orderPrice); // 600Деление не обязательно даёт целое число
console.log(7 / 2); // 3.5Если нужно узнать число полных коробок по шесть предметов, это отдельное действие:
const itemsCount = 20;const boxCapacity = 6;const fullBoxes = Math.floor(itemsCount / boxCapacity);const remainingItems = itemsCount % boxCapacity;console.log(fullBoxes); // 3console.log(remainingItems); // 2Math.floor округляет вниз. Для положительного количества предметов это даёт число полностью заполненных коробок.
% — не вычисление процентов
17 % 5 даёт 2: из семнадцати конфет можно собрать три полные группы по пять, останутся две конфеты.
Остаток удобно использовать для проверки чётности:
const seatsCount = 18;const canSplitIntoPairs = seatsCount % 2 === 0;console.log(canSplitIntoPairs); // trueА процентную скидку считают через деление на сто:
const price = 2000;const discountPercent = 15;const discountAmount = price * discountPercent / 100;const finalPrice = price - discountAmount;console.log(discountAmount); // 300console.log(finalPrice); // 1700Для отрицательных значений у % есть особенность: это именно остаток, а не всегда положительный математический модуль. Например, -7 % 3 даёт -1.
Степень и смена знака
console.log(3 ** 2); // 9console.log(2 ** 5); // 32console.log(-(3 ** 2)); // -9console.log((-3) ** 2); // 9У скобок здесь не декоративная роль. Кроме того, запись -3 ** 2 без нужных скобок является синтаксической ошибкой JavaScript.
Унарный минус меняет знак числа, а унарный плюс может преобразовать значение в число:
const temperature = 7;console.log(-temperature); // -7console.log(+"250"); // 250Для новичка Number("250") обычно понятнее, чем +"250". Унарный плюс стоит узнавать в чужом коде; также он не поддерживает преобразование bigint.
Деление на ноль
Для чисел типа number:
console.log(10 / 0); // Infinityconsole.log(-10 / 0); // -Infinityconsole.log(0 / 0); // NaNИсключение автоматически не возникает. Поэтому результат расчёта может потребовать проверки.
У bigint свои правила: деление отбрасывает дробную часть к нулю, а деление на 0n вызывает ошибку. Смешивать number и bigint в обычной арифметике тоже нельзя без явного преобразования:
console.log(7n / 2n); // 3n// console.log(7n / 0n); // RangeError.// console.log(7n + 2); // TypeError.Почему 0.1 + 0.2 не равно ровно 0.3
console.log(0.1 + 0.2); // 0.30000000000000004console.log(0.1 + 0.2 === 0.3); // falseОбычные числа JavaScript используют двоичное представление с ограниченной точностью. Некоторые десятичные дроби невозможно сохранить в нём точно — примерно как невозможно записать 1 / 3 конечным количеством десятичных знаков.
Это не случайная поломка калькулятора. Это свойство представления чисел.
В простых денежных расчётах полезно хранить целые копейки:
const firstPriceKopecks = 10;const secondPriceKopecks = 20;const totalKopecks = firstPriceKopecks + secondPriceKopecks;console.log(totalKopecks); // 30console.log(totalKopecks / 100); // 0.3Это помогает избегать ошибок на сложении целых сумм в пределах безопасного диапазона. Но проценты, налоги и округления всё равно требуют явно выбранных правил. Для сложных финансовых систем используют дополнительные решения для точной арифметики.
toFixed удобно применять для отображения, но он возвращает строку:
const displayValue = (0.1 + 0.2).toFixed(2);console.log(displayValue); // 0.30console.log(typeof displayValue); // string11. Присваивание, инкремент и декремент
Мы уже использовали =. У присваивания есть сокращённые формы:
| Короткая запись | Для обычной переменной означает |
|---|---|
total += 100 |
total = total + 100 |
total -= 100 |
total = total - 100 |
total *= 2 |
total = total * 2 |
total /= 2 |
total = total / 2 |
total %= 3 |
total = total % 3 |
total **= 2 |
total = total ** 2 |
let wallet = 1000;wallet -= 250;wallet += 100;console.log(wallet); // 850Это похоже на операции по счёту: списать сумму, затем пополнить баланс.
+= сохраняет особенности обычного плюса:
let title = "Заказ";title += " оформлен";console.log(title); // Заказ оформленlet amount = "100";amount += 50;console.log(amount); // 10050 (строка)Инкремент ++ и декремент --
Эти операторы увеличивают или уменьшают значение на единицу:
let likes = 10;likes++;console.log(likes); // 11likes--;console.log(likes); // 10Есть две формы, и они отличаются значением самого выражения:
let firstCounter = 5;const oldValue = firstCounter++;console.log(oldValue); // 5console.log(firstCounter); // 6let secondCounter = 5;const newValue = ++secondCounter;console.log(newValue); // 6console.log(secondCounter); // 6counter++ возвращает значение до увеличения. ++counter возвращает значение после увеличения. В обоих случаях переменная увеличивается уже при выполнении оператора — постфиксная форма не откладывает изменение до следующей строки.
Аналогия — электронная очередь. Один терминал сначала печатает текущий номер и затем переключает счётчик. Другой сначала переключает счётчик и печатает новый номер.
Пока вы учитесь, не прячьте несколько таких изменений внутрь большого выражения. Отдельная строка counter += 1 часто понятнее хитроумной комбинации плюсов.
12. Сравнения: задаём программе вопросы
Арифметика помогает получить число. Сравнение помогает получить ответ на вопрос: «Хватит ли денег?», «Есть ли товар на складе?», «Совпадает ли введённый код с ожидаемым?».
Результат сравнения — true или false.
| Оператор | Вопрос | Пример | Результат |
|---|---|---|---|
> |
Больше? | 10 > 5 |
true |
< |
Меньше? | 10 < 5 |
false |
>= |
Больше или равно? | 10 >= 10 |
true |
<= |
Меньше или равно? | 8 <= 10 |
true |
=== |
Строго равно? | 10 === 10 |
true |
!== |
Строго не равно? | 10 !== 5 |
true |
== |
Равно с возможным приведением типов? | 10 == "10" |
true |
!= |
Не равно с возможным приведением типов? | 10 != "10" |
false |
Почему обычно стоит выбирать ===
console.log(5 === 5); // trueconsole.log(5 === "5"); // falseconsole.log(5 == "5"); // trueconsole.log(0 == false); // trueconsole.log(0 === false); // falseСтрогое сравнение не приводит операнды к одному типу. Число 5 и строка "5" отличаются, поэтому результат — false.
Нестрогое сравнение использует дополнительные правила преобразования. Это не обязательно ошибка, но новичку гораздо проще рассуждать о коде, который не меняет типы незаметно.
Представьте проверку билетов. === смотрит и на содержание, и на вид предъявленного значения. == в некоторых случаях соглашается сначала преобразовать предъявленное. Иногда это удобно, но правила такого «согласования» нужно хорошо знать.
Практический подход: явно преобразовать данные и затем использовать === или !==.
const quantityFromInput = "3";const quantity = Number(quantityFromInput);console.log(quantity === 3); // trueСтроки сравниваются не как числа
console.log("20" > "100"); // trueconsole.log(20 > 100); // falseКогда оба операнда — строки, сравнение идёт по их последовательностям кодовых единиц UTF-16, а не по числовому смыслу текста. Для показанных цифр уже первый символ "2" оказывается больше "1".
Для сравнения числового ввода сначала получите числа:
console.log(Number("20") > Number("100")); // falseТакое строковое сравнение также не заменяет языковые правила сортировки слов. Для сортировки имён с учётом языка существуют отдельные инструменты.
Объекты сравниваются по идентичности
const firstProduct = { price: 100 };const secondProduct = { price: 100 };const sameProduct = firstProduct;console.log(firstProduct === secondProduct); // falseconsole.log(firstProduct === sameProduct); // trueДва одинаково заполненных объекта — всё равно два разных объекта. Это как два одинаковых чемодана: одинаковое содержимое не делает их одним чемоданом.
Нельзя писать интервалы как в математике
Намеренно неправильный пример:
const age = 70;console.log(18 <= age <= 65); // true — но это не проверка интервала!Сначала вычисляется 18 <= 70, получается true. Затем true <= 65 приводит true к 1, и выходит 1 <= 65, то есть снова true.
Нужно соединить две отдельные проверки через логическое «И»:
const age = 70;console.log(age >= 18 && age <= 65); // false13. Логические операторы
Логические операторы объединяют условия. Представим, что вы организуете мероприятие:
- пройти можно, если есть билет и вход открыт;
- получить скидку можно, если есть промокод или клубная карта;
- показать предупреждение нужно, если пользователь не согласился с условиями.
В JavaScript это &&, || и !.
Логическое «И»: &&
const hasTicket = true;const isEntranceOpen = false;const canEnter = hasTicket && isEntranceOpen;console.log(canEnter); // falseДля двух логических операндов результат true получается только тогда, когда оба равны true.
Логическое «ИЛИ»: ||
const hasPromoCode = false;const hasClubCard = true;const hasDiscount = hasPromoCode || hasClubCard;console.log(hasDiscount); // trueДостаточно хотя бы одного выполненного условия. Это включающее «или»: если и промокод, и карта есть одновременно, результат тоже true.
Логическое «НЕ»: !
const isLoading = false;console.log(!isLoading); // trueДля булевых значений оператор переворачивает ответ. Для остальных сначала выполняется преобразование к логическому значению, затем отрицание.
Таблица для двух логических значений
a |
b |
a && b |
a || b |
|---|---|---|---|
true |
true |
true |
true |
true |
false |
false |
true |
false |
true |
false |
true |
false |
false |
false |
false |
Truthy и falsy: не только true и false
В условии можно использовать не только логический тип. JavaScript интерпретирует значения как истинные (truthy) или ложные (falsy).
Основные falsy-значения:
false;0и-0;0n;- пустая строка
""; null;undefined;NaN.
Остальные обычные значения — truthy. Есть специальное историческое браузерное исключение document.all; в обычных прикладных проверках оно не нужно.
Особенно полезно запомнить:
console.log(Boolean("")); // falseconsole.log(Boolean("0")); // trueconsole.log(Boolean("false")); // trueconsole.log(Boolean(" ")); // trueconsole.log(Boolean([])); // trueconsole.log(Boolean({})); // trueНепустая строка "false" не становится ложной только из-за написанного в ней слова. Пустой массив и пустой объект тоже truthy.
Аналогия: запечатанный конверт считается существующим независимо от того, есть ли внутри письмо. Проверка «конверт есть?» не отвечает на вопрос «в нём что-нибудь написано?».
Чтобы проверить непустой массив, спрашивайте о его длине:
const cart = [];console.log(Boolean(cart)); // trueconsole.log(cart.length > 0); // falseДвойное отрицание !!value даёт логическое значение. Например, !!"Кофе" — true. По смыслу это преобразование, как Boolean(value), но полная запись часто понятнее начинающему читателю.
Важный поворот: && и || возвращают операнды
С булевыми значениями всё выглядит как обычная логика. Но результат этих операторов не обязан быть булевым.
|| возвращает первый truthy-операнд, а если такого нет — последний операнд:
console.log("" || "Гость"); // Гостьconsole.log("Анна" || "Гость"); // Аннаconsole.log(0 || null); // null&& возвращает первый falsy-операнд, а если все truthy — последний операнд:
console.log("Анна" && "Казань"); // Казаньconsole.log(0 && "Казань"); // 0console.log(true && 100); // 100Они проверяют логическую интерпретацию значений, но возвращают сами значения. Это одна из самых полезных особенностей JavaScript — и одна из самых частых причин удивления.
Короткое замыкание: когда дальше вычислять незачем
Если && встретил falsy-значение, дальше он не вычисляет. Если || встретил truthy-значение, дальше тоже не вычисляет.
let checksCount = 0;false && (checksCount += 1);true || (checksCount += 1);console.log(checksCount); // 0В обеих строках правая часть не выполнилась. Результат уже был понятен по левой части.
Это как проходная с несколькими условиями. Если вход закрыт для всех, охраннику не нужно проверять ваш билет для принятия решения о входе.
Так можно защитить обращение к свойству:
const user = null;const canSendEmail = user !== null && user.email !== undefined;console.log(canSendEmail); // falseМы сначала проверили, что user не равен null. Правая часть не выполнилась, поэтому попытки прочитать user.email не было. Эта конкретная проверка рассчитана на null или объект, а не на все возможные типы входных данных.
Не превращайте короткое замыкание в головоломку с множеством побочных действий. Иногда обычный if лучше показывает намерение.
14. ??, ?. и логическое присваивание
Этот раздел можно читать вторым проходом. Здесь собраны инструменты, которые часто встречаются в реальном коде, но опираются на уже разобранные идеи.
??: запасное значение только при отсутствии
Допустим, настройка громкости может быть равна нулю. Пользователь специально выключил звук. Если применить ||, программа неожиданно вернёт громкость по умолчанию:
const savedVolume = 0;console.log(savedVolume || 50); // 50console.log(savedVolume ?? 50); // 0|| выбирает правую часть при любом falsy-значении слева. ?? делает это только для null и undefined.
console.log(null ?? "Не указано"); // Не указаноconsole.log(undefined ?? "Не указано"); // Не указаноconsole.log(0 ?? 100); // 0console.log(false ?? true); // falseconsole.log("" ?? "Гость"); // пустая строкаconsole.log(NaN ?? 100); // NaNАналогия: в анкете есть ответ «0 детей». ?? понимает, что ответ получен. || при такой обработке считает ноль поводом подставить запасной вариант.
Ни один из операторов не «лучше всегда». Если пустая строка должна заменяться на "Гость", || может быть уместен. Если пустая строка — допустимая настройка, пригодится ??.
?? тоже использует короткое замыкание: правую часть вычисляет только при null или undefined слева.
?.: безопасно продолжить цепочку доступа
const user = { name: "Анна", address: null};const city = user.address?.city ?? "Город не указан";console.log(city); // Город не указанЕсли user.address равен null или undefined, необязательная цепочка ?. возвращает undefined вместо ошибки доступа к следующему свойству. После этого ?? подставляет текст.
Если сам user тоже может отсутствовать, защитите и этот уровень:
const user = null;const city = user?.address?.city ?? "Город не указан";console.log(city); // Город не указанНо ?. — не универсальный подавитель ошибок. Корневая переменная должна существовать:
// missingUser?.name; // ReferenceError, если missingUser не объявлена.И отдельно: ?. реагирует только на null и undefined, а не на любое falsy-значение.
||=, &&= и ??=
Эти операторы выполняют присваивание только при определённом условии:
| Оператор | Когда присвоит правую часть |
|---|---|
value ||= fallback |
Если текущее значение falsy |
value &&= replacement |
Если текущее значение truthy |
value ??= fallback |
Если текущее значение — null или undefined |
let nickname = "";nickname ||= "Гость";let volume = 0;volume ??= 50;let status = "Черновик";status &&= "Готов к проверке";console.log(nickname); // Гостьconsole.log(volume); // 0console.log(status); // Готов к проверкеЗапоминать их все сразу необязательно. Сначала поймите базовые ||, && и ?? — после этого сокращённые записи становятся естественными.
15. Тернарный оператор
Иногда нужно выбрать одно из двух значений по условию. Например, для заказа от 3000 рублей доставка бесплатна, а для остальных стоит 300 рублей.
Форма записи:
условие ? значениеЕслиИстина : значениеЕслиЛожьРабочий пример:
const orderTotal = 3500;const deliveryPrice = orderTotal >= 3000 ? 0 : 300;console.log(deliveryPrice); // 0Читаем слева направо: «Сумма не меньше 3000? Тогда 0. Иначе 300».
Это как развилка с указателем: условие выбирает одну из двух дорог. Вычисляется только выбранная ветка.
То же решение через if
if выполняет блок кода, если условие truthy, а else — альтернативный блок:
const orderTotal = 3500;let deliveryPrice;if (orderTotal >= 3000) { deliveryPrice = 0;} else { deliveryPrice = 300;}console.log(deliveryPrice); // 0Тернарный оператор удобен, когда мы выбираем короткое значение. if удобнее, когда внутри ветки несколько действий или сложные условия.
Полезный пример для интерфейса
const stockCount = 5;const buttonText = stockCount > 0 ? "Добавить в корзину" : "Нет в наличии";console.log(buttonText); // Добавить в корзинуНе стоит строить длинную лестницу из вложенных ? и :. Экономия строк не должна превращать чтение программы в разбор ребуса.
16. Приоритет и порядок вычислений
Как и в математике, разные операторы имеют разный приоритет:
console.log(2 + 3 * 4); // 14console.log((2 + 3) * 4); // 20В первом случае умножение связывает свои операнды сильнее сложения. Во втором скобки меняют группировку.
Компактная карта приоритетов
Ниже — не полная таблица языка, а ориентир для операторов из статьи, от более высокого приоритета к более низкому:
| Уровень | Примеры |
|---|---|
| Явная группировка | (a + b) |
| Доступ к свойствам и вызовы | user.name, user?.name, Number(value) |
| Постфиксное изменение | value++, value-- |
| Унарные операции и префиксное изменение | !value, typeof value, ++value |
| Возведение в степень | ** |
| Умножение, деление, остаток | *, /, % |
| Сложение, вычитание | +, - |
| Отношения | <, >, <=, >= |
| Равенство | ===, !==, ==, != |
| Логическое «И» | && |
| Логическое «ИЛИ» и nullish coalescing | ||, ?? |
| Условный выбор | ? : |
| Присваивание | =, +=, ??= и другие |
У ** есть уже рассмотренное синтаксическое ограничение с унарным минусом слева. Поэтому (-3) ** 2 и -(3 ** 2) допустимы, а -3 ** 2 — нет.
Приоритет логических операторов
console.log(true || false && false); // trueconsole.log((true || false) && false); // false&& имеет более высокий приоритет, чем ||. Первая строка группируется как true || (false && false). Из-за короткого замыкания правая часть || вообще не вычисляется.
Вот зачем различать два понятия:
- Приоритет определяет, как выражение сгруппировано.
- Порядок вычислений определяет, какие части и когда действительно выполнятся.
Высокий приоритет внутри пропущенной ветки не заставляет эту ветку выполняться.
Особое правило для ??
JavaScript запрещает напрямую смешивать ?? с || или && без скобок:
const savedName = null;// const name = savedName ?? "" || "Гость"; // SyntaxError.const name = (savedName ?? "") || "Гость";console.log(name); // ГостьСкобки здесь обязательны, а не просто желательны.
Ассоциативность: как группируются одинаковые операторы
Вычитание группируется слева направо:
console.log(10 - 3 - 2); // 5: (10 - 3) - 2console.log(10 - (3 - 2)); // 9А возведение в степень — справа налево:
console.log(2 ** 3 ** 2); // 512: 2 ** (3 ** 2)console.log((2 ** 3) ** 2); // 64Не нужно держать всю таблицу приоритетов в голове. Если выражение заставляет остановиться и пересчитать правила, добавьте скобки или вынесите часть расчёта в переменную с понятным именем.
Для дальнейшего изучения категорий операторов полезно руководство MDN по выражениям и операторам.
17. Мини-проект: калькулятор заказа
Теперь соберём изученное в одну программу. Она не будет показывать красивую корзину на странице — зато полностью рассчитает заказ и выведет понятный итог в консоль.
Условия задачи
Наш магазин продаёт настольные лампы. Действуют правила:
- Одна лампа стоит 1490 рублей.
- Количество поступает строкой, как из поля формы.
- Можно заказать от 1 до 10 ламп, только целое количество.
- Активный промокод даёт скидку 10% на товары.
- При стоимости товаров после скидки от 4000 рублей доставка бесплатна. Иначе — 350 рублей.
- Если имя не введено или состоит из пробелов, обращаемся к покупателю «Гость».
- Заказ можно оформить, если на балансе достаточно денег.
Деньги будем хранить в копейках. Правило округления скидки в этом учебном примере — Math.round до целой копейки. На практике правило округления задаётся требованиями конкретного продукта.
Полный код
"use strict";// 1. Входные данные: их удобно менять для экспериментов.const customerNameInput = " Анна ";const quantityInput = "3";const hasPromoCode = true;const balanceKopecks = 500_000;// 2. Настройки магазина.const productName = "Настольная лампа";const productPriceKopecks = 149_000;const deliveryFeeKopecks = 35_000;const freeDeliveryThresholdKopecks = 400_000;const maxQuantity = 10;// 3. Подготовка и проверка количества.const normalizedQuantityInput = quantityInput.trim();const quantity = Number(normalizedQuantityInput);const isQuantityValid = normalizedQuantityInput !== "" && Number.isInteger(quantity) && quantity >= 1 && quantity <= maxQuantity;if (!isQuantityValid) { console.log(`Ошибка: введите целое количество от 1 до ${maxQuantity}.`);} else { // 4. Подготовка имени. const customerName = customerNameInput.trim() || "Гость"; // 5. Стоимость товаров и скидка. const subtotalKopecks = productPriceKopecks * quantity; const discountPercent = hasPromoCode ? 10 : 0; const discountKopecks = Math.round( subtotalKopecks * discountPercent / 100 ); const goodsTotalKopecks = subtotalKopecks - discountKopecks; // 6. Доставка зависит от суммы после скидки. const isDeliveryFree = goodsTotalKopecks >= freeDeliveryThresholdKopecks; const deliveryKopecks = isDeliveryFree ? 0 : deliveryFeeKopecks; const totalKopecks = goodsTotalKopecks + deliveryKopecks; // 7. Проверка возможности оплатить. const canPay = balanceKopecks >= totalKopecks; const missingKopecks = Math.max(totalKopecks - balanceKopecks, 0); const paymentMessage = canPay ? "Средств достаточно. Заказ можно оформить." : `Не хватает: ${(missingKopecks / 100).toFixed(2)} ₽`; // 8. Вывод результата. Форматирование не меняет исходные суммы. console.log(`Покупатель: ${customerName}`); console.log(`Товар: ${productName}`); console.log(`Количество: ${quantity}`); console.log(`До скидки: ${(subtotalKopecks / 100).toFixed(2)} ₽`); console.log(`Скидка: ${(discountKopecks / 100).toFixed(2)} ₽`); console.log(`Доставка: ${(deliveryKopecks / 100).toFixed(2)} ₽`); console.log(`Итого: ${(totalKopecks / 100).toFixed(2)} ₽`); console.log(paymentMessage);}Ожидаемый вывод:
Покупатель: АннаТовар: Настольная лампаКоличество: 3До скидки: 4470.00 ₽Скидка: 447.00 ₽Доставка: 0.00 ₽Итого: 4023.00 ₽Средств достаточно. Заказ можно оформить.Разбираем программу по смыслу
Подготовка ввода. Метод .trim() возвращает строку без пробелов по краям. Исходная строка не изменяется. Из " Анна " получается "Анна", а из строки пробелов — "".
Проверка количества. Мы отдельно проверяем, что ввод непустой, что результат — целое число и что он входит в допустимый диапазон. Number.isInteger возвращает false также для NaN и бесконечности, поэтому в данном условии отдельная проверка Number.isFinite не требуется.
Расчёт. Умножение даёт исходную сумму, тернарный оператор выбирает процент скидки, вычитание даёт сумму после скидки, сравнение определяет бесплатную доставку.
Баланс. Math.max(a, b) выбирает большее значение. Здесь он не позволяет вывести отрицательную «нехватку денег»: если денег достаточно, нехватка равна нулю.
Отображение. Только на последнем шаге делим копейки на сто и превращаем сумму в строку с двумя знаками после точки. Исходные переменные с копейками остаются числами.
Почему почти везде const?
Программа последовательно вычисляет новые значения, а не обновляет одни и те же переменные. Нам не требуется переприсваивание, поэтому подходит const.
Не нужно искусственно добавлять let, чтобы программа выглядела «более программной». Каждый новый осмысленный результат вполне может получить отдельное имя.
Эксперименты
Меняйте исходные данные по одному параметру. Остальные оставляйте как в полном примере:
| Изменение | Что должно получиться |
|---|---|
quantityInput = "1" |
1341 ₽ за товар после скидки + 350 ₽ доставка = 1691 ₽ |
quantityInput = "0" |
Ошибка количества |
quantityInput = "2.5" |
Ошибка: количество не целое |
quantityInput = " " |
Ошибка: пустой ввод |
quantityInput = "кофе" |
Ошибка: результат преобразования — NaN |
hasPromoCode = false |
Сумма 4470 ₽, скидка 0 ₽, доставка бесплатная |
balanceKopecks = 300_000 |
Не хватает 1023 ₽ |
customerNameInput = " " |
Покупатель: Гость |
Граница учебного примера:
Numberпринимает некоторые дополнительные числовые форматы, например"3e0"как3. Если форма должна разрешать только десятичные цифры, понадобится отдельная проверка формата. Кроме того, входные имена и количество здесь предполагаются строками, а настройки — заранее корректными. Код ничего не списывает и не отправляет на сервер. В настоящем магазине цену, скидку, остатки и возможность оплаты обязательно проверяют на сервере: пользователь может изменить клиентский JavaScript.
18. Типичные ошибки и приёмы отладки
Новичок часто думает: «Я допустил ошибку, значит, не понимаю программирование». На практике программирование включает постоянный поиск ошибок. Умение спокойно сузить проблему полезнее способности с первого раза написать идеальную строку.
Ошибка 1. Перепутать имя переменной со строкой
const userName = "Анна";console.log(userName); // Аннаconsole.log("userName"); // userNameБез кавычек мы читаем значение переменной. В кавычках — выводим буквальный текст.
Ошибка 2. Использовать имя с другим регистром
const productPrice = 500;console.log(productPrice); // 500// console.log(productprice); // ReferenceError.Проверяйте регистр и опечатки. Для языка это разные имена.
Ошибка 3. Написать = вместо ===
Намеренно неправильная логика:
let role = "user";if (role = "admin") { console.log("Доступ разрешён"); // Выполнится!}console.log(role); // adminМы не проверили роль, а присвоили новую. Выражение присваивания вернуло строку "admin", которая truthy. Поэтому ветка выполнилась.
Правильная проверка:
const role = "user";if (role === "admin") { console.log("Доступ разрешён");} else { console.log("Доступ запрещён"); // Доступ запрещён}Это демонстрация условия, не полноценная система прав доступа. Настоящая проверка доступа должна выполняться на сервере.
Ошибка 4. Сложить числа, которые оказались строками
const firstValue = "10";const secondValue = "20";console.log(firstValue + secondValue); // 1020console.log(Number(firstValue) + Number(secondValue)); // 30Если сумма похожа на склеенные цифры, первым делом проверьте типы.
Ошибка 5. Потерять правильный ноль из-за ||
const savedDiscount = 0;const wrongDiscount = savedDiscount || 10;const correctDiscount = savedDiscount ?? 10;console.log(wrongDiscount); // 10console.log(correctDiscount); // 0Спросите себя: нужно заменить любое falsy-значение или только отсутствующее?
Ошибка 6. Проверять NaN через равенство
const amount = Number("ошибка");console.log(amount === NaN); // falseconsole.log(Number.isNaN(amount)); // trueОшибка 7. Думать, что ошибка всегда останавливает программу
Склеивание строк вместо сложения чисел не вызывает исключение. Программа может успешно завершиться и выдать неправильную для задачи сумму.
Полезно различать:
| Вид проблемы | Пример | Что это означает |
|---|---|---|
SyntaxError |
const price; |
Запись не соответствует синтаксису |
ReferenceError |
Обращение к недоступному имени | Переменную нельзя получить в этом месте |
TypeError |
Переприсваивание const |
Операция недопустима в данном случае |
| Логическая ошибка | "10" + "20" вместо суммы |
Код выполняется, но решает задачу неправильно |
Небольшой алгоритм отладки
- Сформулируйте ожидаемый результат конкретным числом или значением.
- Посмотрите, что фактически получилось.
- Выведите входные данные вместе с их типами.
- Разбейте большое выражение на промежуточные переменные.
- Проверьте крайние случаи: ноль, пустую строку, границу диапазона, отсутствие данных.
Например:
const input = "250";console.log("Вход:", input);console.log("Тип входа:", typeof input);const number = Number(input);console.log("После преобразования:", number);console.log("Тип результата:", typeof number);console.log("Конечное число:", Number.isFinite(number));Аналогия — поиск протечки. Если последовательно проверять участки трубы, найти место проблемы проще, чем смотреть только на лужу в конце.
19. Практические задания
Сначала решите задания без подсказок. Для задач на предсказание запишите не только значение, но и его тип. Затем запустите код и сравните результат с предположением.
Задание 1. Визитка
Создайте переменные с именем, городом и годом начала обучения. Выведите одну строку вида: Меня зовут Анна. Я из Казани. Изучаю JavaScript с 2026 года. Используйте шаблонный литерал.
Задание 2. Выбор объявления
Что выбрать — let или const — для каждого случая? Объясните почему.
- Название магазина, которое не переприсваивается.
- Счётчик кликов, который увеличивается.
- Массив товаров, в который добавляют элементы, но который не заменяют другим массивом.
Задание 3. Копия цены
Предскажите вывод:
let price = 100;const oldPrice = price;price += 50;console.log(oldPrice);console.log(price);Задание 4. Скидка
Товар стоит 2400 рублей. Скидка — 15%. Рассчитайте размер скидки и итоговую цену в отдельных переменных.
Задание 5. Минуты и секунды
Есть 367 секунд. Получите количество полных минут и оставшихся секунд. Ожидаемый результат: 6 мин. 7 сек.
Задание 6. Числа или строки?
Предскажите значения и типы результатов:
console.log("10" + 5);console.log("10" - 5);console.log(2 + 3 + "4");console.log("2" + 3 + 4);console.log(Number(""));Задание 7. Проверка возраста
Возраст приходит строкой "21". Преобразуйте его в число и проверьте, попадает ли он в диапазон от 18 до 65 включительно.
Задание 8. Доступ к мероприятию
Вход разрешён, если мероприятие открыто и у посетителя есть билет либо он находится в списке гостей. Создайте три логические переменные и выразите правило одной проверкой. Закрытое мероприятие недоступно даже посетителю из списка.
Задание 9. Имя по умолчанию
В переменной nameInput находится строка из трёх пробелов. Получите имя для отображения: если после удаления пробелов по краям строка пуста, используйте "Гость".
Задание 10. Сохранить ноль
Значение savedVolume равно 0. Создайте volume, которое получает 50 только при null или undefined. Ноль должен сохраниться.
Задание 11. Счётчики
Предскажите результат:
let counter = 4;const first = counter++;const second = ++counter;console.log(first, second, counter);Задание 12. Общий объект
Предскажите вывод и объясните его:
const first = { score: 10 };const second = first;second.score += 5;console.log(first.score);console.log(first === second);Задание 13. Проверка количества
Количество товара приходит строкой. Разрешены только непустые значения, которые после преобразования являются целыми числами от 1 до 20. Напишите проверку и испытайте её на "5", "0", "2.5", "abc", " ".
Задание 14. Бесплатная доставка
Сумма товаров — 2800 рублей. Доставка бесплатна от 3000 рублей или при наличии подписки. В остальных случаях она стоит 300 рублей. Используйте логический и тернарный операторы.
Задание 15. Почините расчёт
В примере три независимые ошибки в логике. Должно получиться: количество 2, сумма 1000, скидка 0, проверка количества true.
const price = "500";const quantity = "2";const discountFromSettings = 0;const total = price + quantity;const discount = discountFromSettings || 10;const isQuantityCorrect = quantity === 2;console.log(total, discount, isQuantityCorrect);20. Ответы и объяснения
Решение 1. Визитка
const name = "Анна";const city = "Казань";const learningStartYear = 2026;console.log( `Меня зовут ${name}. Мой город — ${city}. ` + `Изучаю JavaScript с ${learningStartYear} года.`);Формулировку про город немного изменили, чтобы не заниматься программным склонением названия. Внутри ${...} можно подставлять не только строки, но и числа.
Решение 2. Выбор объявления
- Название магазина —
const: переприсваивание не требуется. - Счётчик —
let: значение будет меняться. - Массив —
const: добавление элементов не заменяет саму ссылку на массив.
Решение 3. Копия цены
Программа выведет 100, затем 150. В oldPrice сохранилось числовое значение на момент присваивания; автоматической связи с price нет.
Решение 4. Скидка
const price = 2400;const discountPercent = 15;const discountAmount = price * discountPercent / 100;const finalPrice = price - discountAmount;console.log(discountAmount); // 360console.log(finalPrice); // 2040Решение 5. Минуты и секунды
const totalSeconds = 367;const minutes = Math.floor(totalSeconds / 60);const seconds = totalSeconds % 60;console.log(`${minutes} мин. ${seconds} сек.`); // 6 мин. 7 сек.Решение 6. Числа или строки?
| Выражение | Значение | Тип |
|---|---|---|
"10" + 5 |
"105" |
string |
"10" - 5 |
5 |
number |
2 + 3 + "4" |
"54" |
string |
"2" + 3 + 4 |
"234" |
string |
Number("") |
0 |
number |
В третьей строке сначала складываются два числа. В четвёртой склеивание начинается с первого оператора.
Решение 7. Проверка возраста
const ageInput = "21";const age = Number(ageInput);const isAgeSuitable = age >= 18 && age <= 65;console.log(isAgeSuitable); // trueДля полноценной формы добавьте требования к формату и целочисленности возраста. Здесь исходная строка задана условием.
Решение 8. Доступ к мероприятию
const isEventOpen = true;const hasTicket = false;const isOnGuestList = true;const canEnter = isEventOpen && (hasTicket || isOnGuestList);console.log(canEnter); // trueСкобки отражают смысл правила: сначала выбираем допустимое основание для входа, а открытость мероприятия обязательна для всех.
Если написать isEventOpen && hasTicket || isOnGuestList, человек из списка получит true даже при закрытом мероприятии. Это уже другое правило.
Решение 9. Имя по умолчанию
const nameInput = " ";const displayName = nameInput.trim() || "Гость";console.log(displayName); // ГостьЗдесь || выбран намеренно: пустая строка должна заменяться.
Решение 10. Сохранить ноль
const savedVolume = 0;const volume = savedVolume ?? 50;console.log(volume); // 0Решение 11. Счётчики
Результат — 4 6 6. После counter++ переменная становится равной 5, но в first записывается прежнее значение 4. Затем ++counter увеличивает значение до 6 и возвращает 6.
Решение 12. Общий объект
Результат — 15 и true. Переменные указывают на один объект, поэтому изменение свойства видно через обе переменные.
Решение 13. Проверка количества
const input = "5";const normalizedInput = input.trim();const quantity = Number(normalizedInput);const isValid = normalizedInput !== "" && Number.isInteger(quantity) && quantity >= 1 && quantity <= 20;console.log(isValid); // trueПри замене input на "0", "2.5", "abc" или " " результат будет false.
Решение 14. Бесплатная доставка
const goodsTotal = 2800;const hasSubscription = true;const isDeliveryFree = goodsTotal >= 3000 || hasSubscription;const deliveryPrice = isDeliveryFree ? 0 : 300;console.log(deliveryPrice); // 0Промежуточная переменная isDeliveryFree делает условие понятнее и позволяет отдельно проверить его в консоли.
Решение 15. Исправленный расчёт
const priceInput = "500";const quantityInput = "2";const discountFromSettings = 0;const price = Number(priceInput);const quantity = Number(quantityInput);const total = price * quantity;const discount = discountFromSettings ?? 10;const isQuantityCorrect = quantity === 2;console.log(quantity); // 2console.log(total); // 1000console.log(discount); // 0console.log(isQuantityCorrect); // trueМы выбрали умножение для стоимости заказа, явно преобразовали входные строки и сохранили допустимую нулевую скидку с помощью ??.
21. Шпаргалка и дальнейшие шаги
Переменные и типы
| Нужно сделать | Что использовать |
|---|---|
| Объявить переменную без будущего переприсваивания | const price = 100 |
| Объявить изменяемый счётчик | let count = 0 |
| Узнать тип | typeof value |
| Преобразовать строку в число | Number(value) |
| Получить строку | String(value) |
| Получить логическое значение | Boolean(value) |
Проверить NaN |
Number.isNaN(value) |
| Проверить конечное число | Number.isFinite(value) |
| Проверить целое число | Number.isInteger(value) |
| Подставить значение в текст | Обратные кавычки и ${value} |
Операторы
| Нужно сделать | Запись |
|---|---|
| Присвоить значение | value = 10 |
| Прибавить к переменной | value += 10 |
| Сравнить без приведения типов | a === b |
| Проверить неравенство без приведения типов | a !== b |
| Потребовать оба условия | a && b |
| Потребовать хотя бы одно условие | a || b |
| Получить логическое отрицание | !value |
Подставить запасное значение при null или undefined |
value ?? fallback |
Прочитать свойство, учитывая возможный null/undefined слева |
object?.property |
| Выбрать одно из двух значений | condition ? first : second |
| Получить остаток | a % b |
| Возвести в степень | a ** b |
Что ещё есть в языке
Мы подробно разобрали основу, но не все операторы JavaScript. Позже вам встретятся in, instanceof, delete, new, побитовые операторы и другие возможности.
Например, & и | работают с битовым представлением чисел и не являются заменой логическим && и ||. Если случайно пропустить один символ в условии, смысл программы изменится.
На первом этапе важнее уверенно считать стоимость, проверять диапазон, отличать строку от числа и понимать выбор запасного значения, чем пытаться охватить весь справочник языка.
Семь привычек, которые сэкономят много времени
- Выбирайте
constпо умолчанию,let— для переприсваивания. - Называйте переменную по смыслу и при необходимости указывайте единицы измерения.
- Преобразуйте входные данные явно и проверяйте их отдельно от расчёта.
- Начинайте со строгих сравнений
===и!==. - Помните, что
&&и||возвращают операнды, а не обязательно булевы значения. - Различайте ноль, пустую строку и отсутствие значения.
- Выбирайте читаемую запись, а не минимальное количество символов.
Что изучать дальше
Логичное продолжение — подробнее разобрать if и switch, затем циклы, функции, массивы и объекты. После этого можно перейти к DOM — работе с элементами страницы, событиям кнопок и форм.
Например, наш калькулятор заказа уже содержит вычислительную основу. Следующий шаг — получать количество из поля, пересчитывать итог при изменении и выводить результат рядом с кнопкой заказа.
Но фундамент остаётся тем же:
- переменные дают данным имена;
- типы определяют характер значений;
- операторы выполняют действия;
- условия помогают выбрать дальнейшее поведение.
Программирование начинается не с умения писать сложные конструкции. Оно начинается с привычки задавать точные вопросы: «Какое значение здесь находится? Какой у него тип? Что сделает этот оператор? Что должно произойти на границе?».
Если вы можете объяснить своими словами, почему "5" + 2 превращается в "52", почему 0 ?? 100 остаётся нулём и почему изменение свойства объекта разрешено при const, у вас уже есть хорошая опора для следующего шага.