MakeFirstStep· Frontend + ИИ

Урок 7 · JavaScript

Объекты

В прошлом уроке мы научились хранить списки: покупки, оценки, города. Массив отлично подходит, когда вещей много и они однотипные. Но как описать одну вещь, у которой много разных свойств — студента, товар, заказ такси? Для этого в 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 };
Ключ и значение разделяет двоеточие :, а свойства между собой — запятая ,
Можно редактировать · Ctrl + Enter
const student = {
  name: "Фарход",
  age: 20,
  city: "Душанбе",
  isStudent: true
};

console.log(student);        // → { name: "Фарход", age: 20, city: "Душанбе", isStudent: true }
console.log(typeof student); // → object

const empty = {};            // пустой объект — тоже объект
console.log(empty);          // → {}

Как прочитать свойство: точка и квадратные скобки

Есть два способа. Через точку — student.name — самый частый и короткий. Через квадратные скобки — student["name"] — нужен в двух случаях:

  • в ключе есть пробел или дефис: product["старая цена"], product["made-in"];
  • имя ключа лежит в переменной: product[field].
Можно редактировать · Ctrl + Enter
const product = {
  title: "Казан",
  price: 250,
  "made-in": "Таджикистан",
  "старая цена": 300
};

console.log(product.title);          // → Казан
console.log(product["price"]);       // → 250
console.log(product["made-in"]);     // → Таджикистан
console.log(product["старая цена"]); // → 300

// ключ лежит в переменной
const field = "title";
console.log(product[field]);  // → Казан
console.log(product.field);   // → undefined (ищет ключ с именем "field")

Последняя строка — частая ошибка. 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 проверяет, есть ли ключ.

Можно редактировать · Ctrl + Enter
const order = { dish: "Оши палав", price: 30 };

order.price = 35;      // изменить: ключ price уже был
order.drink = "Чой";   // добавить: ключа drink не было
console.log(order);    // → { dish: "Оши палав", price: 35, drink: "Чой" }

delete order.drink;    // удалить
console.log(order);    // → { dish: "Оши палав", price: 35 }

console.log("price" in order); // → true
console.log("drink" in order); // → false
Важно: const и объекты

const запрещает переприсвоить переменную — положить в неё другой объект. Но менять содержимое объекта можно. Представьте: на коробке приклеена наклейка «card». Наклейку на другую коробку не переклеишь, а вот вещи внутри коробки менять можно.

Можно редактировать · Ctrl + Enter
const card = { owner: "Зарина", balance: 100 };

card.balance = 150;          // можно: меняем содержимое
console.log(card.balance);   // → 150

// card = { owner: "Зарина", balance: 0 };  // нельзя: TypeError, const не переприсваивается

Короткая запись свойств

Часто значения уже лежат в переменных с такими же именами. Тогда вместо { name: name, age: age } можно написать просто { name, age }. Это одно и то же.

Можно редактировать · Ctrl + Enter
const name = "Нилуфар";
const age = 18;
const city = "Бохтар";

const student = { name, age, city };
console.log(student); // → { name: "Нилуфар", age: 18, city: "Бохтар" }

// функция-«фабрика»: принимает данные, возвращает объект
const makeStudent = (name, city) => ({ name, city, course: "Frontend + ИИ" });
console.log(makeStudent("Далер", "Куляб")); // → { name: "Далер", city: "Куляб", course: "Frontend + ИИ" }
Ловушка: стрелочная функция возвращает объект

Обратите внимание на круглые скобки: () => ({ ... }). Без них 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, а пока просто запомните форму.

Можно редактировать · Ctrl + Enter
const grades = { html: 5, css: 4, js: 5 };

for (const [subject, grade] of Object.entries(grades)) {
  console.log(`${subject}: ${grade}`);
}
// → html: 5
// → css: 4
// → js: 5

Есть и старый способ — цикл 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 и все пары ключ: значение
Ожидаемый результат
{ title: "Казан 10 л", price: 290, category: "Посуда" }
false
title: Казан 10 л
price: 290
category: Посуда
Подсказка 1

Уменьшить на 30: product.price = product.price - 30 (или короче -=). Удалить: оператор delete.

Подсказка 2

Для вывода пар: for (const [key, value] of Object.___(product)) { ... }.

ЗАДАЧА 1.3

Что заказать на обед?

сложная

Группа голосует, что заказать из чайханы. Напишите функцию countVotes(votes), которая принимает массив голосов и возвращает объект-счётчик: ключ — блюдо, значение — сколько раз за него проголосовали. Затем выведите объект, количество разных блюд и победителя (блюдо с наибольшим числом голосов).

Можно редактировать · Ctrl + Enter
const votes = [
  "оши палав", "самбуса", "курутоб", "оши палав", "самбуса",
  "оши палав", "шакароб", "курутоб", "оши палав"
];

function countVotes(votes) {
  const result = {};
  // ваш код здесь: пройдите по votes и увеличивайте счётчик result[блюдо]
  return result;
}

const counts = countVotes(votes);
console.log(counts);

// выведите количество блюд и победителя
Ожидаемый результат
{ "оши палав": 4, самбуса: 2, курутоб: 2, шакароб: 1 }
Блюд в голосовании: 4
Победитель: оши палав (4 голоса)
Подсказка 1

Ключ лежит в переменной (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
Зелёные рамки — объекты (берём по ключу через точку), жёлтая — массив (берём по индексу в скобках)
Можно редактировать · Ctrl + Enter
const group = {
  name: "FE-07",
  mentor: { name: "Далер" },
  students: [
    { name: "Фарход", city: "Душанбе" },
    { name: "Мадина", city: "Худжанд" }
  ]
};

console.log(group.mentor.name);       // → Далер
console.log(group.students[1].name);  // → Мадина
console.log(group.students.length);   // → 2

Как читать длинный путь

  1. group — берём весь объект группы.
  2. .students — достаём из него свойство students. Это массив.
  3. [1] — у массива берём элемент с индексом 1 — второй объект.
  4. .name — у этого объекта берём имя: "Мадина".
Совет

Если путь не работает, выводите его по частям: console.log(group), потом console.log(group.students), потом console.log(group.students[1]). Сразу увидите, на каком шаге данные не такие, как вы ждали.

Методы массивов + объекты

Всё, что вы выучили в прошлом уроке, отлично работает с массивом объектов. В колбэк приходит целый объект, и мы берём у него нужное свойство.

Можно редактировать · Ctrl + Enter
const products = [
  { id: 1, title: "Ковёр", price: 900, category: "дом" },
  { id: 2, title: "Казан", price: 320, category: "кухня" },
  { id: 3, title: "Пиала", price: 15, category: "кухня" },
  { id: 4, title: "Чайник", price: 120, category: "кухня" }
];

const titles = products.map(p => p.title);
console.log(titles); // → ["Ковёр", "Казан", "Пиала", "Чайник"]

const kitchen = products.filter(p => p.category === "кухня");
console.log(kitchen.length); // → 3

const cheap = products.find(p => p.price < 100);
console.log(cheap); // → { id: 3, title: "Пиала", price: 15, category: "кухня" }

const total = products.reduce((sum, p) => sum + p.price, 0);
console.log(total); // → 1355

const hasExpensive = products.some(p => p.price > 500);
console.log(hasExpensive); // → true

А теперь цепочка: «все товары для кухни дороже 100 сомони — в виде строк для витрины», и сортировка по цене. Сортируем копию (slice()), чтобы не испортить исходный массив.

Можно редактировать · Ctrl + Enter
const products = [
  { id: 1, title: "Ковёр", price: 900, category: "дом" },
  { id: 2, title: "Казан", price: 320, category: "кухня" },
  { id: 3, title: "Пиала", price: 15, category: "кухня" },
  { id: 4, title: "Чайник", price: 120, category: "кухня" }
];

const showcase = products
  .filter(p => p.category === "кухня" && p.price > 100)
  .map(p => `${p.title} — ${p.price} сомони`);
console.log(showcase); // → ["Казан — 320 сомони", "Чайник — 120 сомони"]

const byPrice = products.slice().sort((a, b) => a.price - b.price);
console.log(byPrice.map(p => p.title)); // → ["Пиала", "Чайник", "Казан", "Ковёр"]
console.log(products[0].title);         // → Ковёр (исходный порядок цел)
Ловушка: find возвращает тот же объект, а не копию

Если изменить то, что вернул find, изменится и объект внутри массива. Это не копия, а ещё одна «ссылка» на тот же объект.

Можно редактировать · Ctrl + Enter
const products = [{ title: "Казан", price: 320 }, { title: "Пиала", price: 15 }];

const kazan = products.find(p => p.title === "Казан");
kazan.price = 280;

console.log(products[0].price); // → 280 (изменился и в массиве!)

Безопасный доступ: ?.

Данные от сервера не всегда полные. У одного пользователя есть адрес, у другого — нет. Если взять свойство у 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, количество услуг и последнюю услугу берите из массива (не пишите числа и слова вручную).

Можно редактировать · Ctrl + Enter
const booking = {
  id: "HRG-2041",
  guest: { name: "Рустам", phone: "+992 93 777 00 11" },
  hotel: { name: "Памир Лодж", city: "Хорог", stars: 3 },
  nights: 3,
  pricePerNight: 450,
  services: ["завтрак", "трансфер", "экскурсия"]
};

// ваш код здесь
Ожидаемый результат
Бронь 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); средний балл группы.

Можно редактировать · Ctrl + Enter
const group = {
  name: "FE-07",
  mentor: { name: "Далер", telegram: "@daler_js" },
  students: [
    { name: "Фарход", city: "Душанбе", score: 87 },
    { name: "Мадина", city: "Худжанд", score: 94 },
    { name: "Шахром", city: "Душанбе", score: 72 },
    { name: "Нилуфар", city: "Бохтар", score: 90 },
    { name: "Абдулло", city: "Душанбе", score: 65 }
  ]
};

// 1. Группа FE-07, ментор Далер (@daler_js)
// 2. Второй в списке: ...
// 3. Массив имён из Душанбе
// 4. Нилуфар: ...
// 5. Средний балл группы: ...
Ожидаемый результат
Группа 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: "Шохмансур" }
];

function describeOrder(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 — объект с математическими методами. Строки и массивы тоже имеют методы.

Можно редактировать · Ctrl + Enter
console.log(typeof console);     // → object
console.log(typeof console.log); // → function

console.log(Math.round(4.6));    // → 5
console.log(Math.max(3, 9, 4));  // → 9

const city = "хорог";
console.log(city.toUpperCase()); // → ХОРОГ

const cart = ["нон"];
cart.push("чой");                // push — метод массива
console.log(cart);               // → ["нон", "чой"]

Так что 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));
Ожидаемый результат
Ассалому алейкум, Нилуфар!
Ассалому алейкум, Далер!
Хайр, Нилуфар! Рахмат, что пришли.
true
false
Подсказка 1

Короткая запись: 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(" ") — получится массив.

Подсказка 2

Инициалы: fullName.split(" ").map(part => ___ + ".").join(" ").

ЗАДАЧА 3.3

Библиотека: Айни, Рудаки, Фирдавси

сложная

Допишите методы объекта library. byAuthor(author) — массив названий книг автора; availableTitles() — названия книг, которые есть в наличии; oldest() — объект самой старой книги; addBook(book) — добавляет книгу и возвращает новое количество книг. Внутри методов обращайтесь к книгам через library.books (в следующей теме узнаем способ лучше).

Можно редактировать · Ctrl + Enter
const library = {
  name: "Библиотека MakeFirstStep",
  books: [
    { title: "Шахнаме", author: "Фирдавси", year: 1010, available: true },
    { title: "Бухара", author: "Айни", year: 1949, available: false },
    { title: "Мать вина", author: "Рудаки", year: 933, available: true },
    { title: "Рабы", author: "Айни", year: 1935, available: true },
    { title: "Одина", author: "Айни", year: 1924, available: false }
  ],
  // ваши методы здесь
};

console.log(library.byAuthor("Айни"));
console.log(library.availableTitles());
const old = library.oldest();
console.log(`Самая старая: ${old.title}, ${old.year}`);
console.log(`Книг в библиотеке: ${library.addBook({ title: "Смерть ростовщика", author: "Айни", year: 1939, available: true })}`);
console.log(library.byAuthor("Айни"));
Ожидаемый результат
["Бухара", "Рабы", "Одина"]
["Шахнаме", "Мать вина", "Рабы"]
Самая старая: Мать вина, 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(); // → Салом! Я Манижа из города Душанбе
  1. Вызов student.introduce(). JavaScript смотрит, что стоит слева от точки, — это student.
  2. this = student. На время этого вызова this указывает на объект student.
  3. this.name — это то же самое, что student.name, то есть "Манижа".
  4. Метод всегда берёт свежие данные. Поменяли city — следующий вызов уже покажет новый город.

Методы меняют объект через this

Можно редактировать · Ctrl + Enter
const counter = {
  count: 0,
  increment() {
    this.count++;
  },
  reset() {
    this.count = 0;
  },
  show() {
    console.log(`Счётчик: ${this.count}`);
  }
};

counter.increment();
counter.increment();
counter.increment();
counter.show();  // → Счётчик: 3
counter.reset();
counter.show();  // → Счётчик: 0

Одна функция — разные объекты

Вот где this по-настоящему полезен. Одну и ту же функцию можно положить в несколько объектов — и каждый раз this будет тем объектом, у которого её вызвали.

Можно редактировать · Ctrl + Enter
function sayHi() {
  console.log(`Ассалому алейкум, я ${this.name}`);
}

const farhod = { name: "Фарход", sayHi };
const zarina = { name: "Зарина", sayHi };

farhod.sayHi(); // → Ассалому алейкум, я Фарход
zarina.sayHi(); // → Ассалому алейкум, я Зарина

Ловушка 1: стрелочная функция как метод

У стрелочной функции нет своего 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, то есть shop
    return this.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.

Можно редактировать · Ctrl + Enter
const card = {
  owner: "Манижа",
  balance: 500,
  history: [],
  // ваши методы здесь
};

card.pay("Корвон", 320);
card.deposit(200);
card.pay("Чайхана Рохат", 85);
card.pay("Такси", 500);
card.report();
console.log(card.history);
Ожидаемый результат
Оплата в Корвон: 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().

Можно редактировать · Ctrl + Enter
const taxi = {
  driver: "Абдулло",
  trips: 0,
  earned: 0,
  addTrip: (price) => {
    this.trips++;
    this.earned += price;
  },
  info() {
    return `${this.driver}: поездок ${this.trips}, заработано ${this.earned} сомони`;
  }
};

taxi.addTrip(25);
taxi.addTrip(40);
taxi.addTrip(18);

const showInfo = taxi.info;
console.log(showInfo());

// создайте taxi2 здесь
Ожидаемый результат
Абдулло: поездок 3, заработано 83 сомони
Шахром: поездок 1, заработано 30 сомони
Подсказка 1

Первая ошибка — в том, как объявлен 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
function showProduct({ 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 возвращает массив из двух элементов, который раскладывают по позициям. Пока просто узнайте знакомую форму:

Так это выглядит в React (запускать не нужно)
function ProductCard({ title, price }) {          // деструктуризация объекта
  const [count, setCount] = useState(0);          // деструктуризация массива
  // ...
}
Частые ошибки

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;

Подсказка 2

Вторая: const { wind: ___, humidity = ___ } = weather;

ЗАДАЧА 5.2

Витрина магазина сувениров

средняя

Напишите функцию finalPrice({ price, discount = 0 }), которая возвращает цену со скидкой в процентах, и функцию formatProduct({ title, price, discount = 0 }), которая возвращает строку для витрины. Если скидки нет, в скобках ничего не пишем. Выведите все товары и итоговую сумму всех цен со скидкой.

Можно редактировать · Ctrl + Enter
const products = [
  { title: "Ковёр 2×3", price: 900, discount: 15 },
  { title: "Чайный сервиз", price: 240 },
  { title: "Сюзане", price: 600, discount: 10 },
  { title: "Пиалы, 6 шт.", price: 60 }
];

// ваш код здесь
Ожидаемый результат
Ковёр 2×3: 765 сомони (скидка 15%)
Чайный сервиз: 240 сомони
Сюзане: 540 сомони (скидка 10%)
Пиалы, 6 шт.: 60 сомони
Итого: 1605 сомони
Подсказка 1

Цена со скидкой: 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. Затем выведите четыре строки.

Можно редактировать · Ctrl + Enter
const response = {
  status: "ok",
  data: {
    booking: {
      id: 7781,
      guest: { firstName: "Сабрина", lastName: "Назарова" },
      dates: ["2026-10-12", "2026-10-15"],
      hotel: { name: "Памир Лодж", city: "Хорог" },
      rooms: [
        { type: "двухместный", price: 520 },
        { type: "одноместный", price: 380 }
      ]
    }
  }
};

// const { data: { booking: { ... } } } = response;
Ожидаемый результат
Бронь №7781 · Сабрина
Хорог: с 2026-10-12 по 2026-10-15
Первая комната: двухместный
Завтрак включён: false
Подсказка 1

Идите по пути шаг за шагом: data → booking → внутри перечисляйте нужное через запятую. Для гостя: guest: { firstName: guestName }.

Подсказка 2

Даты — это массив: dates: [checkIn, checkOut]. Первая комната — массив объектов: rooms: [{ type: ___ }]. Город: hotel: { city: ___ }.

06 Spread и rest: три точки ...

Три точки ... в 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). Зачем? Программа сравнивает старые данные с новыми: если объект тот же самый (вы его просто поправили), она решает, что ничего не изменилось, и не обновляет экран. Новая копия — чёткий сигнал: «данные изменились, перерисуй».

Можно редактировать · Ctrl + Enter
const addToCart = (cart, item) => [...cart, item];
const updateQty = (item, qty) => ({ ...item, qty });

const cart = [{ title: "Нон", qty: 2 }];
const newCart = addToCart(cart, { title: "Чой", qty: 1 });

console.log(cart.length, newCart.length); // → 1 2
console.log(updateQty(cart[0], 5));       // → { title: "Нон", qty: 5 }
console.log(cart[0]);                     // → { title: "Нон", qty: 2 }
Ловушка: копия поверхностная

Spread копирует только первый уровень. Вложенные объекты и массивы не копируются — копия и оригинал продолжают делить их.

Можно редактировать · Ctrl + Enter
const student = { name: "Абдулло", address: { city: "Турсунзаде" } };

const copy = { ...student };
copy.name = "Абдулло К.";          // первый уровень — копия, оригинал цел
copy.address.city = "Душанбе";     // вложенный объект — общий!

console.log(student.name);         // → Абдулло
console.log(student.address.city); // → Душанбе (баг!)

// правильно: копируем и вложенный уровень
const student2 = { name: "Мадина", address: { city: "Худжанд" } };
const moved = { ...student2, address: { ...student2.address, city: "Душанбе" } };
console.log(student2.address.city, moved.address.city); // → Худжанд Душанбе

Полную (глубокую) копию можно сделать через structuredClone — разберём в теме 07.

Rest: собрать «всё остальное»

В параметрах функции ... собирает все переданные аргументы в массив. Так можно написать функцию, которая принимает сколько угодно чисел.

Можно редактировать · Ctrl + Enter
function sum(...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 после обычных параметров: первый — отдельно, остальные — в массив
function order(customer, ...dishes) {
  console.log(`${customer} заказал(а): ${dishes.length} блюда`);
}
order("Зарина", "курутоб", "самбуса", "чой"); // → Зарина заказал(а): 3 блюда

В деструктуризации rest собирает «остаток» объекта или массива. Очень удобно, чтобы убрать лишние поля — например, пароль перед отправкой данных на экран.

Можно редактировать · Ctrl + Enter
const account = { login: "madina", password: "12345", email: "m@mail.tj", city: "Худжанд" };

const { password, ...safeUser } = account;
console.log(safeUser); // → { login: "madina", email: "m@mail.tj", city: "Худжанд" }

const [winner, ...others] = ["Фарход", "Зарина", "Рустам"];
console.log(winner); // → Фарход
console.log(others); // → ["Зарина", "Рустам"]
Spread — распаковываетRest — собирает
Где стоитСправа от =, внутри [ ] или { }, в аргументах при вызовеВ параметрах функции, слева от = в деструктуризации
Что делаетМассив/объект → отдельные элементыОтдельные элементы → массив/объект
Пример[...a, ...b], { ...user, age: 21 }, Math.max(...nums)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 = ["оши палав", "самбуса"];

// ваш код здесь
Ожидаемый результат
["чой", "нон", "каймак", "оши палав", "самбуса", "шакароб"]
["курутоб", "оши палав", "самбуса"]
["оши палав", "самбуса"]
Всего блюд: 6
Подсказка 1

Внутри квадратных скобок можно смешивать spread и обычные элементы: [...a, "x"].

Подсказка 2

const specials = ["курутоб", ___];

ЗАДАЧА 6.2

Профиль без мутаций

средняя

Напишите две функции, которые не меняют исходный объект, а возвращают новый: moveTo(profile, city) — профиль с другим городом; addSkill(profile, skill) — профиль, где skills — новый массив с добавленным навыком. Последняя строка проверяет, что массивы навыков действительно разные.

Можно редактировать · Ctrl + Enter
const profile = { username: "@madina_fe", city: "Худжанд", skills: ["HTML", "CSS"] };

// ваши функции здесь

const p2 = moveTo(profile, "Душанбе");
const p3 = addSkill(p2, "JavaScript");
console.log(profile);
console.log(p3);
console.log(profile.skills === p3.skills);
Ожидаемый результат
{ username: "@madina_fe", city: "Худжанд", skills: ["HTML", "CSS"] }
{
  username: "@madina_fe",
  city: "Душанбе",
  skills: ["HTML", "CSS", "JavaScript"]
}
false
Подсказка 1

moveTo — одна строка: копия объекта и новое значение city после неё.

Подсказка 2

({ ...profile, skills: [...profile.___, skill] }). Если сделать profile.skills.push(skill), вы измените исходный массив — последняя строка покажет true, а первая — лишний навык.

ЗАДАЧА 6.3

Доставка, секреты и очередь

сложная

Напишите три функции. total(deliveryFee, ...prices) — сумма доставки и всех цен (цен может быть сколько угодно, даже ноль). hideSecrets(account) — новый объект без password и pin (через rest в деструктуризации). nextInQueue(queue) — строка о первом в очереди и количестве ожидающих; разложите массив прямо в параметрах функции.

Можно редактировать · Ctrl + Enter
const account = { login: "rustam92", password: "qwerty", pin: "4321", name: "Рустам", city: "Бохтар" };
const queue = ["Зарина", "Далер", "Манижа", "Фарход"];

// ваши функции здесь

console.log(total(10, 30, 28, 7));
console.log(total(15));
console.log(hideSecrets(account));
console.log(account.password);
console.log(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.
  • Файлы настроек проектов, например package.json.
Так выглядит JSON
{
  "name": "Мадина",
  "age": 19,
  "isStudent": true,
  "skills": ["HTML", "CSS"],
  "address": { "city": "Худжанд" },
  "phone": null
}

Правила JSON строже, чем у объекта JS

ЧтоОбъект JSJSON
Ключиможно без кавычек: nameтолько в двойных кавычках: "name"
Строки"...", '...', `...`только "..."
Значениячто угодно, включая функции и undefinedстрока, число, true/false, null, массив, объект
Запятая после последнегоможнонельзя
Комментарииможнонельзя

JSON.stringify: объект → строка

Можно редактировать · Ctrl + Enter
const order = { id: 501, dish: "Курутоб", price: 28, paid: true, extras: ["нон", "чой"] };

const json = JSON.stringify(order);
console.log(json); // → {"id":501,"dish":"Курутоб","price":28,"paid":true,"extras":["нон","чой"]}
console.log(typeof order, typeof json); // → object string
console.log(json.length); // → 73 (это просто строка из 73 символов)

Чтобы JSON было удобно читать человеку, передайте ещё два аргумента: JSON.stringify(obj, null, 2). Третий аргумент — сколько пробелов в отступе, второй пока всегда null.

Можно редактировать · Ctrl + Enter
const weather = { city: "Хорог", temp: 18, wind: 6 };
console.log(JSON.stringify(weather, null, 2));
// {
//   "city": "Хорог",
//   "temp": 18,
//   "wind": 6
// }

Функции и undefined в JSON не попадают — они просто пропадают:

Можно редактировать · Ctrl + Enter
const user = { name: "Нилуфар", age: undefined, greet() { return "Салом"; } };
console.log(JSON.stringify(user)); // → {"name":"Нилуфар"}

JSON.parse: строка → объект

Можно редактировать · Ctrl + Enter
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:

  1. try { ... } — «попробуй выполнить». Если всё хорошо, блок catch пропускается.
  2. catch (error) { ... } — «если внутри try что-то упало, приходи сюда». В error лежит объект ошибки, у него есть name и message.
  3. Программа продолжает работать со следующей строки после catch.
Можно редактировать · Ctrl + Enter
const bad = "{'city': 'Хорог'}"; // одинарные кавычки — это не JSON

try {
  const data = JSON.parse(bad);
  console.log(data.city);
} catch (error) {
  console.log("Не удалось прочитать данные");  // → Не удалось прочитать данные
  console.log(error.name);                      // → SyntaxError
}

console.log("Программа работает дальше");       // → Программа работает дальше

Сохранение в localStorage

localStorage хранит данные в браузере даже после перезагрузки страницы — например, корзину или тему оформления. Он принимает только строки, поэтому без JSON никак. Этот код запускайте у себя в проекте: во встроенном окошке запуска localStorage может быть недоступен.

Код для вашего проекта
const cart = [{ id: 1, title: "Нон", qty: 3 }, { id: 4, title: "Мёд", qty: 1 }];

// сохранить: объект → строка
localStorage.setItem("cart", JSON.stringify(cart));

// прочитать: строка → объект. Если ключа нет, getItem вернёт null
const saved = JSON.parse(localStorage.getItem("cart")) ?? [];
console.log(saved.length); // 2
Частая ошибка: забыли stringify

Если положить объект в localStorage без JSON.stringify, он превратится в бесполезную строку "[object Object]", и данные пропадут.

Можно редактировать · Ctrl + Enter
const cart = { id: 1, qty: 3 };
console.log(String(cart));         // → [object Object]  (вот что сохранится без JSON)
console.log(JSON.stringify(cart)); // → {"id":1,"qty":3}

Глубокая копия

Помните ловушку поверхностной копии из темы 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 аккуратнее, но функции тоже не копирует (выдаст ошибку). Для обычных данных — объектов, массивов, строк и чисел — подходят оба.

Реальный пример: погода в Душанбе

Так примерно выглядит ответ сервиса погоды. Мы превращаем строку в объект, достаём нужное деструктуризацией и ищем самый жаркий день.

Можно редактировать · Ctrl + Enter
const response = `{
  "city": "Душанбе",
  "current": { "temp": 33, "wind": 3, "description": "солнечно" },
  "forecast": [
    { "day": "Пн", "max": 34, "min": 19 },
    { "day": "Вт", "max": 36, "min": 21 },
    { "day": "Ср", "max": 31, "min": 18 }
  ]
}`;

const data = JSON.parse(response);
const { city, current: { temp, description }, forecast } = data;

console.log(`${city}: ${temp}°C, ${description}`); // → Душанбе: 33°C, солнечно

const hottest = forecast.reduce((best, d) => (d.max > best.max ? d : best), forecast[0]);
console.log(`Самый жаркий день: ${hottest.day} (${hottest.max}°C)`); // → Самый жаркий день: Вт (36°C)

const nights = forecast.map(({ day, min }) => `${day}: ${min}°`);
console.log(nights); // → ["Пн: 19°", "Вт: 21°", "Ср: 18°"]
ИИ-подсказка

Если JSON.parse ругается, а строка длинная — вставьте её в чат с ИИ и попросите: «найди, где здесь нарушены правила JSON, и покажи исправленную строку». Но потом обязательно проверьте исправленную строку сами через JSON.parse.

Практика: закрепляем

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

ЗАДАЧА 7.1

Упаковать дыню

лёгкая

Превратите объект fruit в JSON-строку json. Выведите саму строку, её тип, а затем красивую версию с отступом в 2 пробела.

Можно редактировать · Ctrl + Enter
const fruit = { name: "Дыня", from: "Бохтар", price: 12, weightKg: 3, fresh: true };

// ваш код здесь
Ожидаемый результат
{"name":"Дыня","from":"Бохтар","price":12,"weightKg":3,"fresh":true}
string
{
  "name": "Дыня",
  "from": "Бохтар",
  "price": 12,
  "weightKg": 3,
  "fresh": true
}
Подсказка 1

Тип узнаём оператором typeof.

Подсказка 2

Красивая версия: JSON.stringify(fruit, ___, 2).

ЗАДАЧА 7.2

Прогноз для Худжанда

средняя

Сервер прислал строку с прогнозом. Превратите её в объект и выведите: город; массив дней, когда максимум выше 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"}'
];

function safeParse(str) {
  // ваш код здесь
}

// часть 1: выведите сообщения и "Прочитано: ... из ..."


// часть 2
const 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. Такие задачи — это уже маленькие кусочки настоящих приложений: магазина, журнала оценок, сервиса такси.

  1. Посмотрите на форму данных. Что это: массив объектов? Объект с массивом внутри? Выведите один элемент через console.log.
  2. Разбейте задачу на шаги. «Отобрать → посчитать → отсортировать → вывести». Каждый шаг — один метод или маленькая функция.
  3. Проверяйте после каждого шага. Написали filter — выведите результат, и только потом пишите map.
  4. Не портите исходные данные. Перед sort делайте копию [...arr], вместо изменения объекта — { ...obj, поле: значение }.

Практика: большие задачи

Не торопитесь. Решайте по шагам и проверяйте промежуточные результаты.

ЗАДАЧА 8.1

Корзина интернет-магазина

лёгкая

Есть каталог товаров и корзина, где для каждого товара указан только id и количество. Для каждой позиции корзины найдите товар в каталоге и выведите строку чека. Затем выведите сумму. Доставка 15 сомони, но бесплатно, если сумма от 150 сомони. В конце — сумма к оплате.

Можно редактировать · Ctrl + Enter
const products = [
  { id: 1, title: "Нон", price: 4 },
  { id: 2, title: "Чой кабуд, 100 г", price: 18 },
  { id: 3, title: "Зардолу сушёный, 1 кг", price: 45 },
  { id: 4, title: "Мёд горный, 0.5 кг", price: 70 }
];
const cart = [
  { id: 1, qty: 3 },
  { id: 3, qty: 2 },
  { id: 4, qty: 1 }
];

// ваш код здесь
Ожидаемый результат
Нон × 3 = 12 сомони
Зардолу сушёный, 1 кг × 2 = 90 сомони
Мёд горный, 0.5 кг × 1 = 70 сомони
Сумма: 172 сомони
Доставка: бесплатно
К оплате: 172 сомони
Подсказка 1

Для каждой позиции: 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 — средний балл, округлённый до одного знака после точки. Выведите рейтинг по убыванию среднего, лучшего студента и средний балл группы по всем оценкам (тоже с одним знаком). Последняя строка проверяет, что исходный массив не изменился.

Можно редактировать · Ctrl + Enter
const students = [
  { name: "Фарход", city: "Душанбе", scores: [85, 90, 78] },
  { name: "Мадина", city: "Худжанд", scores: [95, 88, 92] },
  { name: "Рустам", city: "Куляб", scores: [70, 65, 80] },
  { name: "Зарина", city: "Душанбе", scores: [88, 94, 91] },
  { name: "Шахром", city: "Истаравшан", scores: [60, 75, 72] }
];

// ваш код здесь

console.log("average" in students[0]);
Ожидаемый результат
1. Мадина — 91.7
2. Зарина — 91
3. Фарход — 84.3
4. Рустам — 71.7
5. Шахром — 69
Лучший студент: Мадина (Худжанд)
Средний балл группы: 81.5
false
Подсказка 1

Округление до одного знака: 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"), посчитайте выручку. Соберите объект-сводку по водителям: для каждого — сумма и количество поездок. Выведите водителей по убыванию суммы. В конце найдите самую дорогую выполненную поездку и выведите её, разложив объект деструктуризацией.

Можно редактировать · Ctrl + Enter
const serverResponse = `[
  {"id": 1, "driver": "Шахром", "from": "Шохмансур", "to": "Ирам", "price": 22, "status": "done"},
  {"id": 2, "driver": "Абдулло", "from": "Корвон", "to": "Аэропорт", "price": 35, "status": "done"},
  {"id": 3, "driver": "Шахром", "from": "Варзоб", "to": "Сино", "price": 60, "status": "cancelled"},
  {"id": 4, "driver": "Далер", "from": "проспект Рудаки", "to": "Шохмансур", "price": 18, "status": "done"},
  {"id": 5, "driver": "Абдулло", "from": "Ирам", "to": "Корвон", "price": 25, "status": "done"},
  {"id": 6, "driver": "Шахром", "from": "Аэропорт", "to": "Сино", "price": 40, "status": "done"}
]`;

// ваш код здесь
Ожидаемый результат
Выполнено заказов: 5 из 6
Выручка: 140 сомони
Шахром: 62 сомони, поездок: 2
Абдулло: 60 сомони, поездок: 2
Далер: 18 сомони, поездок: 1
Самая дорогая: Аэропорт → Сино, 40 сомони (Шахром)
Подсказка 1

Сводка по водителям — объект, где ключ — имя: { Шахром: { 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 }У стрелочной функции нет своего thismethod() { 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, не менять исходный массив, для пустого массива вернуть {}. Вставьте ответ ИИ в код ниже и запустите. Если вывод не совпал — не исправляйте руками, а уточните промпт.

Можно редактировать · Ctrl + Enter
const students = [
  { name: "Фарход", city: "Душанбе" },
  { name: "Мадина", city: "Худжанд" },
  { name: "Шахром", city: "Душанбе" },
  { name: "Нилуфар", city: "Бохтар" },
  { name: "Далер", city: "Худжанд" }
];

// вставьте сюда функцию groupByCity от ИИ

console.log(groupByCity(students));
console.log(groupByCity([]));
console.log(students.length);
Ожидаемый результат
{
  Душанбе: ["Фарход", "Шахром"],
  Худжанд: ["Мадина", "Далер"],
  Бохтар: ["Нилуфар"]
}
{}
5
Подсказка 1

Вставьте в промпт массив students целиком и ожидаемый результат — так ИИ увидит точную форму входа и выхода.

Подсказка 2

Проверьте ответ ИИ глазами: есть ли у reduce начальное значение {}? Нет ли var? Сможете ли вы объяснить каждую строку? Если нет — попросите ИИ объяснить построчно.

ЗАДАЧА 9.2

ИИ ищет баги, вы — проверяете

средняя

В этом коде три ошибки — все из таблицы «Типичные ошибки ИИ». Сначала попробуйте найти их сами. Потом отправьте код ИИ с промптом в стиле пункта 6 (код + что выводится + что ожидали) и сравните: нашёл ли он все три? Объяснил ли причину каждой? Исправьте код и добейтесь ожидаемого вывода. Порядок товаров внутри shop.products должен остаться исходным.

Можно редактировать · Ctrl + Enter
const shop = {
  name: "Корвон-онлайн",
  products: [
    { title: "Дыня", price: 12 },
    { title: "Гранат", price: 9 },
    { title: "Виноград", price: 15 }
  ],
  cheapestFirst: () => {
    return this.products.sort((a, b) => a.price - b.price);
  },
  total() {
    return this.products.reduce((sum, p) => sum + p.price);
  }
};

console.log(shop.cheapestFirst().map(p => p.title));
console.log(shop.products[0].title);
console.log(shop.total());
Ожидаемый результат
["Гранат", "Дыня", "Виноград"]
Дыня
36
Подсказка 1

Ошибки проявляются по очереди: исправите первую (код перестанет падать) — увидите вторую и третью. Смотрите на: как объявлен метод, что делает sort с исходным массивом, с чего начинает reduce.

Подсказка 2

Если ИИ нашёл только одну-две ошибки, напишите ему: «после твоего исправления shop.products[0].title выводит "Гранат", а должен "Дыня". Почему?» — и проверьте, понял ли он причину.

ЗАДАЧА 9.3

Сгенерируйте данные и проверьте функцию

сложная

Напишите функцию libraryReport(books) (сами или с ИИ — но объяснить её должны вы), которая выводит: всего книг, сколько в наличии, объект «автор → количество книг» и самую толстую книгу. Сначала добейтесь точного вывода на маленькой контрольной выборке sample. Затем попросите ИИ сгенерировать JSON-массив из 15 книг таджикских и персидских классиков (Айни, Рудаки, Фирдавси, Хайям, Саади, Джами) с полями title, author, year, pages, available — причём попросите сделать одну книгу без поля pages. Вставьте строку в generated и убедитесь, что функция не падает и считает правильно.

Можно редактировать · Ctrl + Enter
const sample = [
  { title: "Шахнаме", author: "Фирдавси", year: 1010, pages: 980, available: true },
  { title: "Одина", author: "Айни", year: 1924, pages: 120, available: false },
  { title: "Рабы", author: "Айни", year: 1935, pages: 450, available: true }
];

function libraryReport(books) {
  // ваш код здесь
}

libraryReport(sample);

// const generated = `вставьте сюда JSON от ИИ`;
// libraryReport(JSON.parse(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.
  • Вложенность: group.students[1].name. Необязательные данные: user.address?.city ?? "—".
  • Метод — функция в объекте: greet() { }, вызов со скобками obj.greet().
  • this — объект перед точкой при вызове. Методы с this — не стрелочные; колбэки внутри метода — стрелочные. Оторванный метод теряет this.
  • Деструктуризация: const { name: userName, city = "Душанбе" } = user, const [first, , third] = arr, function f({ title, price }).
  • Spread распаковывает: [...a, ...b], { ...user, age: 21 } — новая копия (поверхностная!). Rest собирает: function sum(...nums), const { password, ...safe } = user.
  • JSON — текст для обмена данными: JSON.stringify(obj, null, 2) и JSON.parse(str) внутри try...catch. localStorage хранит только строки. Глубокая копия — structuredClone(obj).
  • С ИИ: называйте методы и структуры, давайте пример данных и точный вывод, просите крайние случаи, проверяйте запуском. Не вставляйте код, который не можете объяснить.