В прошлом уроке мы научились хранить списки: покупки, оценки, города. Массив отлично подходит, когда вещей много и они однотипные. Но как описать одну вещь, у которой много разных свойств — студента, товар, заказ такси? Для этого в JavaScript есть объекты. Почти всё, что приходит с сервера и показывается на сайте, — это объекты и массивы объектов. Сегодня соберём всё вместе: функции, массивы и объекты — и научимся ставить ИИ точные задачи на этом языке.
Создавать объекты, читать, менять, добавлять и удалять их свойства
Работать с вложенными данными и массивами объектов — как в настоящих ответах сервера
Писать методы объекта и понимать, на что указывает this
Быстро доставать данные деструктуризацией и делать копии через ...
Превращать объекты в JSON и обратно, безопасно читать данные от сервера
Писать для ИИ точные промпты на языке JS и проверять его ответы
01 Что такое объект и зачем он нужен
Вспомним массив: ["нон", "чой", "самбуса"]. Это список однотипных вещей, и к каждой мы обращаемся по номеру — индексу. А теперь попробуем описать одного студента: имя, возраст, город, курс. Можно и массивом:
Массивом — неудобно
Что лежит в student[2]? Нужно помнить порядок. Перепутали местами — получили ошибку, которую трудно заметить.
JavaScript
const student = ["Фарход", 20, "Душанбе", "Frontend"];
console.log(student[2]); // а это что? город? курс?
Объектом — понятно
У каждого значения есть название. Читаешь код — и сразу понятно, что где лежит.
JavaScript
const student = { name: "Фарход", age: 20, city: "Душанбе", course: "Frontend" };
console.log(student.city); // сразу видно: город
Аналогия из жизни
Вспомните паспорт или анкету при поступлении в MakeFirstStep. Там нет «строки номер 3» — там есть поля с названиями: «Фамилия», «Дата рождения», «Город». У каждого поля есть название и содержимое. Объект устроен точно так же: это набор пар «название → значение».
Объект
Набор пар «ключ: значение» в фигурных скобках { }. Описывает одну вещь со многими свойствами.
Свойство
Одна пара внутри объекта, например city: "Душанбе". Как одно поле в анкете.
Ключ
Название свойства (city). Всегда строка, даже если пишем без кавычек.
Значение
То, что хранится под ключом ("Душанбе"). Может быть чем угодно: число, строка, массив, другой объект, функция.
Массив или объект?
Ситуация
Что выбрать
Пример
Много однотипных вещей, важен порядок
Массив[ ]
оценки за месяц, список покупок, города маршрута
Одна вещь с названными характеристиками
Объект{ }
анкета студента, паспорт, карточка товара, профиль в Telegram
Много однотипных вещей, у каждой есть свойства
Массив объектов
список товаров магазина, студенты группы (тема 02)
Как создать объект
Объект пишут в фигурных скобках. Внутри — свойства через запятую. Каждое свойство: ключ, двоеточие, значение. Такая запись называется литерал объекта.
const student = { name: "Фарход", age: 20 };
Ключ и значение разделяет двоеточие :, а свойства между собой — запятая ,
Последняя строка — частая ошибка. product.field ищет свойство, которое буквально называется field. А product[field] сначала берёт значение переменной ("title") и уже потом ищет такой ключ.
Нет такого свойства — получаем undefined
Можно редактировать · Ctrl + Enter
const user = { name: "Мадина" };
console.log(user.age); // → undefined
console.log(user.length); // → undefined (у объекта нет length, как у массива)
Ошибки нет: JavaScript просто говорит «тут ничего нет». Ошибка будет, если попытаться взять свойство у самого undefined, например user.address.city. Как защититься — разберём в теме 02.
Изменить, добавить, удалить
Объект можно менять после создания. Присвоили значение существующему ключу — изменили. Новому ключу — добавили. Оператор delete удаляет свойство. А оператор in проверяет, есть ли ключ.
const запрещает переприсвоить переменную — положить в неё другой объект. Но менять содержимое объекта можно. Представьте: на коробке приклеена наклейка «card». Наклейку на другую коробку не переклеишь, а вот вещи внутри коробки менять можно.
Часто значения уже лежат в переменных с такими же именами. Тогда вместо { name: name, age: age } можно написать просто { name, age }. Это одно и то же.
Обратите внимание на круглые скобки: () => ({ ... }). Без них JavaScript решит, что { — это начало тела функции, а не объект, и функция вернёт undefined.
Как перебрать все свойства
У массива есть for...of и методы. Объект напрямую так перебрать нельзя, но есть три помощника, которые превращают объект в массив:
Object.keys(obj) — массив ключей;
Object.values(obj) — массив значений;
Object.entries(obj) — массив пар [ключ, значение].
Можно редактировать · Ctrl + Enter
const grades = { html: 5, css: 4, js: 5 };
console.log(Object.keys(grades)); // → ["html", "css", "js"]
console.log(Object.values(grades)); // → [5, 4, 5]
console.log(Object.entries(grades)); // → [["html", 5], ["css", 4], ["js", 5]]// сколько свойств в объекте
console.log(Object.keys(grades).length); // → 3// средняя оценка: values + reduce из прошлого урокаconst sum = Object.values(grades).reduce((total, g) => total + g, 0);
console.log(sum / Object.keys(grades).length); // → 4.666666666666667
Чаще всего перебирают пары через for...of по Object.entries. Запись const [subject, grade] раскладывает пару на две переменные — подробно разберём это в теме 05, а пока просто запомните форму.
Есть и старый способ — цикл for...in. Он перебирает ключи, а значение берём через квадратные скобки:
Можно редактировать · Ctrl + Enter
const grades = { html: 5, css: 4, js: 5 };
for (const key in grades) {
console.log(key, grades[key]);
}
// → html 5// → css 4// → js 5
Частые ошибки
1) grades.key внутри for...in — ищет ключ «key». Нужно grades[key].
2) obj.length — у объекта нет длины. Используйте Object.keys(obj).length.
3) { name = "Фарход" } — внутри литерала ставят двоеточие, а не =.
4) Забыли запятую между свойствами — SyntaxError.
ИИ-подсказка
Если не уверены, объект нужен или массив, опишите данные ИИ словами: «я храню 30 студентов, у каждого имя, город и оценки — как лучше организовать данные в JS?». Хороший ответ — массив объектов. Но решение принимайте сами: вы теперь знаете правило.
Практика: закрепляем
Начнём с анкеты, потом поработаем с карточкой товара, а в конце посчитаем голоса — объект как «счётчик».
ЗАДАЧА 1.1
Анкета студента MakeFirstStep
лёгкая
Создайте объект student со свойствами: name — «Мадина», age — 19, city — «Худжанд», course — «Frontend + ИИ». Выведите первую строку через точку, вторую — через квадратные скобки. Затем добавьте свойство phone со значением "+992 92 555 11 22" и выведите, сколько всего свойств в анкете.
Можно редактировать · Ctrl + Enter
// 1. Создайте объект student// 2. Выведите: Мадина, 19 лет, Худжанд (через точку)// 3. Выведите: Курс: Frontend + ИИ (через student["..."])// 4. Добавьте phone и выведите количество свойств
Ожидаемый результат
Мадина, 19 лет, Худжанд
Курс: Frontend + ИИ
Свойств в анкете: 5
Подсказка 1
Для строки удобно использовать шаблонную строку: `${student.name}, ...`. Добавить свойство — значит просто присвоить: student.phone = ....
Подсказка 2
Количество свойств: Object.___(student).length. Помните, что student.length вернёт undefined.
ЗАДАЧА 1.2
Карточка товара на Корвоне
средняя
Продавец на Корвоне обновляет карточку казана. Не создавайте объект заново — меняйте существующий product: уменьшите цену на 30 сомони (используйте текущую цену, а не пишите 290 вручную), добавьте category со значением «Посуда», удалите свойство inStock. Затем выведите объект, проверьте через in, остался ли ключ inStock, и выведите все свойства в формате ключ: значение.
Можно редактировать · Ctrl + Enter
const product = { title: "Казан 10 л", price: 320, inStock: true };
// 1. Скидка 30 сомони// 2. Добавьте category// 3. Удалите inStock// 4. Выведите product, проверку "inStock" in product и все пары ключ: значение
Уменьшить на 30: product.price = product.price - 30 (или короче -=). Удалить: оператор delete.
Подсказка 2
Для вывода пар: for (const [key, value] of Object.___(product)) { ... }.
ЗАДАЧА 1.3
Что заказать на обед?
сложная
Группа голосует, что заказать из чайханы. Напишите функцию countVotes(votes), которая принимает массив голосов и возвращает объект-счётчик: ключ — блюдо, значение — сколько раз за него проголосовали. Затем выведите объект, количество разных блюд и победителя (блюдо с наибольшим числом голосов).
Можно редактировать · Ctrl + Enter
const votes = [
"оши палав", "самбуса", "курутоб", "оши палав", "самбуса",
"оши палав", "шакароб", "курутоб", "оши палав"
];
functioncountVotes(votes) {
const result = {};
// ваш код здесь: пройдите по votes и увеличивайте счётчик result[блюдо]return result;
}
const counts = countVotes(votes);
console.log(counts);
// выведите количество блюд и победителя
Ключ лежит в переменной (dish), поэтому только квадратные скобки: result[dish]. Если такого ключа ещё нет, его значение — undefined, и сначала нужно положить туда 1.
Подсказка 2
Внутри цикла: if (dish in result) { result[dish] = ___ } else { result[dish] = ___ }. Для победителя заведите let bestDish = ""; let bestCount = 0; и пройдите по Object.entries(counts), запоминая максимум.
02 Вложенность: объекты в объектах, массивы объектов
Значением свойства может быть что угодно — в том числе другой объект или массив. Так и выглядят настоящие данные: у пользователя есть адрес (объект), список навыков (массив), а у группы — массив студентов, каждый из которых — объект.
Аналогия из жизни
Папка «Группа FE-07» в шкафу. В ней лежит листок с именем ментора и ещё одна папка «Студенты». В папке «Студенты» — анкеты, каждая на своём листке. Чтобы найти город второго студента, вы открываете: группа → папка студентов → вторая анкета → поле «Город». В коде путь записывается точно так же: group.students[1].city.
Можно редактировать · Ctrl + Enter
const user = {
name: "Шахром",
age: 22,
address: { // объект внутри объекта
city: "Худжанд",
street: "ул. Камоли Худжанди"
},
skills: ["HTML", "CSS"] // массив внутри объекта
};
console.log(user.address.city); // → Худжанд
console.log(user.skills[0]); // → HTML
console.log(user.skills.length); // → 2
user.address.zip = "735700"; // добавили свойство во вложенный объект
user.skills.push("JavaScript"); // методы массива работают как обычно
console.log(user.skills); // → ["HTML", "CSS", "JavaScript"]
Массив объектов — самая частая форма данных
Когда сайт запрашивает у сервера список товаров, студентов или заказов, почти всегда приходит массив объектов. Вот как выглядит группа студентов:
group { }
name: "FE-07"
mentor { }group.mentor
name: "Далер" group.mentor.name
students [ ]group.students
[0] { }
name: "Фарход", city: "Душанбе"
[1] { }group.students[1]
name: "Мадина" group.students[1].name
Зелёные рамки — объекты (берём по ключу через точку), жёлтая — массив (берём по индексу в скобках)
.students — достаём из него свойство students. Это массив.
[1] — у массива берём элемент с индексом 1 — второй объект.
.name — у этого объекта берём имя: "Мадина".
Совет
Если путь не работает, выводите его по частям: console.log(group), потом console.log(group.students), потом console.log(group.students[1]). Сразу увидите, на каком шаге данные не такие, как вы ждали.
Методы массивов + объекты
Всё, что вы выучили в прошлом уроке, отлично работает с массивом объектов. В колбэк приходит целый объект, и мы берём у него нужное свойство.
А теперь цепочка: «все товары для кухни дороже 100 сомони — в виде строк для витрины», и сортировка по цене. Сортируем копию (slice()), чтобы не испортить исходный массив.
Данные от сервера не всегда полные. У одного пользователя есть адрес, у другого — нет. Если взять свойство у undefined, программа упадёт с ошибкой TypeError, и весь код ниже не выполнится:
Этот код выдаст ошибку · Ctrl + Enter
const users = [
{ name: "Далер", address: { city: "Куляб" } },
{ name: "Сабрина" } // адреса нет
];
console.log(users[0].address.city); // → Куляб
console.log(users[1].address.city); // TypeError: адрес undefined, у него нет city
console.log("Эта строка уже не выполнится");
Оператор опциональной цепочки?. говорит: «если слева undefined или null — дальше не иди, просто верни undefined». А оператор ?? позволяет подставить значение по умолчанию, если слева undefined или null. Их часто используют вместе.
Можно редактировать · Ctrl + Enter
const users = [
{ name: "Далер", address: { city: "Куляб" } },
{ name: "Сабрина" }
];
console.log(users[1].address?.city); // → undefined (без ошибки)
console.log(users[1].address?.city ??"город не указан"); // → город не указанfor (const u of users) {
console.log(`${u.name}: ${u.address?.city ?? "—"}`);
}
// → Далер: Куляб// → Сабрина: —// ?. работает и с массивамиconst empty = [];
console.log(empty[0]?.name); // → undefined
Не ставьте ?. везде подряд
?. нужен там, где данные по-настоящему могут отсутствовать (необязательное поле, пустой ответ). Если свойство обязательно должно быть, а его нет — лучше получить ошибку и найти баг, чем молча показать пустоту.
ИИ-подсказка
Когда просите ИИ обработать данные, вставляйте в промпт пример объекта — 2–3 элемента массива. Без примера ИИ гадает, как называются поля (name или title? price или cost?), и пишет код под выдуманные данные.
Практика: закрепляем
Три задачи с настоящими «ответами сервера»: бронь отеля, группа студентов и заказы такси, где данных иногда не хватает.
ЗАДАЧА 2.1
Бронь отеля в Хороге
лёгкая
Пришла бронь отеля. Достаньте данные по путям и выведите пять строк, как в образце. Сумму к оплате посчитайте из nights и pricePerNight, количество услуг и последнюю услугу берите из массива (не пишите числа и слова вручную).
Бронь HRG-2041
Гость: Рустам, +992 93 777 00 11
Отель: Памир Лодж (Хорог), 3★
Услуг: 3, первая — завтрак, последняя — экскурсия
К оплате: 1350 сомони
Подсказка 1
Путь к имени гостя: booking.guest.name. К городу: booking.hotel.city.
Подсказка 2
Последний элемент массива: booking.services[booking.services.length - ___].
ЗАДАЧА 2.2
Группа FE-07
средняя
Ментор хочет быстрый отчёт по группе. Выведите: строку о группе и менторе; имя второго студента в списке; массив имён студентов из Душанбе; балл Нилуфар (найдите её через find); средний балл группы.
Группа FE-07, ментор Далер (@daler_js)
Второй в списке: Мадина
["Фарход", "Шахром", "Абдулло"]
Нилуфар: 90
Средний балл группы: 81.6
Подсказка 1
Имена из Душанбе — цепочка из двух методов: сначала отобрать (filter), потом превратить объекты в имена (map).
Подсказка 2
Средний балл: group.students.reduce((sum, s) => sum + s.___, 0) / group.students.___.
ЗАДАЧА 2.3
Заказы такси: где водитель?
сложная
Приложение такси получает заказы. У некоторых водитель ещё не найден (driver равен null или его вовсе нет), у некоторых не указана машина. Напишите функцию describeOrder(order), которая возвращает строку для одного заказа (см. образец). Программа не должна падать с ошибкой. Затем выведите строки для всех заказов и посчитайте, у скольких заказов водитель есть.
Можно редактировать · Ctrl + Enter
const orders = [
{ id: 101, from: "Шохмансур", to: "Ирам", driver: { name: "Шахром", car: { model: "Toyota Prius", number: "1234 AA 01" } } },
{ id: 102, from: "Корвон", to: "проспект Рудаки", driver: null },
{ id: 103, from: "Варзоб", to: "Душанбе", driver: { name: "Абдулло" } },
{ id: 104, from: "Аэропорт", to: "Шохмансур" }
];
functiondescribeOrder(order) {
// ваш код здесь
}
// выведите describeOrder для каждого заказа и строку "С водителем: ..."
Ожидаемый результат
Заказ 101: Шахром, Toyota Prius (1234 AA 01)
Заказ 102: водитель ещё не найден
Заказ 103: Абдулло, машина не указана
Заказ 104: водитель ещё не найден
С водителем: 2
Подсказка 1
Сначала проверьте, есть ли водитель: if (!order.driver?.name) return .... Затем — есть ли машина: order.driver.car?.model.
Подсказка 2
Подсчёт: orders.filter(o => o.driver?.___).length. ?. спокойно работает и с null, и с отсутствующим свойством.
03 Методы объекта
Значением свойства может быть и функция. Такая функция внутри объекта называется методом. Свойства описывают, какой объект (имя, цена, город), а методы — что он умеет делать (поздороваться, посчитать сумму, применить скидку).
Аналогия из жизни
Чайник: у него есть свойства — цвет, объём, сколько воды налито. И есть кнопка «вскипятить» — это действие. В объекте свойства — это данные, а методы — это кнопки, которые что-то делают.
Метод
Функция, которая лежит в свойстве объекта.
Вызов метода
Запись объект.метод() — со скобками! Без скобок вы просто получите саму функцию, но не запустите её.
Можно редактировать · Ctrl + Enter
const chaikhana = {
name: "Рохат",
city: "Душанбе",
greet: function () {
console.log("Хуш омадед в чайхану!");
}
};
chaikhana.greet(); // → Хуш омадед в чайхану!
console.log(typeof chaikhana.greet); // → function
Короткая запись метода
Слово function и двоеточие можно опустить: greet() { ... }. Это современная запись, её вы будете видеть чаще всего. Методы, как обычные функции, принимают параметры и возвращают значения через return.
Можно редактировать · Ctrl + Enter
const calc = {
add(a, b) {
return a + b;
},
multiply(a, b) {
return a * b;
},
percent(sum, p) {
return sum * p / 100;
}
};
console.log(calc.add(25, 17)); // → 42
console.log(calc.multiply(4, 12)); // → 48
console.log(calc.percent(300, 15)); // → 45
Вы уже давно пользуетесь методами
Каждый раз, когда вы ставили точку и скобки, вы вызывали метод встроенного объекта. console — это объект, а log — его метод! Math — объект с математическими методами. Строки и массивы тоже имеют методы.
Так что arr.push(...) и calc.add(...) — одна и та же идея: у объекта есть свойство-функция, и мы его вызываем через точку.
Забыли скобки
Метод не запустился — вывелась сама функция.
JavaScript
console.log(calc.add); // ƒ add() — это функция, а не результат
chaikhana.greet; // ничего не произошло
Со скобками
Метод выполнился и вернул результат.
JavaScript
console.log(calc.add(2, 3)); // 5
chaikhana.greet(); // Хуш омадед в чайхану!
Метод, которому нужны данные объекта
Часто методу нужны другие свойства того же объекта. Пока можно обратиться к ним по имени переменной:
Можно редактировать · Ctrl + Enter
const chaikhana = {
name: "Рохат",
open: "07:00",
info() {
return`Чайхана «${chaikhana.name}», открыта с ${chaikhana.open}`;
}
};
console.log(chaikhana.info()); // → Чайхана «Рохат», открыта с 07:00
Это работает, но неудобно: если переименовать переменную или захотеть такой же метод в другом объекте — всё сломается. Для этого есть специальное слово this — следующая тема.
Частые ошибки
1) Вызов без скобок: obj.method вместо obj.method().
2) Опечатка в имени: calc.Add() → TypeError: calc.Add is not a function. Регистр букв важен.
3) Забыли return — метод вернёт undefined, хотя внутри всё посчитал.
Практика: закрепляем
Собираем свои «инструменты» в объекты — так же, как устроены Math и console.
ЗАДАЧА 3.1
Объект-приветствие
лёгкая
Создайте объект greeter с тремя методами (короткой записью): hello(name) возвращает приветствие, bye(name) — прощание, isMorning(hour) возвращает true, если час меньше 12. Методы должны возвращать значения, а выводит их console.log снаружи.
Можно редактировать · Ctrl + Enter
const greeter = {
// ваш код здесь
};
console.log(greeter.hello("Нилуфар"));
console.log(greeter.hello("Далер"));
console.log(greeter.bye("Нилуфар"));
console.log(greeter.isMorning(9));
console.log(greeter.isMorning(15));
Короткая запись: hello(name) { return ...; }, — не забудьте запятую между методами.
Подсказка 2
Для isMorning не нужен if: сравнение hour < 12 уже даёт true или false.
ЗАДАЧА 3.2
Набор инструментов для текста
средняя
Создайте объект textTools с методами: capitalize(word) — делает первую букву заглавной; countWords(text) — считает слова (они разделены одним пробелом); initials(fullName) — возвращает инициалы с точками.
Можно редактировать · Ctrl + Enter
const textTools = {
// ваш код здесь
};
console.log(textTools.capitalize("душанбе"));
console.log(textTools.capitalize("хорог"));
console.log(textTools.countWords("Ассалому алейкум дорогие студенты"));
console.log(textTools.initials("Мадина Рахимова"));
Ожидаемый результат
Душанбе
Хорог
4
М. Р.
Подсказка 1
Первая буква — word[0], остаток — word.slice(1). Чтобы разбить текст на слова, используйте text.split(" ") — получится массив.
Допишите методы объекта library. byAuthor(author) — массив названий книг автора; availableTitles() — названия книг, которые есть в наличии; oldest() — объект самой старой книги; addBook(book) — добавляет книгу и возвращает новое количество книг. Внутри методов обращайтесь к книгам через library.books (в следующей теме узнаем способ лучше).
["Бухара", "Рабы", "Одина"]
["Шахнаме", "Мать вина", "Рабы"]
Самая старая: Мать вина, 933
Книг в библиотеке: 6
["Бухара", "Рабы", "Одина", "Смерть ростовщика"]
Подсказка 1
byAuthor и availableTitles — это filter, а потом map по названию. Помните: push возвращает новую длину массива — это как раз нужно для addBook.
Подсказка 2
Самая старая книга: library.books.reduce((oldest, b) => (b.year < oldest.year ? ___ : ___), library.books[0]).
04 this — «этот объект»
Внутри метода часто нужно сказать: «возьми имя этого объекта». Для этого есть слово this. Правило, которое нужно запомнить:
Главное правило
this — это объект, который стоит перед точкой в момент вызова метода. Вызвали student.introduce() — значит внутри this равен student.
Аналогия из жизни
Слово «я». Когда Фарход говорит «я из Душанбе», «я» — это Фарход. Когда ту же фразу говорит Мадина, «я» — это Мадина. Фраза одна, а смысл зависит от того, кто говорит. this — это «я» для метода: зависит от того, у какого объекта метод вызвали.
Можно редактировать · Ctrl + Enter
const student = {
name: "Манижа",
city: "Истаравшан",
introduce() {
console.log(`Салом! Я ${this.name} из города ${this.city}`);
}
};
student.introduce(); // → Салом! Я Манижа из города Истаравшан
student.city = "Душанбе"; // переехала
student.introduce(); // → Салом! Я Манижа из города Душанбе
Вызов student.introduce(). JavaScript смотрит, что стоит слева от точки, — это student.
this = student. На время этого вызова this указывает на объект student.
this.name — это то же самое, что student.name, то есть "Манижа".
Метод всегда берёт свежие данные. Поменяли city — следующий вызов уже покажет новый город.
Вот где this по-настоящему полезен. Одну и ту же функцию можно положить в несколько объектов — и каждый раз this будет тем объектом, у которого её вызвали.
У стрелочной функции нет своего this. Она берёт this снаружи — оттуда, где её написали, а не от объекта перед точкой. Поэтому как метод с this она не работает:
Можно редактировать · Ctrl + Enter
const user = {
name: "Рустам",
hello: () => {
console.log(this=== user); // стрелочная: this взят снаружи объекта
},
hi() {
console.log(this=== user); // обычный метод: this — объект перед точкой
}
};
user.hello(); // → false
user.hi(); // → true
Если бы внутри hello было this.name, вы получили бы не «Рустам», а пустоту или undefined. Зато стрелочная функция идеальна внутри метода — например, как колбэк для map: она возьмёт this у метода, а значит, у объекта.
Можно редактировать · Ctrl + Enter
const shop = {
discount: 10,
prices: [100, 250, 40],
withDiscount() {
// стрелочный колбэк берёт this из метода withDiscount, то есть shopreturnthis.prices.map(p => p - p * this.discount / 100);
}
};
console.log(shop.withDiscount()); // → [90, 225, 36]
Правило на каждый день
Метод объекта пишем короткой записью method() { }. Колбэки внутри метода (map, filter, reduce) — стрелочными. Тогда this всегда будет тем, чем нужно.
Ловушка 2: метод «оторвали» от объекта
Если сохранить метод в отдельную переменную и вызвать её, перед точкой уже ничего нет. В строгом режиме ("use strict" — он включён в нашем запуске кода и во всех современных проектах) this становится undefined, и обращение this.name падает:
Этот код выдаст ошибку · Ctrl + Enter
const user = {
name: "Рустам",
introduce() {
console.log(`Я ${this.name}`);
}
};
user.introduce(); // → Я Рустамconst f = user.introduce; // взяли функцию без объектаf(); // TypeError: this — undefined, у него нет name
Как исправить: вызывать через объект — user.introduce(), или обернуть в стрелочную функцию: const f = () => user.introduce();. Так часто бывает, когда метод передают в другую функцию, например setTimeout(user.introduce, 1000) — там тоже потеряется this.
Можно редактировать · Ctrl + Enter
const user = {
name: "Рустам",
introduce() {
console.log(`Я ${this.name}`);
}
};
const f = () => user.introduce(); // обёртка: внутри вызов снова через точкуf(); // → Я Рустам
Для управления this есть ещё методы bind, call и apply — например, user.introduce.bind(user) навсегда «привязывает» объект. Мы разберём их позже, сейчас достаточно правила «объект перед точкой».
ИИ-подсказка
ИИ иногда пишет методы стрелочными функциями «для краткости» — и this ломается. Увидели method: () => { ... this ... } в ответе ИИ — сразу просите переписать на method() { }.
Практика: закрепляем
Учимся писать методы с this и находить две главные ловушки.
ЗАДАЧА 4.1
Преподаватель представляется
лёгкая
Допишите объекту teacher метод introduce(), который выводит фразу через this, и метод changeSubject(newSubject), который меняет предмет. Затем создайте объект teacher2 с именем «Сабрина», предметом «CSS» и тем же самым методом introduce (возьмите его из teacher, не пишите заново).
Можно редактировать · Ctrl + Enter
const teacher = {
name: "Далер",
subject: "JavaScript",
// ваши методы здесь
};
teacher.introduce();
teacher.changeSubject("React");
teacher.introduce();
// создайте teacher2 и вызовите teacher2.introduce()
Ожидаемый результат
Меня зовут Далер, я веду JavaScript
Меня зовут Далер, я веду React
Меня зовут Сабрина, я веду CSS
Подсказка 1
Внутри методов используйте this.name и this.subject, а не teacher.name — иначе teacher2 будет говорить чужим именем.
Подсказка 2
const teacher2 = { name: "Сабрина", subject: "CSS", introduce: teacher.___ }; — без скобок, мы берём саму функцию.
ЗАДАЧА 4.2
Банковская карта
средняя
Сделайте объект карты (вымышленный учебный пример). Методы: deposit(amount) — пополнить баланс и записать в history строку вида "+200"; pay(shop, amount) — если денег хватает, списать, записать "-320 Корвон" и вывести сообщение об оплате, иначе вывести отказ и ничего не списывать; report() — вывести итог. Все методы работают через this.
Оплата в Корвон: 320 сомони. Остаток: 180
Пополнение: +200. Баланс: 380
Оплата в Чайхана Рохат: 85 сомони. Остаток: 295
Недостаточно средств для Такси: нужно 500, на карте 295
Манижа: баланс 295 сомони, операций: 3
["-320 Корвон", "+200", "-85 Чайхана Рохат"]
Подсказка 1
В pay сначала проверка: if (amount > this.balance) { ...; return; }. Ранний return выходит из метода, и списание ниже не выполнится.
Подсказка 2
Запись в историю: this.history.push(`-${amount} ${shop}`). Количество операций — длина this.history.
ЗАДАЧА 4.3
Найдите две ошибки с this
сложная
Этот код такси-водителя написан с двумя ошибками, связанными с this. Сначала запустите его и прочитайте ошибку. Найдите обе проблемы и исправьте их (менять можно только способ объявления метода и способ вызова). Затем создайте второго водителя taxi2 — «Шахром», 0 поездок, 0 заработано — с теми же методами, взятыми из taxi, добавьте ему поездку за 30 сомони и выведите его info().
Первая ошибка — в том, как объявлен addTrip: у такой функции нет своего this. Вторая — в том, как вызывается info: что стоит перед точкой при вызове showInfo()?
Подсказка 2
addTrip(price) { ... } и console.log(taxi.___()). Для второго водителя: addTrip: taxi.addTrip, info: taxi.info.
05 Деструктуризация
Деструктуризация — короткий способ «разложить» объект или массив по отдельным переменным. Вместо трёх строк const name = user.name; const age = user.age; ... — одна.
Аналогия из жизни
Вы пришли с базара с пакетом: дыня, нон, гранат. Можно каждый раз лезть в пакет — «пакет, дай дыню». А можно сразу разложить: дыню на стол, нон в хлебницу, гранат в вазу. Деструктуризация — это и есть «разложить содержимое по местам», чтобы дальше брать сразу.
Из объекта: по именам ключей
Можно редактировать · Ctrl + Enter
const user = { name: "Зарина", age: 21, city: "Пенджикент" };
// по-старомуconst userCity = user.city;
// деструктуризация: берём ключи name и age в одноимённые переменныеconst { name, age } = user;
console.log(name, age); // → Зарина 21
console.log(userCity); // → Пенджикент
Слева от = фигурные скобки означают не «создать объект», а «достать из объекта». Имена переменных должны совпадать с ключами, порядок не важен.
Переименование и значения по умолчанию
Можно редактировать · Ctrl + Enter
const user = { name: "Зарина", age: 21, city: "Пенджикент" };
// name: userName — «возьми ключ name, но назови переменную userName»const { name: userName } = user;
console.log(userName); // → Зарина// значение по умолчанию — если ключа нет (undefined)const { country = "Таджикистан", city = "Душанбе" } = user;
console.log(country); // → Таджикистан (ключа не было)
console.log(city); // → Пенджикент (ключ был — default не нужен)
Как запомнить двоеточие
В деструктуризации { откуда: куда }: слева от двоеточия — ключ объекта, справа — имя новой переменной. А = — значение по умолчанию.
Вложенная деструктуризация
Можно редактировать · Ctrl + Enter
const order = {
id: 77,
customer: { name: "Фарход", phone: "+992 90 111 22 33" },
address: { city: "Душанбе", street: "проспект Рудаки, 45" }
};
const { id, customer: { name }, address: { street } } = order;
console.log(id, name, street); // → 77 Фарход проспект Рудаки, 45// customer и address тут — только «путь», переменных с такими именами нет
Из массива: по позиции
Для массива используются квадратные скобки, и переменные берутся по порядку, а не по именам.
Можно редактировать · Ctrl + Enter
const route = ["Душанбе", "Варзоб", "Искандеркуль"];
const [start, stop, finish] = route;
console.log(start); // → Душанбе
console.log(finish); // → Искандеркуль// пропустить элемент — просто запятаяconst [, , lake] = route;
console.log(lake); // → Искандеркуль// поменять две переменные местамиlet first = "чой";
let second = "шакароб";
[first, second] = [second, first];
console.log(first, second); // → шакароб чой
В параметрах функции
Самое частое применение: функция принимает объект и сразу раскладывает его. Видно, какие поля ей нужны, и можно задать значения по умолчанию.
Можно редактировать · Ctrl + Enter
functionshowProduct({ title, price, currency = "сомони" }) {
console.log(`${title}: ${price} ${currency}`);
}
showProduct({ title: "Гранат", price: 15 }); // → Гранат: 15 сомониshowProduct({ title: "Черешня", price: 3, currency: "$" }); // → Черешня: 3 $// в колбэках массивовconst fruits = [{ title: "Дыня", price: 12 }, { title: "Виноград", price: 18 }];
console.log(fruits.map(({ title }) => title)); // → ["Дыня", "Виноград"]// и в переборе пар объекта — теперь понятно, что тут происходитfor (const [fruit, kg] of Object.entries({ зардолу: 3, гранат: 2 })) {
console.log(`${fruit} — ${kg} кг`);
}
// → зардолу — 3 кг// → гранат — 2 кг
Почему это повсюду в React
Во frontend-фреймворке React, который вы будете изучать, компонент получает данные как объект — и почти всегда его сразу деструктурируют. А хук useState возвращает массив из двух элементов, который раскладывают по позициям. Пока просто узнайте знакомую форму:
1) Опечатка в имени: const { nmae } = user — получите undefined, ошибки не будет.
2) Деструктуризация из undefined: const { name } = undefined → TypeError.
3) Переменная уже объявлена: const { name } = user, когда name уже есть выше → SyntaxError: ... has already been declared. Используйте переименование.
4) Значение по умолчанию срабатывает только для undefined, а не для null или 0.
Практика: закрепляем
Раскладываем погоду, витрину магазина и сложный ответ сервера с бронью.
ЗАДАЧА 5.1
Погода в Душанбе
лёгкая
Одной строкой деструктуризации достаньте city, temp и sky. Отдельной строкой достаньте wind под именем windSpeed и humidity со значением по умолчанию 25 (в объекте её нет). Используйте только новые переменные, без weather. в выводе.
Можно редактировать · Ctrl + Enter
const weather = { city: "Душанбе", temp: 31, wind: 4, sky: "ясно" };
// ваш код здесь
Ожидаемый результат
Душанбе: +31°C, ясно
Ветер: 4 м/с
Влажность: 25%
Подсказка 1
Первая строка: const { city, temp, sky } = weather;
Напишите функцию finalPrice({ price, discount = 0 }), которая возвращает цену со скидкой в процентах, и функцию formatProduct({ title, price, discount = 0 }), которая возвращает строку для витрины. Если скидки нет, в скобках ничего не пишем. Выведите все товары и итоговую сумму всех цен со скидкой.
Цена со скидкой: price - price * discount / 100. Если discount не передан, сработает значение по умолчанию 0 — и цена не изменится.
Подсказка 2
Внутри formatProduct вызовите finalPrice({ price, discount }). Итог: products.reduce((sum, p) => sum + finalPrice(___), 0).
ЗАДАЧА 5.3
Ответ сервера с бронью
сложная
Сервер прислал глубоко вложенный ответ. Одним выражением деструктуризации достаньте: id; имя гостя под именем guestName; даты въезда и выезда в переменные checkIn и checkOut; город отеля под именем hotelCity; тип первой комнаты под именем roomType; и breakfast из booking со значением по умолчанию false. Затем выведите четыре строки.
Три точки ... в JavaScript работают в двух ролях. Spread («распаковка») раскладывает массив или объект на отдельные элементы. Rest («остаток») наоборот собирает отдельные элементы в массив или объект. Запись одна, а смысл зависит от места.
Spread для массивов
Можно редактировать · Ctrl + Enter
const fruits = ["зардолу", "гранат"];
const more = ["дыня", "виноград"];
// объединитьconst all = [...fruits, ...more];
console.log(all); // → ["зардолу", "гранат", "дыня", "виноград"]// добавить элемент в начало или конец, не меняя исходный массивconst withCherry = ["черешня", ...fruits];
console.log(withCherry); // → ["черешня", "зардолу", "гранат"]// разложить массив в аргументы функцииconst prices = [12, 45, 7];
console.log(Math.max(...prices)); // → 45
Копия или ссылка?
Очень важная вещь. Знак =не копирует массив или объект — он даёт второе имя тому же самому. Настоящая копия — через spread.
Аналогия из жизни
const b = a — это второй ключ от той же квартиры: переставили диван — увидят оба хозяина. const b = [...a] — это новая квартира с такой же мебелью: в одной меняете что угодно, другая остаётся прежней.
Можно редактировать · Ctrl + Enter
const fruits = ["зардолу", "гранат"];
const same = fruits; // НЕ копия — то же самое
same.push("дыня");
console.log(fruits); // → ["зардолу", "гранат", "дыня"] (испортили!)const copy = [...fruits]; // настоящая копия
copy.push("виноград");
console.log(fruits); // → ["зардолу", "гранат", "дыня"] (не изменился)
console.log(copy); // → ["зардолу", "гранат", "дыня", "виноград"]
Spread для объектов
С объектами — так же: копировать, объединять и «менять одно поле» в копии. Если ключ встречается дважды, побеждает последний.
Можно редактировать · Ctrl + Enter
const user = { name: "Далер", age: 20, city: "Куляб" };
// копия с изменённым полемconst older = { ...user, age: 21 };
console.log(user.age, older.age); // → 20 21// объединить два объектаconst contacts = { phone: "+992 91 000 11 22", telegram: "@daler" };
const full = { ...user, ...contacts };
console.log(Object.keys(full)); // → ["name", "age", "city", "phone", "telegram"]// порядок важен: последний побеждаетconst wrong = { age: 21, ...user };
console.log(wrong.age); // → 20 (age из user перезаписал 21)
Почему frontend любит новые копии
Во frontend (и особенно в React) принято не менять старые данные, а создавать новые копии с изменениями. Это называется неизменяемость (immutability). Зачем? Программа сравнивает старые данные с новыми: если объект тот же самый (вы его просто поправили), она решает, что ничего не изменилось, и не обновляет экран. Новая копия — чёткий сигнал: «данные изменились, перерисуй».
Полную (глубокую) копию можно сделать через structuredClone — разберём в теме 07.
Rest: собрать «всё остальное»
В параметрах функции ... собирает все переданные аргументы в массив. Так можно написать функцию, которая принимает сколько угодно чисел.
Можно редактировать · Ctrl + Enter
functionsum(...nums) {
console.log(nums); // это обычный массивreturn nums.reduce((total, n) => total + n, 0);
}
console.log(sum(10, 20, 5)); // → [10, 20, 5]// → 35
console.log(sum()); // → []// → 0// rest после обычных параметров: первый — отдельно, остальные — в массивfunctionorder(customer, ...dishes) {
console.log(`${customer} заказал(а): ${dishes.length} блюда`);
}
order("Зарина", "курутоб", "самбуса", "чой"); // → Зарина заказал(а): 3 блюда
В деструктуризации rest собирает «остаток» объекта или массива. Очень удобно, чтобы убрать лишние поля — например, пароль перед отправкой данных на экран.
function f(...args), const { password, ...rest } = user
Ограничение
Можно несколько раз в одном выражении
Только один раз и только в конце
Частые ошибки
1) Думать, что = копирует массив или объект.
2) Rest не в конце: function f(...items, last) → SyntaxError.
3) Забыть, что копия поверхностная, и менять вложенный объект «копии».
4) { age: 21, ...user } вместо { ...user, age: 21 } — новое значение перезаписывается старым.
Практика: закрепляем
Собираем меню, обновляем профиль без мутаций и прячем секретные данные.
ЗАДАЧА 6.1
Меню чайханы
лёгкая
Через spread создайте fullMenu: сначала все блюда завтрака, потом обеда, в конце «шакароб». Создайте specials: «курутоб», а за ним все блюда обеда. Исходные массивы меняться не должны. Выведите fullMenu, specials, lunch и количество блюд в полном меню.
Можно редактировать · Ctrl + Enter
const breakfast = ["чой", "нон", "каймак"];
const lunch = ["оши палав", "самбуса"];
// ваш код здесь
Внутри квадратных скобок можно смешивать spread и обычные элементы: [...a, "x"].
Подсказка 2
const specials = ["курутоб", ___];
ЗАДАЧА 6.2
Профиль без мутаций
средняя
Напишите две функции, которые не меняют исходный объект, а возвращают новый: moveTo(profile, city) — профиль с другим городом; addSkill(profile, skill) — профиль, где skills — новый массив с добавленным навыком. Последняя строка проверяет, что массивы навыков действительно разные.
moveTo — одна строка: копия объекта и новое значение city после неё.
Подсказка 2
({ ...profile, skills: [...profile.___, skill] }). Если сделать profile.skills.push(skill), вы измените исходный массив — последняя строка покажет true, а первая — лишний навык.
ЗАДАЧА 6.3
Доставка, секреты и очередь
сложная
Напишите три функции. total(deliveryFee, ...prices) — сумма доставки и всех цен (цен может быть сколько угодно, даже ноль). hideSecrets(account) — новый объект без password и pin (через rest в деструктуризации). nextInQueue(queue) — строка о первом в очереди и количестве ожидающих; разложите массив прямо в параметрах функции.
75
15
{ login: "rustam92", name: "Рустам", city: "Бохтар" }
qwerty
Первый в очереди: Зарина, ждут: 3
Подсказка 1
В total параметр prices — массив, его удобно сложить через reduce с начальным значением.
Подсказка 2
const { password, pin, ...rest } = account; внутри hideSecrets. Для очереди: function nextInQueue([first, ...___]) { ... }.
07 JSON: как данные путешествуют
Сервер и браузер — это разные программы, часто на разных компьютерах. Объект JavaScript нельзя «передать по проводу» как есть: по сети передаётся текст. Поэтому придумали JSON (JavaScript Object Notation) — текстовый формат для обмена данными. Он выглядит почти как объект JS, но это строка.
Аналогия из жизни
Вы переезжаете из Худжанда в Душанбе. Шкаф целиком в машину не влезет — вы его разбираете, упаковываете в коробки с инструкцией, а на новом месте собираете обратно. JSON.stringify — «разобрать и упаковать» объект в строку. JSON.parse — «собрать обратно» объект из строки.
Где вы встретите JSON:
Ответы сервера. Погода, список товаров, заказы — всё приходит строкой JSON, которую нужно превратить в объект.
localStorage — хранилище в браузере. Оно умеет хранить только строки, поэтому объекты кладут туда в виде JSON.
Чтобы JSON было удобно читать человеку, передайте ещё два аргумента: JSON.stringify(obj, null, 2). Третий аргумент — сколько пробелов в отступе, второй пока всегда null.
const text = '{"city":"Душанбе","temp":31,"sunny":true}';
console.log(text.temp); // → undefined (у строки нет свойства temp!)const weather = JSON.parse(text);
console.log(typeof text, typeof weather); // → string object
console.log(weather.temp + 1); // → 32
console.log(weather.city); // → Душанбе
Ошибки разбора и try/catch
Если строка — не правильный JSON (одинарные кавычки, лишняя запятая, обрезанный ответ), JSON.parse выбрасывает ошибку SyntaxError, и программа останавливается. Чтобы этого не было, используют конструкцию try...catch:
try { ... } — «попробуй выполнить». Если всё хорошо, блок catch пропускается.
catch (error) { ... } — «если внутри try что-то упало, приходи сюда». В error лежит объект ошибки, у него есть name и message.
Программа продолжает работать со следующей строки после catch.
Можно редактировать · Ctrl + Enter
const bad = "{'city': 'Хорог'}"; // одинарные кавычки — это не JSONtry {
const data = JSON.parse(bad);
console.log(data.city);
} catch (error) {
console.log("Не удалось прочитать данные"); // → Не удалось прочитать данные
console.log(error.name); // → SyntaxError
}
console.log("Программа работает дальше"); // → Программа работает дальше
Сохранение в localStorage
localStorage хранит данные в браузере даже после перезагрузки страницы — например, корзину или тему оформления. Он принимает только строки, поэтому без JSON никак. Этот код запускайте у себя в проекте: во встроенном окошке запуска localStorage может быть недоступен.
Помните ловушку поверхностной копии из темы 06? Есть два способа сделать полную копию со всеми вложенными объектами:
Можно редактировать · Ctrl + Enter
const original = { name: "Шахром", address: { city: "Бохтар" }, skills: ["HTML"] };
const copy1 = JSON.parse(JSON.stringify(original)); // старый способconst copy2 = structuredClone(original); // современный способ
copy1.address.city = "Душанбе";
copy2.skills.push("CSS");
console.log(original.address.city); // → Бохтар (не изменился)
console.log(original.skills); // → ["HTML"]
У JSON-способа есть минус: он теряет функции и undefined. structuredClone аккуратнее, но функции тоже не копирует (выдаст ошибку). Для обычных данных — объектов, массивов, строк и чисел — подходят оба.
Реальный пример: погода в Душанбе
Так примерно выглядит ответ сервиса погоды. Мы превращаем строку в объект, достаём нужное деструктуризацией и ищем самый жаркий день.
Если JSON.parse ругается, а строка длинная — вставьте её в чат с ИИ и попросите: «найди, где здесь нарушены правила JSON, и покажи исправленную строку». Но потом обязательно проверьте исправленную строку сами через JSON.parse.
Практика: закрепляем
Упаковываем данные в JSON, разбираем прогноз погоды и учимся не падать на сломанных данных.
ЗАДАЧА 7.1
Упаковать дыню
лёгкая
Превратите объект fruit в JSON-строку json. Выведите саму строку, её тип, а затем красивую версию с отступом в 2 пробела.
Можно редактировать · Ctrl + Enter
const fruit = { name: "Дыня", from: "Бохтар", price: 12, weightKg: 3, fresh: true };
// ваш код здесь
Сервер прислал строку с прогнозом. Превратите её в объект и выведите: город; массив дней, когда максимум выше 30°; среднюю максимальную температуру за 5 дней.
Можно редактировать · Ctrl + Enter
const response = '{"city":"Худжанд","days":[{"day":"Пн","max":29},{"day":"Вт","max":33},{"day":"Ср","max":35},{"day":"Чт","max":30},{"day":"Пт","max":27}]}';
// ваш код здесь
Ожидаемый результат
Город: Худжанд
Жаркие дни: ["Вт", "Ср"]
Средняя температура: 30.8°C
Подсказка 1
Сначала const data = JSON.parse(response); — дальше работаете с data.days как с обычным массивом объектов. Обратите внимание: 30 — это не «выше 30».
Подсказка 2
Жаркие дни: filter, потом map(d => d.___). Вывод массива рядом с текстом: console.log("Жаркие дни:", hot).
ЗАДАЧА 7.3
Сообщения чата и «хранилище»
сложная
Часть сообщений пришла повреждённой. Напишите safeParse(str): возвращает объект или null, если строка — не JSON (используйте try...catch). Выведите каждое сообщение или пометку о повреждении (номер считайте с 1), затем сколько прочитано. Во второй части объект storage изображает localStorage (хранит только строки): напишите save(key, value) и load(key), где load использует safeParse.
Можно редактировать · Ctrl + Enter
const messages = [
'{"from":"Фарход","text":"Салом!"}',
"{'from':'Мадина','text':'Рахмат'}",
'{"from":"Далер","text":"Хуш омадед",}',
'{"from":"Зарина","text":"Дарс соат 18:00"}'
];
functionsafeParse(str) {
// ваш код здесь
}
// часть 1: выведите сообщения и "Прочитано: ... из ..."// часть 2const storage = {};
// function save(key, value) { ... }// function load(key) { ... }// save("draft", { to: "Мадина", text: "Завтра в 10" });// console.log(typeof storage.draft);// console.log(load("draft").text);// console.log(load("missing"));
Ожидаемый результат
Фарход: Салом!
Сообщение 2 повреждено
Сообщение 3 повреждено
Зарина: Дарс соат 18:00
Прочитано: 2 из 4
string
Завтра в 10
null
Подсказка 1
В try верните результат JSON.parse(str), в catch — null. JSON.parse(undefined) тоже выбрасывает ошибку, поэтому load("missing") сам вернёт null.
Подсказка 2
Для номеров перебирайте через for (let i = 0; i < messages.length; i++) и выводите i + 1. save: storage[key] = JSON.___(value); load: return safeParse(storage[key]).
08 Итоговая практика
Пора соединить всё, что вы знаете за три урока: функции, массивы с методами, объекты, деструктуризацию, spread и JSON. Такие задачи — это уже маленькие кусочки настоящих приложений: магазина, журнала оценок, сервиса такси.
Посмотрите на форму данных. Что это: массив объектов? Объект с массивом внутри? Выведите один элемент через console.log.
Разбейте задачу на шаги. «Отобрать → посчитать → отсортировать → вывести». Каждый шаг — один метод или маленькая функция.
Проверяйте после каждого шага. Написали filter — выведите результат, и только потом пишите map.
Не портите исходные данные. Перед sort делайте копию [...arr], вместо изменения объекта — { ...obj, поле: значение }.
Практика: большие задачи
Не торопитесь. Решайте по шагам и проверяйте промежуточные результаты.
ЗАДАЧА 8.1
Корзина интернет-магазина
лёгкая
Есть каталог товаров и корзина, где для каждого товара указан только id и количество. Для каждой позиции корзины найдите товар в каталоге и выведите строку чека. Затем выведите сумму. Доставка 15 сомони, но бесплатно, если сумма от 150 сомони. В конце — сумма к оплате.
Для каждой позиции: const product = products.find(p => p.id === item.___);. Удобно сначала сделать массив строк чека через map: { title, price, qty, sum }.
Подсказка 2
Сумма — reduce по корзине. Доставка: const delivery = sum >= 150 ? 0 : 15;, а для текста — ещё одна проверка: delivery === 0 ? "бесплатно" : ___.
ЗАДАЧА 8.2
Рейтинг группы MakeFirstStep
средняя
У каждого студента массив оценок за три проекта. Создайте новый массив withAverage, где у каждого студента есть ещё поле average — средний балл, округлённый до одного знака после точки. Выведите рейтинг по убыванию среднего, лучшего студента и средний балл группы по всем оценкам (тоже с одним знаком). Последняя строка проверяет, что исходный массив не изменился.
Округление до одного знака: Math.round(x * 10) / 10. Новый объект с дополнительным полем: students.map(s => ({ ...s, average: ___ })).
Подсказка 2
Рейтинг: [...withAverage].sort((a, b) => b.average - a.average), номер места — индекс + 1 (второй параметр колбэка). Средний по группе: сначала соберите все оценки в один массив через spread, например в reduce: (all, s) => [...all, ...s.scores].
ЗАДАЧА 8.3
Отчёт сервиса такси
сложная
Сервер прислал JSON-строку с заказами за день. Превратите её в массив, оставьте только выполненные (status === "done"), посчитайте выручку. Соберите объект-сводку по водителям: для каждого — сумма и количество поездок. Выведите водителей по убыванию суммы. В конце найдите самую дорогую выполненную поездку и выведите её, разложив объект деструктуризацией.
Сводка по водителям — объект, где ключ — имя: { Шахром: { sum: 62, trips: 2 }, ... }. Собирайте его циклом по выполненным заказам, как счётчик голосов в задаче 1.3. Чтобы отсортировать, превратите его в массив через Object.entries.
Подсказка 2
Сортировка пар: Object.entries(stats).sort((a, b) => b[1].sum - a[1].sum), а в цикле — for (const [driver, { sum, trips }] of ___). Самая дорогая — reduce с начальным значением done[0], затем const { from, to, price, driver } = ___;.
09 Лайфхаки: как писать код быстрее с ИИ
За три урока вы выучили словарь, на котором говорит фронтенд: функции, массивы, методы, объекты. Теперь вы можете не просто «просить ИИ сделать сайт», а ставить точные задачи и проверять ответы. Именно этим хороший разработчик с ИИ отличается от человека, который копирует код наугад.
1. Говорите на языке JS
Чем точнее вы называете структуры и методы, тем точнее ответ. Сравните:
Сделай чтобы показывались товары которые дешёвые и посчитай сколько всего
Есть массив объектов products с полями title (строка) и price (число, в сомони).
Напиши стрелочную функцию cheapSummary(products), которая:
1) через filter оставляет товары с price меньше 100;
2) через map превращает их в строки вида "Пиала — 15 сомони";
3) через reduce (с начальным значением 0) считает сумму их цен.
Верни объект { lines, total }. Исходный массив не изменяй.
Во втором промпте нет ни одного лишнего слова, но ИИ уже не может ошибиться в форме ответа: вы назвали данные, методы, имя функции и формат результата.
2. Давайте ИИ форму данных
Вставляйте пример данных (2–3 объекта, а не 500) и точный ожидаемый вывод. Это самый сильный приём: ИИ видит и вход, и выход.
Данные:
const orders = [
{ id: 1, driver: "Шахром", price: 22, status: "done" },
{ id: 2, driver: "Абдулло", price: 35, status: "cancelled" }
];
Нужно получить ровно такой вывод в консоль:
Шахром: 22 сомони
Итого: 22 сомони
Отменённые заказы не считаем.
3. Генерируйте тестовые данные
Не тратьте время на ручной набор 15 товаров. ИИ отлично генерирует данные — особенно если сразу указать поля, типы и местный контекст.
Сгенерируй JSON-массив из 15 товаров для интернет-магазина в Душанбе.
Поля: id (число от 1), title (строка на русском: продукты, посуда, сувениры), price (целое число в сомони от 3 до 900), category (одна из: "продукты", "кухня", "сувениры"), inStock (true/false).
Сделай 3 товара с inStock: false и хотя бы 2 товара дороже 500.
Ответ — только валидный JSON, без комментариев.
Просьба про «3 товара с false» и «2 дороже 500» — не случайна: так в данных точно будут случаи, на которых проверяется ваш код.
4. Просите объяснить построчно и предсказать вывод
Получили код — попросите: «объясни каждую строку на русском и напиши, что выведет console.log». Затем нажмите ▶ Запустить и сравните. Если ИИ предсказал неверно — значит, он сам не до конца понимает код, и доверять ему без проверки нельзя.
5. Рефакторинг — с условием «не меняя поведение»
Перепиши этот код через стрелочные функции, деструктуризацию в параметрах и методы массивов вместо for. Поведение и вывод в консоль менять нельзя. После кода покажи, что именно ты изменил, списком.
[ваш код]
После рефакторинга запустите старый и новый код на одних и тех же данных: вывод должен совпасть символ в символ.
6. Отладка: код + точный текст ошибки + что ожидали
не работает почему
Код ниже падает с ошибкой:
TypeError: Cannot read properties of undefined (reading 'name')
Ожидал, что выведется "Я Рустам".
Объясни причину простыми словами и покажи минимальное исправление, не переписывая всё.
[ваш код]
7. Просите крайние случаи
Код, который работает на «хороших» данных, может упасть на реальных. Добавляйте в промпт: «проверь, что будет, если массив пустой; если у объекта нет поля address; если price равен undefined». Или попросите ИИ: «напиши 5 вызовов функции с крайними случаями и ожидаемым результатом для каждого».
8. Типичные ошибки ИИ, которые вы теперь поймаете сами
Что видите в ответе ИИ
В чём проблема
Как исправить
arr.sort(...) прямо по исходным данным
sort меняет исходный массив
[...arr].sort(...)
reduce((sum, p) => sum + p.price)
Нет начального значения — первым sum станет объект, и выйдет "[object Object]..."
reduce(..., 0)
method: () => { this.x }
У стрелочной функции нет своего this
method() { this.x }
var
Устаревшее объявление, странная область видимости
const, а где нужно менять — let
user.address.city для необязательных данных
Упадёт, если адреса нет
user.address?.city ?? "—"
arr.sum(), obj.length, arr.findAll()
Выдуманные методы — таких нет
Проверьте в документации (MDN) или запуском
const copy = obj «для копии»
Это ссылка, а не копия
{ ...obj } или structuredClone(obj)
9. Главное правило
Не вставляйте код, который не можете объяснить
Если вы не можете сказать, что делает каждая строка, — вы не сможете исправить её, когда она сломается. Двигайтесь маленькими шагами: одна функция за раз. Попросили — прочитали — запустили — поняли — только потом следующая.
Универсальный шаблон промпта
Скопируйте и заполняйте под свою задачу. Пять блоков закрывают почти все причины плохих ответов.
Контекст: я изучаю JavaScript (функции, массивы, объекты). Пишу [что за проект: магазин / журнал оценок / ...].
Данные (пример):
[вставьте 2–3 объекта или кусок JSON]
Задача: напиши функцию [имя]([параметры]), которая [что делает по шагам]. Ожидаемый результат для примера выше: [точный вывод].
Ограничения: только const/let, стрелочные функции, методы массивов (filter/map/find/reduce), деструктуризация там, где уместно. Не изменяй исходные данные. Без библиотек. Учти крайние случаи: пустой массив, отсутствующее поле.
Формат ответа: сначала код, потом объяснение по шагам на русском, потом 3 примера вызова с ожидаемым выводом.
Практика с ИИ
В этих задачах главное — не получить код, а проверить его. Результат сверяйте кнопкой «Запустить».
ЗАДАЧА 9.1
Промпт, который работает с первого раза
лёгкая
По универсальному шаблону составьте промпт, чтобы ИИ написал функцию groupByCity(students): она возвращает объект, где ключ — город, значение — массив имён студентов из этого города. Требования в промпте: через reduce, не менять исходный массив, для пустого массива вернуть {}. Вставьте ответ ИИ в код ниже и запустите. Если вывод не совпал — не исправляйте руками, а уточните промпт.
Вставьте в промпт массив students целиком и ожидаемый результат — так ИИ увидит точную форму входа и выхода.
Подсказка 2
Проверьте ответ ИИ глазами: есть ли у reduce начальное значение {}? Нет ли var? Сможете ли вы объяснить каждую строку? Если нет — попросите ИИ объяснить построчно.
ЗАДАЧА 9.2
ИИ ищет баги, вы — проверяете
средняя
В этом коде три ошибки — все из таблицы «Типичные ошибки ИИ». Сначала попробуйте найти их сами. Потом отправьте код ИИ с промптом в стиле пункта 6 (код + что выводится + что ожидали) и сравните: нашёл ли он все три? Объяснил ли причину каждой? Исправьте код и добейтесь ожидаемого вывода. Порядок товаров внутри shop.products должен остаться исходным.
Ошибки проявляются по очереди: исправите первую (код перестанет падать) — увидите вторую и третью. Смотрите на: как объявлен метод, что делает sort с исходным массивом, с чего начинает reduce.
Подсказка 2
Если ИИ нашёл только одну-две ошибки, напишите ему: «после твоего исправления shop.products[0].title выводит "Гранат", а должен "Дыня". Почему?» — и проверьте, понял ли он причину.
ЗАДАЧА 9.3
Сгенерируйте данные и проверьте функцию
сложная
Напишите функцию libraryReport(books) (сами или с ИИ — но объяснить её должны вы), которая выводит: всего книг, сколько в наличии, объект «автор → количество книг» и самую толстую книгу. Сначала добейтесь точного вывода на маленькой контрольной выборке sample. Затем попросите ИИ сгенерировать JSON-массив из 15 книг таджикских и персидских классиков (Айни, Рудаки, Фирдавси, Хайям, Саади, Джами) с полями title, author, year, pages, available — причём попросите сделать одну книгу без поля pages. Вставьте строку в generated и убедитесь, что функция не падает и считает правильно.
Всего книг: 3
В наличии: 2
По авторам: { Фирдавси: 1, Айни: 2 }
Самая толстая: Шахнаме (980 стр.)
Для generated: 4 такие же строки без ошибок; «Всего книг: 15»,
сумма чисел в объекте «По авторам» тоже равна 15,
книга без pages не ломает поиск самой толстой.
Подсказка 1
Подсчёт по авторам — тот же приём «объект-счётчик», что в задаче 1.3. Для книги без pages используйте b.pages ?? 0.
Подсказка 2
Как проверить ответ ИИ: посчитайте вручную 2–3 значения в сгенерированных данных (например, сколько книг Айни) и сравните с выводом. Если ИИ прислал JSON с комментариями или запятой в конце — JSON.parse упадёт: попросите «только валидный JSON».
Итоги урока
Объект — одна вещь с названными свойствами: { name: "Фарход", age: 20 }. Массив — список однотипных вещей. Массив объектов — самая частая форма данных.
Чтение: obj.key или obj["key"] (ключ с пробелом или в переменной: obj[field]). Нет свойства → undefined.
Изменить/добавить: obj.key = value. Удалить: delete obj.key. Проверить: "key" in obj. Объект в const менять можно, переприсвоить нельзя.
Короткая запись: { name, age }. Стрелочная функция возвращает объект в скобках: () => ({ ... }).
Перебор: Object.keys / values / entries + for (const [key, value] of Object.entries(obj)). Число свойств: Object.keys(obj).length.
JSON — текст для обмена данными: JSON.stringify(obj, null, 2) и JSON.parse(str) внутри try...catch. localStorage хранит только строки. Глубокая копия — structuredClone(obj).
С ИИ: называйте методы и структуры, давайте пример данных и точный вывод, просите крайние случаи, проверяйте запуском. Не вставляйте код, который не можете объяснить.