Значения, функции, массивы и объекты

Зачем: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);
Что получилось
[
  'Первая услуга',
  'Третья услуга'
]
А исходный список items после этого
[
  { 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: свернуть массив в одно значение. Исходный массив они не меняют.

  • Что такое область видимости?

    Ответ

    Место, где имя переменной существует. Переменная из функции или блока снаружи не видна, а внешняя изнутри видна.

Источники

Дальше

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