Значения, функции, массивы и объекты
Зачем:HTML и CSS ничего не решают: решает JavaScript. После урока вы читаете и пишете короткие программы: храните пункты вашего списка в массиве объектов, превращаете их функциями в другие данные и знаете, чем отличаются тип, переменная и область видимости.
25 минут
Озвучено синтезом речи (Qwen3-TTS), голос не принадлежит человеку.
JavaScript — язык программирования браузера. Он работает со значениями разных типов, хранит данные в массивах и объектах и обрабатывает их функциями.
Язык, который умеет решать
HTML говорит, что есть на странице. CSS говорит, как это выглядит. Но ни тот, ни другой не умеют решать: открыть меню по нажатию, проверить, что телефон введён целиком, показать только те пункты списка, которые нашёл посетитель. Для этого нужен язык программирования, и в браузере он один: JavaScript (его часто сокращают до JS).
Коротко о его истории. В 1995 году в компании Netscape (она встречалась нам в рассказе о браузерных войнах) Брендан Эйх написал язык для её браузера; по его собственным словам, первый вариант был готов за десять дней. Имя JavaScript язык получил из-за моды на Java, и общее у них только начало слова. Потом Microsoft сделала собственную копию для Internet Explorer, и у веб-разработчиков настали годы, когда один и тот же скрипт приходилось писать дважды. Чтобы с этим покончить, язык отдали организации Ecma International. Его стандарт называется ECMAScript, а новая редакция выходит раз в год. Исполняет код в браузере «движок»: V8 в Chrome и в браузерах на его основе, SpiderMonkey в Firefox, JavaScriptCore в Safari.
Где его писать
Мест два.
- Консоль в инструментах разработчика (F12, вкладка «Консоль»; вы открывали её на рабочем месте). Набрали строку, нажали Enter, увидели результат. Это лучший способ попробовать что-то на пять секунд.
- Тег
<script>на странице. Браузер выполняет его код, когда доходит до него при разборе страницы. Вспомните конвейер из урока о рендеринге: пока обычный скрипт не выполнится, разбор HTML ждёт, и скрипт в начале страницы не видит элементов, которые ещё не разобраны. Самый простой способ избежать этого сегодня:<script type="module">. Такой скрипт выполняется только после разбора страницы, а всё, что он объявил, остаётся внутри него и не мешает другим скриптам. (Старые способы: класть скрипт в самый конец<body>или добавлять атрибутdefer.)
В настоящем проекте код живёт в отдельном файле (<script type="module" src="script.js"></script>). В нашем редакторе скрипт пишется прямо в страницу, внутрь тега: так проверки могут прочитать ваш код. Ничего страшного, это один и тот же JavaScript. О том, что такое модуль, мы поговорим позже, в уроке про модули; пока достаточно слов «такому скрипту можно доверять, что страница уже готова».
Значения и типы
Программа работает со значениями, и у каждого значения есть тип:
- число (
number):42,3.14. Целые и дробные в JavaScript одного типа; - строка (
string): текст в кавычках,'Скатерть'или"Скатерть"; - логическое значение (
boolean):trueилиfalse, «да» и «нет»; undefined: «значения нет», например переменную объявили и ничего в неё не положили;null: «здесь нарочно пусто»;- объекты, к которым относятся и массивы, и функции (о них ниже).
Тип любого значения показывает оператор typeof: typeof 42 даёт 'number'.
Значение кладут в переменную: имя, по которому его потом достают.
const price = 4500; // const: имя навсегда привязано к значению
let count = 0; // let: значение можно будет заменить
count = count + 1; Правило такое: по умолчанию пишите const, а let берите, когда значение действительно должно меняться. Есть и третье слово, var. Это единственный способ объявить переменную до 2015 года, и у него странные правила: переменная «подпрыгивает» к началу функции и видна там, где вы её не ждёте. В новом коде var не пишут. Если встретите его в старом, это знак возраста кода.
Сравнивать значения надо тройным знаком равенства: ===. Он сравнивает и значение, и тип. Двойной == сначала пытается привести типы друг к другу, и результаты удивляют: 0 == '' оказывается истиной. Привыкайте к === и забудьте про ==.
Типы иногда подставляют ловушки. Плюс умеет и складывать, и склеивать строки, поэтому '5' + 3 даёт строку '53', а '5' - 3 даёт число 2. Значения из полей формы всегда приходят строками, и Number('42') превращает строку в число. Покрутите примеры:
typeof 42
'number'
Тип результата: string. typeof называет тип значения. У целых и дробных чисел он один: number.
Ответы здесь записаны заранее: страница не запускает введённый текст как код. Попробуйте те же строки в консоли браузера.
Проверьте себя: что даст '5' + 3 и почему?
Строку '53'. Если хотя бы один операнд строка, плюс склеивает текст, а не складывает числа.
Объекты и массивы
Объект это карточка: набор пар «ключ: значение». Один пункт вашего списка (услуга, товар, блюдо) естественно записывается объектом:
const item = { title: 'Скатерть', price: '0 000 ₽' };
item.title; // 'Скатерть'
item.price; // '0 000 ₽' Массив это пронумерованный список. Номера идут с нуля, длина лежит в свойстве length:
const names = ['Скатерть', 'Фартук', 'Полотенца'];
names[0]; // 'Скатерть'
names.length; // 3 Вместе они дают главный способ хранить данные: массив объектов. Список items из вашего брифа выглядит именно так, и дальше на нём держится вся часть: его рисуют, фильтруют и получают с сервера. Запись, очень похожая на неё, называется JSON, и мы разберём её в уроке про fetch.
const items = [
{ title: 'Скатерть', price: '0 000 ₽' },
{ title: 'Фартук', price: '' }
]; Функции
Функция это именованная последовательность действий. Ей передают данные (параметры), она возвращает результат (return).
function priceOf(item) {
return item.price;
}
const titleOf = (item) => item.title; // стрелочная запись, с 2015 года Две записи делают почти одно и то же; стрелочную пишут, когда функция короткая и нужна на месте. Вызывают функцию по имени со скобками: priceOf(items[0]). Функция без return возвращает undefined.
Лучшая привычка: делайте функции чистыми. Чистая функция берёт данные из параметров, возвращает результат и больше ничего не трогает. Такую легко проверить («дал это, получил то»), легко переиспользовать, и в ней негде спрятаться ошибке «кто-то поменял мой массив».
Область видимости говорит, где имя существует. Переменная, объявленная внутри функции или блока { ... }, видна только там. Изнутри можно смотреть наружу, снаружи внутрь нельзя. Благодаря этому две функции могут иметь по переменной total, и они не перепутаются. (Есть и тонкость: функция запоминает переменные того места, где её создали. Это называется замыканием, и именно оно позволит вам в уроке про события держать состояние меню в одном месте.)
Проверьте себя: почему console.log(secret) снаружи функции даёт ошибку, если secret объявлен внутри неё?
Переменная живёт только в своей области видимости. Снаружи функции такого имени не существует.
map, filter, reduce: конвейер вместо цикла
Работу над списком часто описывают циклом со счётчиком. Привычнее и надёжнее описывать её методами массива, которые принимают функцию:
map(f)возвращает новый массив той же длины: к каждому элементу применилиf;filter(f)возвращает новый массив из тех элементов, для которыхfвернулаtrue;reduce(f, начало)сворачивает массив в одно значение: сумму, самый большой, самый дешёвый;join(', ')склеивает массив строк в одну строку.
Главное свойство: исходный массив они не меняют. А вот push, sort, splice меняют его на месте, и из-за них чаще всего ломаются программы («я только отсортировал для показа, а список пропал в другом месте»). Методы можно соединять в цепочку, и она читается как предложение:
items
.filter((item) => item.price !== '') // оставить с ценой
.map((item) => item.title) // взять названия
.join(', '); // склеить в строку Соберите конвейер на пунктах вашего списка. Это те же пункты, что в брифе:
items .filter((item) => item.price !== '') .map((item) => item.title);
[ 'Первая услуга', 'Третья услуга' ]
[
{ title: 'Первая услуга', price: '0 000 ₽' },
{ title: 'Вторая услуга', price: '' },
{ title: 'Третья услуга', price: '0 000 ₽' }
]Исходный массив остался как был. map и filter каждый раз делают новый массив, а старый не трогают. Поэтому их можно соединять в цепочку, не боясь что-нибудь испортить.
Читаем ошибки
Красные строки в консоли не приговор, а подсказка: в них названа ошибка, сказано, что не так, и указаны файл и строка. Три самые частые:
ReferenceError: x is not defined: опечатка в имени или обращение к переменной вне её области видимости;TypeError: Cannot read properties of undefined (reading 'title'): вы взяли поле у того, чего нет, напримерitems[10].title, хотя в списке три пункта;SyntaxError: потерянная скобка, кавычка или запятая. Скрипт при этом не запускается вообще.
А console.log(значение) остаётся главным способом заглянуть внутрь программы: что лежит в переменной в этом месте. Прочитайте ошибку целиком и сверху, прежде чем менять что-то наугад.
Практика
Вы на сайте эпохи 2014 года, где страница уже адаптивная, а скрипта ещё нет. Добавьте первый. Он возьмёт список ваших пунктов, посчитает по нему сводку и покажет её в подвале.
Данные пока лежат прямо в скрипте, продублированные из разметки. Это временно: чуть дальше (уроки про DOM и fetch) мы научим скрипт читать их со страницы и с сервера. Написанный здесь код пригодится: те же функции будут работать на любых данных.
Заметьте, как устроены проверки. Они читают текст вашего скрипта: есть ли массив объектов с вашими названиями, нет ли var, нет ли push. Что скрипт делает, проверка не запускает: это вы видите сами, в подвале предпросмотра должна появиться строка вида «Всего позиций: 3, с ценой: 3. Скатерть, Фартук…». Если не появилась, откройте консоль окна предпросмотра (F12; в списке контекстов консоли выберите рамку предпросмотра) и прочитайте красную строку.
Что было не так раньше
В 2000-е JavaScript писали иначе. Обработчики вешали прямо в разметку (onclick="..."), ссылки делали с адресом javascript:void(0), значения держали в глобальных переменных, а сообщения показывали окошком alert(). Результат: логика размазана по HTML, любое имя может быть случайно перезаписано другим скриптом, а без скриптов ссылки не работают. Определяли браузер по его названию (navigator.userAgent), потому что Internet Explorer и Netscape отличались.
Сохранилось и правильное: маленькие функции с понятными именами и один источник данных вместо копий. А то, что в 2015 году появились const, let и стрелочные функции, а потом язык стал выходить обновлениями каждый год, привело к тому, что код 2025 года заметно короче и надёжнее кода 2005 года.
Практика
Готовлю вашу страницу…
Тест
Вопрос 1 из 7
Карточки
Эти карточки попадут в повторение, когда вы завершите урок.
Какие бывают типы значений в JavaScript?
Ответ
Число, строка, логическое значение (true и false), undefined (нет значения), null (нарочно пусто), а ещё объекты, в том числе массивы и функции. Тип показывает typeof.
Чем const отличается от let, и почему не var?
Ответ
const: имя привязано к значению навсегда. let: значение можно менять. var устарел: у него странная область видимости, в новом коде его нет. По умолчанию берут const.
Чем === отличается от ==?
Ответ
=== сравнивает значение и тип. == сначала приводит типы и даёт сюрпризы (0 == "" истинно). Всегда пишите ===.
Что делают map, filter и reduce?
Ответ
map: новый массив, где к каждому элементу применили функцию. filter: новый массив из подходящих. reduce: свернуть массив в одно значение. Исходный массив они не меняют.
Что такое область видимости?
Ответ
Место, где имя переменной существует. Переменная из функции или блока снаружи не видна, а внешняя изнутри видна.
Источники
- Руководство по JavaScript (MDN) CC BY-SA, по-русски
- Основы JavaScript (MDN) CC BY-SA, по-русски
- Современный учебник JavaScript (learn.javascript.ru) только ссылка, по-русски
- JavaScript (Википедия) CC BY-SA, по-русски
- Eloquent JavaScript (Марейн Хавербеке) только ссылка, по-английски
Дальше
Теперь вы знаете: значения и типы, функции и область видимости, массивы и объекты.