MakeFirstStep· Frontend + зеҳни сунъӣ

Дарси 19 · Абзорҳо ва марҳалаи ниҳоӣ

Git ва GitHub + ислоҳи хатоҳои код

Ҳар лоиҳаи гурӯҳӣ дар Git зиндагӣ мекунад, ва ҳар код як рӯз вайрон мешавад. Имрӯз шумо ёд мегиред, ки таърихи корро нигоҳ доред, тағйиротро тавассути pull request пешниҳод кунед, низоъҳоро ҳал намоед ва хатоҳоро оромона ҷустуҷӯ кунед — ба ҷои суханони «дар назди ман кор намекунад».

  • Дар таърихи фаҳмо commit кардан ва паёмҳоро аз рӯи Conventional Commits навиштан
  • Бо шохаҳо кор кардан ва давраи «шоха → push → pull request → ревью → merge»-ро гузаштан
  • Низои ҷамъкуниро (merge conflict) ҳал кардан ва .gitignore-ро танзим намудан
  • Хатоҳоро гирифтан ва партофтан: try / catch / finally, throw, навъҳои хатоҳо
  • Стеки даъватҳоро хондан, breakpoint-ро дар DevTools гузоштан ва ҷадвали Network-ро дидан
  • Хаторо тавре тасвир кардан, ки ЗС (ва одам) кумак карда тавонад: мисоли ҳадди ақали такроршаванда
Бо ин саҳифа чӣ тавр кор кардан лозим

Блокҳои «Терминал» — фармонҳо барои компютери шумо ҳастанд, дар браузер онҳо иҷро намешаванд: онҳоро дар терминали VS Code ворид кунед. Коди JavaScript бо тугмаи ▶ Иҷро кардан — зинда аст: онро тағйир додан ва худи ҳамин ҷо санҷидан мумкин аст. Git-ро мо бо массивҳо ва объектҳои оддӣ моделсозӣ мекунем, барои ҳамин мантиқи add, commit, шохаҳо ва низоъҳоро бе насб ламс кардан мумкин аст. Ишораҳо ба вазифаҳо пинҳонанд: аввал худатон кӯшиш кунед.

01 Git: таърихи лоиҳа

Тасаввур кунед, ки шумо барои чойхона сайт месозед. Имрӯз ҳама чиз кор мекунад, пагоҳ шумо менюро «каме ислоҳ кардед», ва сайт вайрон шуд. Чӣ тавр бозгашт кард? Папкаро бо номҳои site-final, site-final-2, site-final-REAL нусха кардан? Навомӯзон ҳамин тавр мекунанд. Касбиҳо Git-ро истифода мебаранд — низоми назорати версияҳо. Он ҳар ҳолати нигоҳдошташудаи лоиҳаро сабт мекунад, нишон медиҳад, ки чӣ ва кӣ тағйир дод, ва имкон медиҳад ба ҳар лаҳза бозгашт кард.

Монандӣ: албоми оилавӣ

Шумо аз телефон аксҳои тӯйро интихоб карда, ба лифофа мемонед — ин git add аст. Баъд лифофаро ба албом мечаспонед ва менависед «Никоҳ, 12 июн» — ин git commit аст. Албом ҳамаи саҳифаҳоро бо тартиб нигоҳ медорад. Хоҳед — ҳар саҳифаро мекушоед ва мебинед, ки он вақт чӣ гуна буд.

Се минтақа ва чор ҷой

Файл дар Git дар компютери шумо аз се минтақа мегузарад. Ҷойи чорум — сервери GitHub аст, ки шумо нусхаро ба он мефиристед.

Папкаи корӣфайлҳое, ки шумо дар муҳаррир таҳрир мекунед
→
Индекс (staging)«лифофа»: чӣ ба commit-и навбатӣ дохил мешавадgit add
→
Репозиторий«албом»: таърихи нигоҳдошташуда дар компютери шумоgit commit
→
GitHub (remote)нусха дар интернет, барои гурӯҳ умумӣgit push
Роҳи тағйирот: таҳрир → git add → git commit → git push

Репозиторийи аввал

Як бор дар компютер бояд худро муаррифӣ кунед — ин ном ба ҳар commit меафтад:

Терминал
git config --global user.name "Ваше Имя"
git config --global user.email "you@example.com"

Акнун лоиҳа эҷод мекунем ва commit-и аввалро мегузаронем:

Терминал
mkdir chaykhona-site
cd chaykhona-site
git init -b main
echo "# Чойхона" > README.md
git status
git add README.md
git commit -m "docs: add readme"
git log --oneline

Қадам ба қадам чӣ рух дод:

  1. git init папкаи пинҳонии .git-ро эҷод кард — тамоми таърих дар он зиндагӣ мекунад. Онро хориҷ кунед, ва лоиҳа дигар репозиторий нахоҳад буд.
  2. git status гуфт: файли README.md «untracked» аст — Git онро ҳанӯз пайгирӣ намекунад.
  3. git add файлро ба индекс («лифофа») гузошт.
  4. git commit -m "…" акси фаврӣ (snapshot)-ро бо имзо сабт кард.
  5. git log --oneline таърихро нишон дод: рамзи кӯтоҳи commit ва имзо.

Фармонҳое, ки шумо ҳар рӯз истифода мебаред

ФармонЧӣ кор мекунад
git statusНишон медиҳад, ки кадом файлҳо тағйир ёфтаанд, кадомҳо дар индекс ҳастанд, кадомҳо пайгирӣ намешаванд
git add файл / git add .Файлро (ё ҳамаи тағйиротро) ба индекс мегузорад
git commit -m "матн"Индексро ҳамчун commit бо имзо нигоҳ медорад
git log --onelineТаърих мухтасар, як сатр барои ҳар commit
git diffЧӣ тағйир ёфтааст, вале ҳанӯз дар индекс нест
git diff --stagedЧӣ дар индекс аст ва ба commit дохил мешавад
git restore файлТаҳрирҳои нигоҳнадошташудаи файлро бекор мекунад (ба commit-и охирин бармегардонад)
git restore --staged файлФайлро аз индекс мебарорад, таҳрирҳо дар папка мемонанд
Хатогиҳои маъмул

1. git add . бе нигоҳ кардан: ба commit файлҳои зиёдатӣ (паролҳо, node_modules) меафтанд. Аввал git status, баъд git add.

2. git commit бе -m муҳаррири матнии Vim-ро мекушояд. Агар часпидед: Esc-ро пахш кунед, :q! ворид кунед ва Enter. Ё ҳамеша -m-ро нависед.

3. Як commit-и азим «ҳама чиз барои як ҳафта». Commit — ин як андешаи ба итмом расида: «филтр илова кардам», «маҷмӯи ниҳоиро ислоҳ кардам».

Барои ҳис кардани сохти минтақаҳо, онҳоро бо код моделсозӣ мекунем. Ин вазифаи аввал дар саҳифа аст.

Амал: мустаҳкам мекунем

Мо Git-ро бо JavaScript-и оддӣ моделсозӣ мекунем — ҳамин тавр мебинед, ки дар дохил ҷодугарӣ нест.

ВАЗИФАИ 1.1

Се минтақаи Git дар код

осон

Мо се минтақаи Git-ро бо массивҳои оддӣ моделсозӣ мекунем. add (git add: файл аз work ба staged мегузарад) ва commit (git commit: ҳар чизи аз staged ба commit-и нав меравад, ва staged тоза мешавад)-ро анҷом диҳед. Таърих мисли git log чоп мешавад — commit-ҳои нав дар боло.

Таҳрир кардан мумкин аст · Ctrl + Enter
const repo = {
  work: ["index.html", "style.css", "app.js"], // файлҳои тағйирёфта
  staged: [],                                  // омодашуда: git add
  commits: [],                                 // таърих: git commit
};

function add(file) {
  // файлро аз work хориҷ кунед ва ба staged гузоред
}

function commit(message) {
  // файлҳоро аз staged ба commit-и нав { message, files } нигоҳ доред
  // ва staged-ро тоза кунед
}

add("index.html");
add("style.css");
commit("add page skeleton");
add("app.js");
commit("add order counter");

for (let i = repo.commits.length - 1; i >= 0; i--) {
  const c = repo.commits[i];
  console.log(`${i + 1}. ${c.message} (${c.files.join(", ")})`);
}
console.log(repo.work.length, repo.staged.length);
Натиҷаи интизоршаванда
2. add order counter (app.js)
1. add page skeleton (index.html, style.css)
0 0
Ишора 1

Дар add ду чиз лозим аст: файлро аз work хориҷ кардан (масалан, тавассути filter) ва push ба staged кардан.

Ишора 2

Дар commit нусхаи рӯйхатро нигоҳ доред: files: [...repo.staged]. Баъд repo.staged = []. Одати нусха бардоштан дар қисми 8 кор меояд — дар он ҷо мутатсия барномаҳоро вайрон мекунад.

ВАЗИФАИ 1.2

git status чӣ нишон медиҳад

осон

Функсияи statusLine файлро ҳамон тавр тасвир мекунад, ки git status. Аммо як ҳолатро намедонад: файл аллакай дар индекс аст (staged), ва пас аз git add он боз як бор тағйир ёфт (changedAfterAdd: true). Барои он сатри staged, but changed again: ном лозим аст. Шартро анҷом диҳед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const files = [
  { name: "index.html", tracked: true, changed: false, staged: false },
  { name: "app.js", tracked: true, changed: true, staged: false },
  { name: "notes.txt", tracked: false, changed: true, staged: false },
  { name: "style.css", tracked: true, changed: true, staged: true, changedAfterAdd: false },
  { name: "README.md", tracked: true, changed: true, staged: true, changedAfterAdd: true },
];

function statusLine(f) {
  if (!f.tracked) return `untracked: ${f.name}`;
  if (f.staged) return `to be committed: ${f.name}`;
  if (f.changed) return `modified: ${f.name}`;
  return null; // файли тоза дар статус нишон дода намешавад
}

files.map(statusLine).filter(Boolean).forEach((line) => console.log(line));
Натиҷаи интизоршаванда
modified: app.js
untracked: notes.txt
to be committed: style.css
staged, but changed again: README.md
Ишора 1

Санҷиши навро бояд пеш аз санҷиши оддии f.staged гузоред: вагарна он кор мекунад, ва то шарти шумо навбат намерасад.

Ишора 2

if (f.staged && f.changedAfterAdd) return `staged, but changed again: ${f.name}`;

Муфассалтар хонед: Pro Git — сабти тағйирот дар репозиторий.

02 Шохаҳо: кор мекунем, асосиро вайрон намекунем

Дар репозиторий шохаи асосӣ ҳаст — одатан main (гоҳо онро master меноманд). Дар он версияи «ҳатман кор мекунанда» ҷойгир аст. Функсияи навро дар шохаи алоҳида месозанд: кӯшиш кардан, хато кардан ва commit кардан ба қадри дилхоҳ мумкин аст — шохаи асосӣ зарар намебинад. Вақте функсия тайёр ва санҷида шуд, шохаро бозпас медароранд.

Монандӣ: палави озмоишӣ

Дар дегҳои калон палав барои меҳмонон мепазад — ин main аст. Шумо мехоҳед дорупази нави бо биҳиро санҷед. Шумо ба дегҳои умумӣ даст намезанед, балки дегчаи хурд мегиред — ин шоха аст. Лазиз баромад — биҳиро ба дегҳои умумӣ илова кардем (merge). Нашуд — дегчаро партофтем, ва меҳмонон чизеро пай набурданд.

init header footer fix typo merge release filters sort tests main feature/filters шоха дар ин ҷо ҷудо шуд
Хатти сабз — main, кабуд — шохаи feature/filters. То шумо дар шоха кор мекунед, main зиндагии худро дорад, баъд шохаҳо бо commit-и merge ҷамъ мешаванд

Шоха нусхаи папка нест. Ин танҳо «хатчӯби» ҳаракаткунанда ба яке аз commit-ҳост. Commit-и нав хатчӯбро пеш мебарад. Ба шарофати ин шохаҳо дар Git фавран эҷод мешаванд ва қариб чизе арзиш надоранд. Хатчӯби махсуси HEAD нишон медиҳад, ки шумо ҳоло дар кадом шоха ҳастед.

Терминал
git branch                         # список веток, звёздочка у текущей
git switch -c feature/filters      # создать ветку и перейти на неё
# ...правим файлы, git add, git commit...
git log --oneline --graph --all    # история в виде дерева
git switch main                    # вернуться на main
git merge feature/filters          # влить ветку в main
git branch -d feature/filters      # удалить ветку, она больше не нужна

Шохаҳоро чӣ тавр номгузорӣ кардан

Дар фармонҳо шохаро аз рӯи қолаби навъ/тавсифи-кӯтоҳ меноманд: feature/order-filters, fix/wrong-total, docs/readme-tajik. Калимаҳои англисӣ, ҳарфҳои хурд, бе фосила, калимаҳо бо дефис. Аз рӯи ном фавран намоён аст, ки дар шоха чӣ ҳаст ва барои чӣ.

Хатогиҳои маъмул

Бевосита дар main кор кардан. Он гоҳ аз чӣ PR сохтан намешавад, ва ҳангоми мушкил бозгашт кардан ҷое нест. Одат: аввал git switch -c, баъд код.

Шохаро бо таҳрирҳои нигоҳнадошта иваз кардан. Git ё онҳоро мебарад, ё рад мекунад. Беҳтараш аввал commit кунед.

Шохаҳо — графи commit-ҳо ҳастанд, ва мо онро бо код гашта метавонем. Аз оддӣ оғоз мекунем: занҷири «падар → падар»-ро мегузарем, чунон ки git log мекунад.

Амал: мустаҳкам мекунем

ВАЗИФАИ 2.1

Таърихи шоха

осон

Ҳар commit «падар»-и худро ба ёд дорад. Шоха — танҳо ишоратгар ба commit-и охирин аст. history(branch)-ро анҷом диҳед: аз охири шоха бо занҷири parent гузаред ва массиви паёмҳоро аз нав ба кӯҳна баргардонед — git log ҳамин тавр кор мекунад.

Таҳрир кардан мумкин аст · Ctrl + Enter
const commits = [
  { id: "c1", parent: null, message: "init project" },
  { id: "c2", parent: "c1", message: "add header" },
  { id: "c3", parent: "c2", message: "add footer" },   // охири main
  { id: "f1", parent: "c2", message: "add filters" },  // шохаи feature аз c2 ҷудо шуд
  { id: "f2", parent: "f1", message: "add sort" },     // охири feature
];
const branches = { main: "c3", feature: "f2" };
const byId = Object.fromEntries(commits.map((c) => [c.id, c]));

function history(branch) {
  // аз branches[branch] оғоз кунед ва аз рӯи parent равед, то он null шавад
}

console.log(history("main").join(" <- "));
console.log(history("feature").join(" <- "));
Натиҷаи интизоршаванда
add footer <- add header <- init project
add sort <- add filters <- add header <- init project
Ишора 1

let id = branches[branch] ва давраи while (id !== null) эълон кунед. Дар бадана: commit-и byId[id]-ро гиред, message-и онро нигоҳ доред, ба parent гузаред.

Ишора 2

id-ро дар ҳар давр иваз карданро фаромӯш накунед, вагарна давра беохир мешавад.

ВАЗИФАИ 2.2

Чӣ танҳо дар шохаи ман ҳаст

миёна

Пеш аз pull request донистан фоиданок аст: кадом commit-ҳо дар шохаи шумо ҳастанд, вале ҳанӯз ба асосӣ нарасидаанд? Дар Git инро ҳамчун main..feature сабт мекунанд. Функсияи history аллакай омода аст. onlyIn(branch, base)-ро анҷом диҳед: паёмҳои commit-ҳо аз branch, ки дар base нестанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const commits = [
  { id: "c1", parent: null, message: "init project" },
  { id: "c2", parent: "c1", message: "add header" },
  { id: "c3", parent: "c2", message: "add footer" },   // охири main
  { id: "f1", parent: "c2", message: "add filters" },  // шохаи feature аз c2 ҷудо шуд
  { id: "f2", parent: "f1", message: "add sort" },     // охири feature
];
const branches = { main: "c3", feature: "f2" };
const byId = Object.fromEntries(commits.map((c) => [c.id, c]));

function history(branch) {
  const result = [];
  let id = branches[branch];
  while (id !== null) {
    result.push(byId[id].message);
    id = byId[id].parent;
  }
  return result;
}

function onlyIn(branch, base) {
  // массиви паёмҳоро баргардонед
}

console.log(onlyIn("feature", "main").join(", "));
console.log(onlyIn("main", "feature").join(", "));
console.log(onlyIn("main", "main").length);
Натиҷаи интизоршаванда
add sort, add filters
add footer
0
Ишора 1

Таърихи ҳарду шохаро гиред ва дар якумӣ танҳо он чиро монед, ки дар дуюмӣ нест: filter + includes.

Ишора 2

return history(branch).filter((m) => !history(base).includes(m));

Муфассалтар хонед: Pro Git — дар бораи шохабандӣ дар ду калима.

03 GitHub: push, pull request, ревью

Git дар компютери шумо зиндагӣ мекунад. GitHub — сайтест, ки нусхаи репозиторий дар он нигоҳ дошта мешавад (онро origin — «репозиторийи дур» меноманд). Маҳз тавассути GitHub гурӯҳ коди худро мубодила мекунад, ва корфармо портфолиои шуморо мебинад.

Терминал
git remote add origin https://github.com/ВАШ_ЛОГИН/chaykhona-site.git
git push -u origin main            # первая отправка, -u запоминает связь веток
git push                           # дальше достаточно просто push
git clone https://github.com/ВАШ_ЛОГИН/chaykhona-site.git   # скачать чужой репозиторий
git pull                           # забрать новые коммиты с GitHub
git fetch                          # только скачать информацию, ничего не менять

git pull — ин fetch (зеркашӣ) + merge (ба шохаи шумо даровардан) аст. Агар дар GitHub commit-ҳои нави ҳамкорон пайдо шуда бошанд, вале дар назди шумо нестанд, пеш аз push аввал pull мекунанд.

Pull request — ғояи асосии кори гурӯҳӣ

Pull request (PR) — ин хоҳиш аст: «тағйироти маро дар шоха бинед ва агар ҳама хуб бошад, онҳоро ба main дароред». PR фармони Git нест, балки имкони GitHub аст. Шумо шохаро push мекунед, дар сайт «Compare & pull request»-ро пахш мекунед, тавсиф менависед, ва ҳамкор тағйиротро сатр ба сатр мехонад, шарҳ мемонад, тасдиқ мекунад ё ислоҳ металабад.

Монандӣ: чек пеш аз пардохт дар бозор

Фурӯшанда маблағро дар коғазча навишт, аммо пеш аз пардохт шумо коғазчаро ба ҳамсоя нишон медиҳед: «Санҷ, дуруст ҷамъ кардаам?». Ҳамсоя хатои як сомониро ёфт — шумо ислоҳ кардед, ва танҳо баъд пул додед. PR — ҳамон «нигоҳи аз берун» пеш аз он аст, ки код ба шохаи асосӣ мерасад.

Давраи корӣ дар ҷуфт

Маҳз ҳамин тавр шумо имрӯз дар машғулот ва минбаъд — дар лоиҳаҳо ва дар кор кор мекунед:

  1. main-ро навсозӣ кунед: git switch main, git pull.
  2. Шоха эҷод кунед барои як вазифа: git switch -c feature/order-filters.
  3. Кор кунед ва commit кунед бо қадамҳои хурди бомаъно.
  4. Шохаро фиристед: git push -u origin feature/order-filters. GitHub истиноди эҷоди PR-ро чоп мекунад.
  5. Pull request-ро кушоед: сарлавҳа, чӣ анҷом шуд, барои чӣ, чӣ тавр санҷидан. Ҳамкорро ҳамчун ревьюер таъин кунед.
  6. Ревью: ҳамкор diff-ро мехонад, шарҳ менависад. Шумо ҷавоб медиҳед ва ислоҳ мекунед — commit-ҳои нав ба ҳамон шоха худашон ба PR мерасанд.
  7. Merge: пас аз тасдиқ (approve) ва санҷишҳои муваффақ PR-ро бо тугмаи Merge pull request дар GitHub ба main медароранд.
  8. Аз паси худ тоза кунед: git switch main, git pull, git branch -d feature/order-filters.
Ҳангоми ревью ба чӣ нигоҳ кардан лозим

Оё код кор мекунад (шохаро иҷро кунед)? Оё номҳо фаҳмоанд? Оё файлҳои зиёдатӣ ва console.log нестанд? Оё ҳолатҳои канорӣ тасвир шудаанд (рӯйхати холӣ, сифр, хатои шабака)? Шарҳро дар бораи код менависем, на дар бораи одам: «Дар ин ҷо ҳангоми рӯйхати холӣ хато мешавад» ба ҷои «Ту фаромӯш кардӣ».

Се роҳи дарорҷарии PR

ТугмаДар таърих чӣ мешавад
Create a merge commitҲамаи commit-ҳои шоха нигоҳ дошта мешаванд, илова бар ин commit-и ҷамъкунӣ
Squash and mergeҲамаи commit-ҳои PR ба як часпонда мешаванд — таърихи main кӯтоҳтар аст
Rebase and mergeCommit-ҳои шоха ба main яктоӣ интиқол меёбанд, бе commit-и ҷамъкунӣ

Барои оғоз варианти якум мувофиқ аст. Дигаронро шумо вақте интихоб мекунед, ки дар гурӯҳ дар бораи услуби таърих мувофиқа кунанд.

Қоидаҳои амният

Ба main бевосита push накунед ва PR-и худро бе ревью ҷамъ накунед. Дар бисёр гурӯҳҳо main баста аст: танҳо тавассути PR.

git push --force-ро истифода набаред, то даме ки намефаҳмед чӣ мекунед: фармон commit-ҳои бегонаро дар сервер нест мекунад.

Ҳаргиз паролҳо ва калидҳои API-ро commit накунед. Дар бахши .gitignore шарҳ медиҳем.

Амал: мустаҳкам мекунем

ВАЗИФАИ 3.1

Тавсифи pull request

миёна

Тавсифи хуби PR вақти ревьюерро сарфа мекунад. buildPr(pr)-ро анҷом диҳед: функсия як сатр бо формати «Натиҷаи интизоршаванда»-ро бармегардонад: сарлавҳаи «Что сделано» бо рӯйхати нишонадор, сатри холӣ, «Зачем», сатри холӣ, «Как проверить» бо рӯйхати рақамгузорӣшуда.

Таҳрир кардан мумкин аст · Ctrl + Enter
function buildPr(pr) {
  // сатрро баргардонед; массиви сатрҳоро ҷамъ кунед ва join("\n")-ро даъват намоед
}

const pr = {
  what: ["добавлен фильтр заказов по статусу", "фильтр запоминается в localStorage"],
  why: "Менеджер хочет быстро находить неоплаченные заказы.",
  check: ["Откройте страницу заказов", "Выберите статус «Не оплачен»", "Обновите страницу: фильтр остался"],
};
console.log(buildPr(pr));
Натиҷаи интизоршаванда
## Что сделано
- добавлен фильтр заказов по статусу
- фильтр запоминается в localStorage

## Зачем
Менеджер хочет быстро находить неоплаченные заказы.

## Как проверить
1. Откройте страницу заказов
2. Выберите статус «Не оплачен»
3. Обновите страницу: фильтр остался
Ишора 1

Аз массиви const lines = ["## Что сделано"] оғоз кунед. Бандҳоро бо forEach илова кунед: lines.push(`- ${w}`). Сатри холӣ дар массив — ин танҳо "" аст.

Ишора 2

Рақамгузорӣ: pr.check.forEach((c, i) => lines.push(`${i + 1}. ${c}`)). Дар охир — lines.join("\n").

ВАЗИФАИ 3.2

Оё merge кардан мумкин аст

душвор

Дар GitHub тугмаи Merge то иҷрои шартҳо «хокистарӣ» аст. mergeProblems(pr)-ро анҷом диҳед: функсия массиви сабабҳоро бармегардонад (бо ин тартиб): «нет одобрения» (approvals < 1), «проверки не прошли» (checks ба "passed" баробар нест), «есть конфликт» (conflicts), «это черновик» (draft). Агар сабаб набошад — массиви холӣ.

Таҳрир кардан мумкин аст · Ctrl + Enter
function mergeProblems(pr) {
  const problems = [];
  // чор шартро санҷед ва сабабҳоро ба массив илова кунед
  return problems;
}

const prs = [
  { title: "add order filters", approvals: 1, checks: "passed", conflicts: false, draft: false },
  { title: "fix cart total", approvals: 0, checks: "passed", conflicts: false, draft: false },
  { title: "wip: new header", approvals: 2, checks: "failed", conflicts: true, draft: true },
];
for (const pr of prs) {
  const p = mergeProblems(pr);
  console.log(`${pr.title}: ${p.length ? p.join("; ") : "можно мержить"}`);
}
Натиҷаи интизоршаванда
add order filters: можно мержить
fix cart total: нет одобрения
wip: new header: проверки не прошли; есть конфликт; это черновик
Ишора 1

Чор if-и мустақил, ҳар кадом problems.push("…") мекунад. else дар ин ҷо лозим нест: сабабҳо метавонанд якбора якчанд бошанд.

Ишора 2

Барои санҷишҳо pr.approvals < 1 ва pr.checks !== "passed"-ро истифода баред.

Муфассалтар хонед: GitHub Docs — дар бораи pull request, Pro Git — репозиторийҳои дур.

04 Низоъҳои ҷамъкунӣ (merge conflict)

Git метавонад тағйиротро худкор ҷамъ кунад, агар шумо ва ҳамкор ҷойҳои гуногунро таҳрир карда бошед. Аммо агар шумо ҳарду як сатри ягонаро гуногун тағйир диҳед, Git намедонад, версияи кӣ дуруст аст, ва аз одам хоҳиш мекунад, ки ҳал кунад. Ин низоъ (conflict) аст. Он вайроншавӣ нест, балки қисми оддии кор: дар ҳар гурӯҳ низоъҳо ҳар ҳафта рух медиҳанд.

Фарз кунем, ки шумо бо ҳамкор файли rate.js-ро таҳрир кардед. Ҳамкор шохаи худро аллакай ба main даровард, ва шумо дар шохаи худ git pull origin main кардед:

Терминал
git switch feature/rate
git pull origin main
# Auto-merging rate.js
# CONFLICT (content): Merge conflict in rate.js
# Automatic merge failed; fix conflicts and then commit the result.

Git истод ва дар файл маркерҳои низоъ-ро сабт кард:

rate.js пас аз merge-и ноком
// қурби мубодила
<<<<<<< HEAD
const convertUsd = (usd, rate = 10.9) => Math.round(usd * rate);
=======
const toSomoni = (usd) => usd * 11;
>>>>>>> origin/main
<<<<<<< HEAD
Оғози низоъ. Дар зер — версияи шумо (аз шохае, ки дар он истодаед).
=======
Ҷудокунанда. Дар зер — версияи аз шохаи дигар (дар ин ҷо origin/main).
>>>>>>> origin/main
Охири низоъ ва номи шохае, ки версияи дуюм аз он омадааст.

Низоъро чӣ тавр ҳал кардан

  1. Файлро кушоед бо калимаи CONFLICT. Дар VS Code болои блок тугмаҳо пайдо мешаванд: Accept Current (аз они ман), Accept Incoming (аз они бегона), Accept Both (ҳарду) ва Compare.
  2. Аз рӯи маъно ҳал кунед, ки чӣ бояд бимонад. Гоҳо бояд порчаҳоро аз ҳарду версия гирифт — ва коди ниҳоиро бо даст навишт. Бо муаллифи таҳрири дуюм мувофиқа кунед.
  3. Ҳамаи се сатри маркерҳоро хориҷ кунед. Дар файл танҳо коди муқаррарӣ бояд бимонад.
  4. Лоиҳаро иҷро кунед ва боварӣ ҳосил намоед, ки ҳама кор мекунад.
  5. Ҷамъкуниро анҷом диҳед:
    Терминал
    git add rate.js
    git commit -m "merge main into feature/rate"
    git push
Агар ошуфтед

Фармони git merge --abort (ё git rebase --abort) ҷамъкуниро бекор мекунад ва ҳама чизро ба ҳолати пеш аз он бармегардонад. Низоъро дар шохаи худ пеш аз merge ҳал мекунанд: он гоҳ ба main натиҷаи тоза мерасад.

Хатои хатарнок

Файлеро commit кардан, ки дар он сатрҳои <<<<<<< мондаанд. Чунин код иҷро намешавад. Пеш аз git add дар лоиҳа <<<<<<<-ро ҷустуҷӯ кунед (Ctrl+Shift+F дар VS Code).

Маркерҳо — матни оддӣ ҳастанд, ва онҳоро бо код таҳлил кардан мумкин аст. Вазифаи аввалро шумо бо даст, мисли низои ҳақиқӣ ҳал мекунед. Дар дуюмӣ «ҳалли худкор»-ро менависед, чунон ки муҳаррирҳо мекунанд.

Амал: мустаҳкам мекунем

ВАЗИФАИ 4.1

Низоъро бо даст ҳал мекунем

миёна

Пас аз merge-и ноком файл мисли дар тағйирёбандаи conflicted менамояд. Гурӯҳ қарор дод: номи функсияро аз main (toSomoni) мемонем, ва формула-ро аз шохаи feature/rate мегирем: параметри rate бо қимати пешфарзии 10.9 ва мудаввар кардан. Дар resolved матни ниҳоии файлро нависед: бе маркерҳо ва бо як сатри код. Чор санҷиши зер бояд false / true / true / true диҳанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const conflicted = `// курс обмена
<<<<<<< HEAD
const toSomoni = (usd) => usd * 11;
=======
const convertUsd = (usd, rate = 10.9) => Math.round(usd * rate);
>>>>>>> feature/rate
`;

const resolved = `// курс обмена
`; // сатри ниҳоии кодро анҷом диҳед

console.log("маркеры остались:", /<<<<<<<|=======|>>>>>>>/.test(resolved));
console.log("имя из main:", resolved.includes("const toSomoni ="));
console.log("формула из feature:", resolved.includes("rate = 10.9") && resolved.includes("Math.round(usd * rate)"));
console.log("лишнее убрано:", !resolved.includes("convertUsd") && !resolved.includes("usd * 11"));
Натиҷаи интизоршаванда
маркеры остались: false
имя из main: true
формула из feature: true
лишнее убрано: true
Ишора 1

Натиҷа — як сатр: const toSomoni = (usd, rate = 10.9) => Math.round(usd * rate);. Сатрҳои <<<<<<<, ======= ва >>>>>>> дар файл мондан намебояд.

Ишора 2

Санҷиш дар матн порчаҳои дақиқро меҷӯяд: const toSomoni =, rate = 10.9 ва Math.round(usd * rate). Ба фосилаҳо ва хатоҳои имлоӣ диққат диҳед.

ВАЗИФАИ 4.2

Ҳалли худкор

душвор

resolveAll(text, side)-ро нависед, ки маркерҳои низоъро хориҷ мекунад. side = "ours" версияро аз HEAD (то =======) мемонад, "theirs" — версияро аз шохаи бегона (пас аз =======), "both" — ҳарду пай дар пай. Сатрҳои берун аз низоъ чунон ки ҳастанд, нигоҳ дошта мешаванд. Аз рӯи сатрҳо гузаред ва дар ёд доред, ки ҳоло дар кадом қисми блок ҳастед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const conflicted = [
  "<h1>MakeFirstStep</h1>",
  "<<<<<<< HEAD",
  "<p>Курсы программирования</p>",
  "=======",
  "<p>Курсы Frontend и ИИ</p>",
  ">>>>>>> feature/text",
  "<footer>Душанбе</footer>",
].join("\n");

function resolveAll(text, side) {
  const out = [];
  let part = "none"; // none | ours | theirs
  for (const line of text.split("\n")) {
    // коди шумо
  }
  return out.join("\n");
}

for (const side of ["ours", "theirs", "both"]) {
  console.log(side + ": " + resolveAll(conflicted, side).split("\n").join(" / "));
}
Натиҷаи интизоршаванда
ours: <h1>MakeFirstStep</h1> / <p>Курсы программирования</p> / <footer>Душанбе</footer>
theirs: <h1>MakeFirstStep</h1> / <p>Курсы Frontend и ИИ</p> / <footer>Душанбе</footer>
both: <h1>MakeFirstStep</h1> / <p>Курсы программирования</p> / <p>Курсы Frontend и ИИ</p> / <footer>Душанбе</footer>
Ишора 1

Се «гузарондагар»: сатри бо <<<<<<< оғозёфта реҷаи "ours"-ро фаъол мекунад; сатри ======= — "theirs"; сатри бо >>>>>>> "none"-ро бармегардонад. Худи маркерҳо ба натиҷа намеафтанд.

Ишора 2

Сатри берун аз низоъро (part === "none") ҳамеша илова мекунем; сатри дарунро — танҳо агар part === side ё side === "both".

Муфассалтар хонед: Pro Git — асосҳои шохабандӣ ва ҷамъкунӣ (низоъҳо).

05 .gitignore ва паёмҳои commit

.gitignore: чӣ набояд ба репозиторий афтад

Дар папкаи лоиҳа файлҳое ҳастанд, ки онҳоро дар Git нигоҳ доштан мумкин нест ё лозим нест. Онҳоро дар файли .gitignore дар решаи лоиҳа номбар мекунанд: Git онҳоро пайгирӣ намекунад ва дар git status нишон намедиҳад.

.gitignore
# зависимости: восстанавливаются командой npm install
node_modules/

# готовая сборка: создаётся командой npm run build
dist/

# секреты: пароли, ключи API
.env

# логи и системный мусор
*.log
.DS_Store
ном/
Папка бо ин ном, ҳар ҷое ки бошад: dist/, node_modules/.
*.васеъкунӣ
Ҳамаи файлҳо бо чунин анҷом: *.log.
ном
Файл бо айнан чунин ном: .env. Файли .env.example ба ин қоида намеафтад.
!ном
Истисно: файлро ба назорати Git баргардонидан. Пас аз қоидаи умумӣ меистад.
# матн
Шарҳ.

.gitignore-и ҳақиқӣ боз бисёр синтаксисро дастгирӣ мекунад (**, роҳҳо бо хати моил), аммо ин қариб ҳамеша кифоя аст. Дар вазифаи зер шумо маҳз чунин «муҳаррики» соддакардашударо истифода мебаред.

Гаронтарин хато

.env-ро бо калид commit карданд, баъд онро ба .gitignore илова намуданд. Кумак намекунад: файл аллакай дар таърих аст. Ҳар кас репозиторийро кушояд, калидро дар commit-и кӯҳна меёбад. Дуруст: фавран калидро дар хидмат бекор кунед (иваз кунед) ва танҳо баъд репозиторийро тоза намоед. Агар файл аллакай пайгирӣ мешавад, онро бо фармони git rm --cached .env аз Git хориҷ кардан мумкин аст — дар диск он мемонад.

Паёмҳои commit: Conventional Commits

Пас аз ним сол шумо ба ёд намеоред, ки «update» ё «fix» чӣ маъно дошт. Барои ҳамин дар гурӯҳҳо дар бораи формат мувофиқа мекунанд: навъ(соҳа): чӣ анҷом шуд. Соҳа ихтиёрӣ аст.

НавъКайМисол
featимконияти навfeat: add order filters
fixислоҳи хатоfix(cart): count first item in total
docsҳуҷҷатгузорӣ, READMEdocs: add install steps
styleшакли ороишӣ бе таъсир ба маъноstyle: format with prettier
refactorаз нав навиштанд, рафтор ҳамонrefactor: extract calcTotal
testсанҷишҳо (тестҳо)test: cover empty cart
choreхизматӣ: вобастагиҳо, танзимотchore: bump deps
Бад

update, fix, changes, asdf, final, Fixed bug. Нофаҳмо чӣ, дар куҷо ва барои чӣ; commit-и даркориро ёфтан намешавад.

Хуб

fix: wrong total in cart, feat(ui): add dark theme. Феъл дар шакли амр («add», «fix»), ҳарфҳои хурд, то 50 аломат, бе нуқта, як андеша барои як commit.

Барои мустаҳкам кардан мебинем, ки таснифи паёмҳо дар код чӣ гуна менамояд. Мисолро иҷро кунед ва паёмҳоро тағйир диҳед:

Навъи commit аз рӯи паём · Ctrl + Enter
const messages = ["feat: add filters", "fix(cart): sum", "update", "docs: readme"];

for (const m of messages) {
  const match = m.match(/^([a-z]+)(\([^)]*\))?: /);
  console.log(m.padEnd(20), "->", match ? match[1] : "нет типа");
}

Амал: мустаҳкам мекунем

ВАЗИФАИ 5.1

.gitignore-ро менависем

миёна

Дар зер — «муҳаррик»-и соддакардашудаи .gitignore (он ном/, *.васеъкунӣ, номи дақиқ ва !бекор-ро мефаҳмад). Ба массиви rules чунин қоидаҳо нависед, ки папкаҳои node_modules, dist, coverage, файли .env (аммо на .env.example), .DS_Store-и низомӣ ва ҳамаи *.log нодида гирифта шаванд, ба ҷуз keep.log. Боқӣ бояд зери назорати Git бимонад.

Таҳрир кардан мумкин аст · Ctrl + Enter
function matchRule(rule, path) {
  const parts = path.split("/");
  const name = parts[parts.length - 1];
  if (rule.endsWith("/")) {                          // «папка/»: ҳар феҳрист бо чунин ном
    return parts.slice(0, -1).includes(rule.slice(0, -1));
  }
  if (rule.startsWith("*.")) return name.endsWith(rule.slice(1)); // «*.log»: аз рӯи васеъкунӣ
  return name === rule;                              // номи дақиқи файл
}

function isIgnored(path, rules) {
  let ignored = false;
  for (const rule of rules) {                        // қоидаҳо аз боло ба поён хонда мешаванд
    if (rule.startsWith("!")) {                      // «!ном» файлро бозпас мегардонад
      if (matchRule(rule.slice(1), path)) ignored = false;
    } else if (matchRule(rule, path)) {
      ignored = true;
    }
  }
  return ignored;
}

const paths = [
  "node_modules/react/index.js", "dist/app.js", "src/app.js", ".env", ".env.example",
  "debug.log", "logs/server.log", "keep.log", "README.md", ".DS_Store", "src/.DS_Store", "coverage/index.html",
];

const rules = [
  // қоидаҳоро бо сатрҳо нависед
];

console.log("игнорируются:", paths.filter((p) => isIgnored(p, rules)).join(", "));
console.log("остаются:", paths.filter((p) => !isIgnored(p, rules)).join(", "));
Натиҷаи интизоршаванда
игнорируются: node_modules/react/index.js, dist/app.js, .env, debug.log, logs/server.log, .DS_Store, src/.DS_Store, coverage/index.html
остаются: src/app.js, .env.example, keep.log, README.md
Ишора 1

Папкаҳо бо хати моил дар охир навишта мешаванд: "dist/". Васеъкунӣ — бо ситорача: "*.log". Файл — бо номи дақиқ: ".env".

Ишора 2

Истисно бояд пас аз қоидаи умумӣ истад: "*.log", "!keep.log". Қоидаи охирини мувофиқ мепирӯзад.

ВАЗИФАИ 5.2

Омори commit-ҳо

осон

Дар формати Conventional Commits паём бо навъ оғоз мешавад: feat: …, fix(cart): …. commitType(msg)-ро анҷом диҳед: он навъро (калима то дунуқта ё то қавс) ё "other"-ро, агар паём аз рӯи формат нест, бармегардонад. Ҳисоб ва чоп аллакай омодаанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const messages = [
  "feat: add order filters",
  "fix: wrong total in cart",
  "docs: update readme",
  "fix(cart): off-by-one in loop",
  "update",
  "feat(ui): dark theme",
  "fix: crash on empty order",
  "Changes",
];

function commitType(msg) {
  // регулярка: калима дар аввал, (соҳа)-и ихтиёрӣ, дунуқта ва фосила
  return "other";
}

const counts = {};
for (const m of messages) {
  const type = commitType(m);
  counts[type] = (counts[type] || 0) + 1;
}
console.log(JSON.stringify(counts));
Натиҷаи интизоршаванда
{"feat":2,"fix":3,"docs":1,"other":2}
Ишора 1

Регулярка: /^([a-z]+)(\([^)]*\))?: /. Гурӯҳи якум — навъ, дуюм (ихтиёрӣ) — соҳа дар қавс.

Ишора 2

const m = msg.match(regex); return m ? m[1] : "other";

ВАЗИФАИ 5.3

Линтери commit-ҳо

душвор

lintCommit(msg)-ро нависед — массиви мушкилот. Қоидаҳо: (1) паём бояд бо feat|fix|docs|style|refactor|test|chore, (соҳа)-и ихтиёрӣ ва : оғоз шавад; вагарна як мушкили «нет типа вида feat: …» — минбаъд санҷидан лозим нест; (2) «тема длиннее 50 символов» (мавзӯъ — матни пас аз : ); (3) «тема с заглавной буквы»; (4) «точка в конце». Массиви холӣ маънои «ҳама хуб»-ро дорад.

Таҳрир кардан мумкин аст · Ctrl + Enter
function lintCommit(msg) {
  const problems = [];
  return problems;
}

const list = [
  "feat: add order filters",
  "Fixed bug.",
  "fix(cart): Wrong total.",
  "docs: describe in the readme how to install dependencies and run the project",
];
for (const m of list) {
  const p = lintCommit(m);
  console.log(`${m.slice(0, 24)} -> ${p.length ? p.join("; ") : "ok"}`);
}
Натиҷаи интизоршаванда
feat: add order filters -> ok
Fixed bug. -> нет типа вида feat: …
fix(cart): Wrong total. -> тема с заглавной буквы; точка в конце
docs: describe in the re -> тема длиннее 50 символов
Ишора 1

Як регулярка бо се гурӯҳ: /^(feat|fix|docs|style|refactor|test|chore)(\([^)]*\))?: (.+)$/. Мавзӯъ — ин m[3] аст.

Ишора 2

Агар match null баргардонд — фавран return ["нет типа вида feat: …"]. Санҷишҳои боқӣ — се if-и мустақил бо push.

Муфассалтар хонед: Conventional Commits, ҳуҷҷатҳои .gitignore.

06 Хатоҳо: try / catch / finally ва throw

Хатоҳо ба ҳама рух медиҳанд: корбар сатри холӣ ворид кард, сервер ҷавоб надод, аз localStorage партов омад. Агар коре накунем, барнома дар сатри аввалини дорои хато меистад. Вазифаи таҳиягар — пешакӣ дида баромадани ҷойҳоест, ки код метавонад биафтад, ва ҳамин тавр кардан, ки корбар паёми фаҳморо бубинад, на экрани сафедро.

try / catch / finally

Монандӣ: суғурта дар маршрутка

try — «кӯшиш мекунем аз ин роҳ равем». catch — нақшаи эҳтиётӣ, агар роҳ баста бошад: рӯ тоб додан ва аз гирди роҳ рафтан. finally — он чизе, ки ба ҳар ҳол мешавад: аз маршрутка баромадан ва ба ронанда пул додан.

Се блок · Ctrl + Enter
function readAge(text) {
  try {
    const age = JSON.parse(text);          // метавонад хато партояд
    console.log("прочитали:", age);
    return age;
  } catch (error) {
    console.log("не получилось:", error.name);
    return 0;                              // нақшаи эҳтиётӣ
  } finally {
    console.log("— чтение закончено —");   // ҳамеша иҷро мешавад
  }
}

console.log(readAge("19"));
console.log(readAge("девятнадцать"));

Хато дар дохили try онро фавран қатъ мекунад, ва идора ба catch мегузарад. Сатрҳои боқимондаи try гузашта мешаванд. Ба catch объекти хато меояд (онро одатан error ё e меноманд).

Дар дохили объекти хато чӣ ҳаст

name
Навъи хато: TypeError, ReferenceError, SyntaxError, RangeError…
message
Тавсифи инсонӣ: «Cannot read properties of null».
stack
Стеки даъватҳо — занҷири функсияҳое, ки ба хато овардаанд. Дар қисми 7 таҳлил мекунем.

Навъҳои асосии хатоҳо

НавъКай рух медиҳадМисол
TypeErrorАмал барои ин навъи қимат ғайриимкон аст: аз null / undefined хосият мехонем, чизеро, ки функсия нест, даъват мекунемnull.name
ReferenceErrorТағйирёбанда вуҷуд надорад (хатои имлоӣ, эълон нашудааст, берун аз соҳаи намоӣ)console.log(pirce)
SyntaxErrorКод бар хилофи қоидаҳои забон навишта шудааст; ҳамчунин хатоҳои JSON.parseJSON.parse("{a: 1}")
RangeErrorҚимат берун аз доираи иҷозатшудаnew Array(-1)
Ба объекти хато менигарем · Ctrl + Enter
try {
  const user = null;
  console.log(user.name);
} catch (error) {
  console.log("name:", error.name);
  console.log("message:", error.message);
  console.log("есть stack:", typeof error.stack === "string");
}

throw: хатои худро мепартоем

Гоҳо хаторо худатон бояд эҷод кунед. Масалан, функсия аз корбар миқдори манфӣ гирифт. throw функсияи ҷориро қатъ мекунад ва хаторо ба боло — то наздиктарин catch «мепартояд». Объекти хато-ро партоед (new Error("матн")), на танҳо сатр: объект стек дорад.

throw new Error · Ctrl + Enter
function setQuantity(qty) {
  if (!Number.isInteger(qty)) throw new TypeError("Количество должно быть целым числом");
  if (qty < 1) throw new RangeError("Количество не может быть меньше 1");
  return qty;
}

for (const value of [3, 0, 2.5]) {
  try {
    console.log("ок:", setQuantity(value));
  } catch (e) {
    console.log(`${e.name}: ${e.message}`);
  }
}
Хатои маъмул: catch-и холӣ

catch (e) {}-и холӣ хаторо «фурӯ мебарад»: барнома хомӯш аст ва ғайриодатӣ рафтор мекунад, ва сабабро ёфтан намешавад. Ҳадди ақал — хаторо ба консол бароред ё ба корбар паём нишон диҳед. Ва танҳо он хатоҳоеро бигиред, ки коркард карда метавонед: боқиро тавассути throw e ба пеш гузаронед (вазифаи 6.5).

Ин код хато медиҳад · Ctrl + Enter
function risky() {
  throw new Error("что-то пошло не так");
}

risky();   // хато гирифта нашуд: иҷро қатъ шуд
console.log("до этой строки мы уже не дойдём");

Хатоҳоро дар коди ғайримуттаҳид бо ҳамон try / catch дар гирди await мегиранд (шумо инро дар дарси 13 дидед, ва дар дарсҳои минбаъда ҳамин тавр дархостҳои fetch-ро коркард мекунед). try-и алоҳида дар гирди setTimeout кор намекунад: колбэк баъдтар иҷро мешавад, вақте блок аллакай анҷом ёфтааст.

Амал: мустаҳкам мекунем

ВАЗИФАИ 6.1

Пешгӯӣ кунед: тартиб бо finally

осон

Аввал бе иҷро ҳал кунед, ки сатрҳо бо кадом тартиб пайдо мешаванд. Блоки finally «ҳамеша» иҷро мешавад — ҳатто агар дар try аллакай return буд. Пешгӯиро ба prediction нависед (сатрҳо бо тартиб, айнан чунон ки console.log нишон медод) ва иҷро кунед. Боқии кодро тағйир додан лозим нест.

Таҳрир кардан мумкин аст · Ctrl + Enter
const log = [];   // дар ин ҷо код «баромад»-ро ҷамъ мекунад
function test() {
  try {
    log.push("1: try");
    return "результат";
  } catch (e) {
    log.push("catch");
  } finally {
    log.push("2: finally");
  }
  log.push("после");
}
log.push("3: " + test());

const prediction = [];   // пешгӯии шумо: сатрҳо бо тартиб
console.log(log.join("|") === prediction.join("|") ? "Верно!" : "Не угадали, подумайте ещё");
Натиҷаи интизоршаванда
Верно!
Ишора 1

Функсия бо return тамом мешавад, аммо қимат ба берун танҳо пас аз он «дода мешавад», ки finally иҷро шавад.

Ишора 2

Сатри «после» пайдо намешавад: функсия аллакай қиматро баргардонд. «catch» пайдо намешавад: хато набуд.

ВАЗИФАИ 6.2

Номи хато

осон

errorName(fn)-ро анҷом диҳед: функсияро дар дохили try даъват кунед, ва агар хато партояд — name-и онро баргардонед (масалан, "TypeError"). Агар хато набошад — "ok"-ро баргардонед.

Таҳрир кардан мумкин аст · Ctrl + Enter
function errorName(fn) {
  // try / catch
}

const tests = [
  () => { const user = null; return user.name; },
  () => missingVariable + 1,
  () => JSON.parse("{name: 'Али'}"),
  () => new Array(-5),
  () => 2 + 2,
];
tests.forEach((t, i) => console.log(i + 1, errorName(t)));
Натиҷаи интизоршаванда
1 TypeError
2 ReferenceError
3 SyntaxError
4 RangeError
5 ok
Ишора 1

Сохтор: try { fn(); return "ok"; } catch (e) { return e.name; }.

Ишора 2

Хосияти name дар объекти хато — ин «навъ» аст: TypeError, ReferenceError, SyntaxError, RangeError.

ВАЗИФАИ 6.3

Хондани бехатари JSON

миёна

Маълумот аз localStorage ё аз сервер метавонад вайрон бошад. safeParse(text, fallback)-ро анҷом диҳед: натиҷаи JSON.parse-ро бармегардонад, ва ҳангоми хато ошибка чтения: ИмяОшибки-ро чоп мекунад ва fallback-ро бармегардонад.

Таҳрир кардан мумкин аст · Ctrl + Enter
function safeParse(text, fallback) {
  // try / catch
}

console.log(safeParse('{"name":"Фарҳод","age":19}', null).name);
console.log(safeParse("{name: Фарҳод}", "пусто"));
console.log(safeParse("", []).length);
Натиҷаи интизоршаванда
Фарҳод
ошибка чтения: SyntaxError
пусто
ошибка чтения: SyntaxError
0
Ишора 1

Дар try — return JSON.parse(text). Дар catch — console.log("ошибка чтения:", e.name) ва return fallback.

Ишора 2

Сатри холӣ ҳам JSON-и нодуруст аст, барои ҳамин барои даъвати сеюм catch ҳам кор мекунад ва массиви холӣ бармегардонад.

ВАЗИФАИ 6.4

Хатоҳои худӣ тавассути throw

миёна

withdraw(balance, amount)-ро анҷом диҳед — гирифтани сомонӣ. Агар amount аз сифр зиёд набошад, new RangeError("Сумма должна быть больше нуля")-ро партоед. Агар amount аз баланс зиёд бошад — new Error("Недостаточно сомони"). Вагарна боқимондаро баргардонед. Тартиби санҷишҳо — чунон ки дар шарт.

Таҳрир кардан мумкин аст · Ctrl + Enter
function withdraw(balance, amount) {
  // ду санҷиш бо throw, баъд return
}

for (const amount of [200, 0, 5000, -10]) {
  try {
    console.log("остаток:", withdraw(1000, amount));
  } catch (e) {
    console.log(`${e.name}: ${e.message}`);
  } finally {
    console.log("— операция завершена —");
  }
}
Натиҷаи интизоршаванда
остаток: 800
— операция завершена —
RangeError: Сумма должна быть больше нуля
— операция завершена —
Error: Недостаточно сомони
— операция завершена —
RangeError: Сумма должна быть больше нуля
— операция завершена —
Ишора 1

throw функсияро фавран қатъ мекунад, барои ҳамин пас аз он else лозим нест: if (amount <= 0) throw new RangeError("…");.

Ишора 2

Блоки finally дар давра пас аз ҳар амал иҷро мешавад — ҳам муваффақ, ҳам ноком.

ВАЗИФАИ 6.5

Танҳо хатоҳои худро мегирем

душвор

catch-и хуб танҳо хатоҳоеро коркард мекунад, ки интизор аст, ва бегонаро ба пеш мегузаронад: throw e. validate(order)-ро анҷом диҳед: агар дар фармоиш name набошад, fail("name", "укажите имя")-ро даъват кунед; агар qty аз 1 камтар бошад — fail("qty", "минимум 1 штука"). Функсияи ёрирасони fail аллакай ҳаст. Даъвати process(null) бояд бо TypeError-и ҳақиқӣ биафтад, ки process онро фурӯ намебарад.

Таҳрир кардан мумкин аст · Ctrl + Enter
function fail(field, message) {
  const e = new Error(message);
  e.name = "ValidationError";
  e.field = field;
  throw e;
}

function validate(order) {
  // ду санҷиш тавассути fail(...)
}

function process(order) {
  try {
    validate(order);
    return "заказ принят";
  } catch (e) {
    if (e.name === "ValidationError") return `поле ${e.field}: ${e.message}`;
    throw e; // хатоҳои бегонаро фурӯ намебарем
  }
}

console.log(process({ name: "Мадина", qty: 2 }));
console.log(process({ name: "", qty: 2 }));
console.log(process({ name: "Рустам", qty: 0 }));
try {
  process(null);
} catch (e) {
  console.log("неожиданная ошибка:", e.name);
}
Натиҷаи интизоршаванда
заказ принят
поле name: укажите имя
поле qty: минимум 1 штука
неожиданная ошибка: TypeError
Ишора 1

if (!order.name) fail("name", "укажите имя");. Санҷиши дуюм айнан ҳамин тавр навишта мешавад, бо шарти order.qty < 1.

Ишора 2

Барои null худи сатри аввал order.name TypeError меандозад: null хосият надорад. validate-и шумо ин ҳолатро махсус коркард кардан набояд.

Муфассалтар хонед: learn.javascript.ru — коркарди хатоҳо, try…catch, MDN — try...catch.

07 Стеки даъватҳо ва DevTools

Хатои сурхро чӣ тавр хондан

Вақте код меафтад, браузер сатри сурх ва стеки даъватҳо-ро чоп мекунад: рӯйхати функсияҳо аз ҷойи хато то оғози кор. Онро аз боло ба поён хонед: сатри якум — дар куҷо афтод, сатрҳои баъдӣ — кӣ ин функсияро даъват кард.

Хато дар консоли браузер
Uncaught TypeError: Cannot read properties of undefined (reading 'price')
    at calcTotal (cart.js:12:23)
    at renderCart (ui.js:48:15)
    at HTMLButtonElement.<anonymous> (main.js:7:3)
  1. Навъ ва паём: TypeError — хосияти price-ро аз undefined мехонем. Яъне дар ягон ҷо ба ҷои маҳсулот undefined омад.
  2. Сатри болоии стек: calcTotal (cart.js:12:23) — функсияи calcTotal, файли cart.js, сатри 12, аломати 23. Дар ин ҷо афтод.
  3. Сатри дуюм: renderCart calcTotal-ро даъват кард. Аксар вақт хато на дар функсияи боло, балки дар он чизест, ки ба он дода шуд.
  4. Сатри охирин: ҳама чиз аз зеркунӣ ба тугма дар main.js оғоз шуд.

Зеркунӣ ба cart.js:12 дар консоли DevTools ҳамон файлро дар сатри даркорӣ мекушояд.

Монандӣ: занҷири интиқол дар курс

Донишҷӯ аз куратор мепурсад, куратор — аз муаллим, муаллим — аз методист, ва дар охир маълум мешавад, ки дар ҷадвал хато ҳаст. Стек — ин сабти он аст, ки кӣ аз кӣ пурсид. Барои ёфтани он, ки хато аз куҷост, занҷирро аз поён ба боло мегузарем, аммо ба даъвати болоӣ менигарем: маҳз он «натавонист».

Стек дар код: хосияти stack · Ctrl + Enter
function inner() {
  throw new Error("сломалось внутри");
}
function outer() {
  inner();
}

try {
  outer();
} catch (e) {
  const first = e.stack.split("\n")[0];   // сатри якуми стек — «Навъ: паём»
  console.log(first);
  console.log("в стеке есть outer:", e.stack.includes("outer"));
}

DevTools: абзорҳои таҳиягар

DevTools-ро дар ҳар саҳифа кушоед: F12 ё Ctrl+Shift+I (дар Mac — Cmd+Option+I). Ҷадвалҳои асосӣ барои ислоҳи хато:

Console — консол

Хатоҳо, огоҳиҳо ва console.log-ҳои шуморо нишон медиҳад. JavaScript-ро бевосита дар консол навиштан ва фавран натиҷа гирифтан мумкин аст. Методҳои муфид:

МетодБарои чӣ
console.log(x)Танҳо қиматро чоп мекунад. Ҳила: console.log({ price, qty }) номҳоро якҷоя бо қиматҳо чоп мекунад
console.table(array)Дар DevTools массиви объектҳоро ҳамчун ҷадвал бо сутунҳо нишон медиҳад. (Дар ин саҳифа баромад матнӣ аст: ҷадвалро танҳо дар браузери ҳақиқӣ мебинед)
console.trace()Стекро чоп мекунад: «кӣ ин функсияро даъват кард?» — қулай аст, вақте нофаҳмо аст, ки даъват аз куҷо омад
console.error() / console.warn()Паёми сурх / огоҳии зард — онҳоро осон филтр кардан мумкин аст
console.group() / console.groupEnd()Гурӯҳи паёмҳо, ки ҷамъ шуданаш мумкин аст
console.time("t") / console.timeEnd("t")Чен кардани вақт байни ду нуқта
Баромади имзошуда · Ctrl + Enter
const price = 20;
const qty = 3;

console.log(price, qty);           // 20 3 — чӣ чист?
console.log({ price, qty });       // { price: 20, qty: 3 } — фавран фаҳмост
console.log("итого:", price * qty);

Sources — ислоҳи қадам ба қадам ва breakpoint-ҳо

Ба ҷои даҳҳо console.log метавонед барномаро дар сатри даркорӣ қатъ кунед ва атрофро дида бароед. Ҷадвали Sources-ро кушоед, файлро интихоб кунед ва ба рақами сатр дар чап зер кунед: нишони кабуд — breakpoint пайдо мешавад. Акнун дар саҳифа амалеро иҷро кунед (масалан, тугмаро пахш кунед): иҷро пеш аз ин сатр қатъ мешавад.

  1. Атрофро бубинед. Дар тарафи рост дар панели Scope қиматҳои ҳамаи тағйирёбандаҳо дар ин лаҳза намоёнанд. Дар Watch ҳар ифодаро илова кардан мумкин аст, масалан items.length. Дар Call Stack — ҳамон стеки даъватҳо.
  2. Қадам ба қадам равед. F10 (Step over) — сатрро иҷро кардан ва ба навбатӣ гузаштан. F11 (Step into) — ба дохили функсияи даъватшаванда даромадан. Shift+F11 (Step out) — аз функсия баромадан. F8 — то breakpoint-и навбатӣ идома додан.
  3. Интизориро бо воқеият муқоиса кунед. Дар кадом сатр қимат на он тавр шуд, ки шумо меандешидед? Ин ҳамон ҷойи хатост.

Вариантҳои қулай: breakpoint-и шартӣ (зеркунии рост ба рақами сатр → Add conditional breakpoint, масалан i === 3) ва Pause on exceptions (тугмаи бо таваққуф): DevTools худаш дар лаҳзае, ки код хато меандозад, қатъ мешавад.

Оператори debugger

Агар ҷустуҷӯи сатри даркорӣ дар DevTools намехоҳед, дар код оператори debugger;-ро гузоред. Вақте DevTools кушода аст, иҷро маҳз дар ҳамин ҷо, мисли breakpoint, қатъ мешавад. Агар DevTools баста бошад, оператор ҳеҷ кор намекунад.

debugger дар код
function calcTotal(items) {
  let sum = 0;
  for (const item of items) {
    debugger;               // дар ин ҷо барнома қатъ мешавад, агар DevTools кушода бошад
    sum += item.price;
  }
  return sum;
}
debugger ва console.log-ҳои зиёдатиро дар код нагузоред

Пеш аз commit ва pull request онҳоро хориҷ кунед. Ревьюер чунин партовро дар diff мебинад — ва PR-ро барои такмил бармегардонад.

Network — шабака

Ҷадвали Network ҳар дархости саҳифаро нишон медиҳад: HTML, расмҳо, fetch ба API. Онро пеш аз амал дар саҳифа фаъол кунед (ё саҳифаро навсозӣ намоед). Барои дархостҳои fetch филтри Fetch/XHR-ро пахш кунед.

  • Status: 200 — муваффақият, 404 — нишонӣ ёфт нашуд, 500 — сервер вайрон шуд. Сатри сурх — дархост ноком шуд (масалан, интернет нест).
  • Ба дархост зер кунед: Headers (нишонӣ, метод, сарлавҳаҳо), Payload (чӣ фиристодем), Preview / Response (сервер чӣ ҷавоб дод), Timing (чанд интизор шудем).
  • Саволи маъмул «чаро дар саҳифа холӣ аст?»: дар Network менигарем — оё ҷавоб омад, кадом статус, оё дар баданаи ҷавоб хато нест.

Усули ислоҳи хато: шаш қадам

  1. Хаторо такрор кунед: дақиқан чиро пахш кардан ва ворид кардан лозим, то он ҳар дафъа такрор шавад.
  2. Танг кунед: ҳар чизи зиёдатиро хориҷ кунед, то коди ҳадди ақал бо хато бимонад.
  3. Паёми хато ва стекро бихонед. Аксар вақт ҷавоб аллакай дар он навишта шудааст.
  4. Фарзияро пешниҳод кунед: «қимат дар ин ҷо undefined шуд, зеро…».
  5. Фарзияро бо як санҷиш месанҷед: console.log ё breakpoint. Як адад, на панҷто якбора.
  6. Ислоҳ кунед ва аз нав санҷед: хато рафт, боқӣ вайрон нашуд? Дар commit нависед, ки сабаб чӣ буд.

Амал: мустаҳкам мекунем

ВАЗИФАИ 7.1

Стеки даъватҳоро мехонем

миёна

Дар пеши шумо матни стеки хато (мисли сатри сурхи консол). parseFrame(line)-ро анҷом диҳед: аз сатри намуди at calcTotal (cart.js:12:23) номи функсия, файл ва рақами сатрро бигиред. Бояд объекти { fn, file, line } баргардонед. Боқӣ аллакай омода аст: код сатрҳои бо at оғозшавандаро интихоб мекунад ва натиҷаро чоп менамояд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const stack = `TypeError: Cannot read properties of undefined (reading 'price')
    at calcTotal (cart.js:12:23)
    at renderCart (ui.js:48:15)
    at HTMLButtonElement.<anonymous> (main.js:7:3)`;

function parseFrame(line) {
  // регулярка: at (.+?) \((.+?):(\d+):(\d+)\)
  return { fn: "?", file: "?", line: 0 };
}

const frames = stack.split("\n").filter((l) => l.trim().startsWith("at ")).map(parseFrame);
console.log("ошибка в:", frames[0].fn, frames[0].file, frames[0].line);
console.log("глубина стека:", frames.length);
console.log("всё началось в файле:", frames[frames.length - 1].file);
Натиҷаи интизоршаванда
ошибка в: calcTotal cart.js 12
глубина стека: 3
всё началось в файле: main.js
Ишора 1

Регулярка бо гурӯҳҳои қавсӣ: /at (.+?) \((.+?):(\d+):(\d+)\)/. Гурӯҳҳои 1, 2, 3 — функсия, файл, сатр. Қавсҳои гирди ҷой дар стекро бояд экранировать кард: \( ва \).

Ишора 2

const m = line.match(…); return { fn: m[1], file: m[2], line: Number(m[3]) };

ВАЗИФАИ 7.2

Лог ба ҷои breakpoint

осон

Бе DevTools «ба дохили» давра нигоҳ кардан тавассути console.log мумкин аст. Ба давра як сатри логро тавре илова кунед, ки пас аз коркарди ҳар рақам n=…, sum=… (қиматҳо дар ин лаҳза) чоп шавад. Қимати ниҳоиро функсия худаш чоп мекунад.

Таҳрир кардан мумкин аст · Ctrl + Enter
function sumPositive(numbers) {
  let sum = 0;
  for (const n of numbers) {
    if (n > 0) sum += n;
    // дар ин ҷо console.log илова кунед
  }
  return sum;
}

console.log(sumPositive([5, -3, 10, 0, 7]));
Натиҷаи интизоршаванда
n=5, sum=5
n=-3, sum=5
n=10, sum=15
n=0, sum=15
n=7, sum=22
22
Ишора 1

Логро пас аз сатри бо if мегузорем, вале дар дохили давра: ҳамин тавр он маҷмӯи аллакай навшударо мебинад.

Ишора 2

console.log(`n=${n}, sum=${sum}`);

Муфассалтар хонед: Chrome DevTools — ислоҳи JavaScript, learn.javascript.ru — ислоҳи хато дар браузер.

08 Хаторо ёбед ва ислоҳ кунед

Қисми зиёди хатоҳо — маҷмӯи хурди хатоҳои «классикӣ» мебошад. Агар донед, ки онҳо чӣ гуна менамоянд, ҷустуҷӯ чандин маротиба тезтар мешавад. Инак харита:

Навъи хатоЧӣ гуна менамоядДар куҷо ҷустан
Off-by-one (хато ба як)NaN ё undefined дар охири рӯйхат; унсури якум ё охирин гузашта шудаастСарҳадҳои давраҳо: < бар муқобили <=, оғоз аз 0 ё 1, slice
Муқоисаи нодурустШарт дар қиматҳои «нодуруст» кор мекунад; мураттабсозӣ бо тартиби аҷиб= ба ҷои ===, сатрҳо ба ҷои рақамҳо, > ба ҷои >=, sort() бе функсия
Тартиби asyncДар баромад «Promise», паёмҳо бо тартиби нодуруст меоянд, маълумот «ҳанӯз нарасидааст»await-и гузашта, forEach бо async, интизории натиҷа берун аз then
МутатсияМаълумот «худ аз худ» дар ҷои дигари барнома тағйир меёбадsort, push, splice дар массиви аслӣ; нусха, ки дар асл истинод аст; fill([])
Соҳаи намоӣДар колбэкҳои давра ҳамеша як қиматvar ба ҷои let
Қоида барои вазифаҳои зер

Аввал кодро ба ҳамин ҳол иҷро кунед ва бубинед, ки чӣ чоп мекунад — бо натиҷаи интизоршаванда муқоиса кунед. Ишораи «аломат»-ро дар шарт бихонед. Фарзия пешниҳод кунед, як ҷойро ислоҳ намоед ва боз иҷро кунед. Ишораҳоро танҳо пас аз кӯшиши худатон кушоед.

Мисоли таҳлил

Инак хато ва ислоҳи он «бо суханон». Иҷро кунед:

Чӣ чоп мешавад? · Ctrl + Enter
const scores = [70, 85, 90];

function average(list) {
  let sum = 0;
  for (let i = 0; i < list.length; i++) {
    sum += list[i];
  }
  return sum / list.length + 1;
}

console.log(average(scores));

Тақрибан 81.67 интизор будем, тақрибан 82.67 гирифтем. Фарзия: чизе зиёдатӣ илова мешавад. Санҷиш: console.log(sum / list.length) пеш аз return — ин 81.67 аст. Яъне, гунаҳкор сатри охирин аст: + 1 зиёдатӣ аст. Мебинед: мо «ба тахмин ислоҳ накардем», балки ҷустуҷӯро бо санҷиш танг кардем.

Off-by-one ва муқоисаҳои нодуруст

Амал: мустаҳкам мекунем

ВАЗИФАИ 8.1

Хато: қадами зиёдатии давра

осон

Сабад бояд маблағи 445-ро баргардонад, аммо чизи аҷиб чоп мешавад. Сабабро ёбед (ишора дар консол: кадом унсур нарх надорад?) ва як сатрро ислоҳ кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const prices = [120, 80, 45, 200];

function total(list) {
  let sum = 0;
  for (let i = 0; i <= list.length; i++) {
    sum += list[i];
  }
  return sum;
}

console.log(total(prices));
Натиҷаи интизоршаванда
445
Ишора 1

Дар массиви 4 унсура индексҳо аз 0 то 3 мераванд. Дар давраи охирини давра i ба 4 баробар аст, ва list[4] — ин undefined аст. Ҷамъи рақам бо undefined NaN медиҳад.

Ишора 2

<=-ро ба < иваз кунед. Қоида: «то дарозӣ, бе худи он».

ВАЗИФАИ 8.2

Хато: саҳифаҳо на аз он мавқеъ

миёна

Рӯйхати донишҷӯёни курс ба ҳар саҳифа 3 нафар нишон дода мешавад. Саҳифаи якум бояд аз «Алишер» оғоз шавад, аммо чизи дигар чоп мешавад. getPage-ро ислоҳ кунед, то саҳифаҳои 1, 2, 3 гурӯҳҳои даркориро чоп кунанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const students = ["Алишер", "Бахтиёр", "Дилшод", "Зарина", "Мадина", "Нигина", "Рустам"];

function getPage(list, page, size) {
  const start = page * size;
  return list.slice(start, start + size);
}

console.log(getPage(students, 1, 3).join(", "));
console.log(getPage(students, 2, 3).join(", "));
console.log(getPage(students, 3, 3).join(", "));
Натиҷаи интизоршаванда
Алишер, Бахтиёр, Дилшод
Зарина, Мадина, Нигина
Рустам
Ишора 1

Одамон саҳифаҳоро аз 1 мешуморанд, массив — аз 0. Саҳифаи якум аз индекси 0 оғоз мешавад, дуюм — аз индекси 3.

Ишора 2

Оғози саҳифа: (page - 1) * size.

ВАЗИФАИ 8.3

Хато: се беҳтарин аз рӯи хол

осон

Функсия бояд се холи беҳтаринро бо тартиби камшаванда баргардонад: 100, 85, 42. Аммо мураттабсозӣ тартиби аҷиб медиҳад. Чаро sort() бе аргумент бо рақамҳо хато мекунад? Ислоҳ кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
function top3(scores) {
  const sorted = [...scores].sort();
  return sorted.reverse().slice(0, 3);
}

console.log(top3([85, 9, 100, 42, 7]).join(", "));
Натиҷаи интизоршаванда
100, 85, 42
Ишора 1

Бе функсияи муқоиса sort рақамҳоро ба сатр табдил медиҳад ва аз рӯи ҳарфҳо мураттаб мекунад: «100» пеш аз «42» меояд, ва «9» — баъд аз «85».

Ишора 2

(a, b) => b - a-ро гузаронед — ҳамин тавр тартиби камшаванда мешавад, ва reverse() дигар лозим нест.

ВАЗИФАИ 8.4

Хато: сатрҳо ба ҷои рақамҳо

миёна

Қиматҳо аз майдонҳои шакл ҳамеша бо сатр меоянд. cheaper("9", "100") бояд 9-ро баргардонад, cheaper("45", "5") бошад — 5. Дар label низ хато ҳаст: нархи 0 бояд ҳамчун «бесплатно» чоп шавад. Ду хаторо ёбед.

Таҳрир кардан мумкин аст · Ctrl + Enter
function cheaper(a, b) {
  return a < b ? a : b;
}

function label(price) {
  if (price = 0) return "бесплатно";
  return price + " сомони";
}

console.log(cheaper("9", "100"));
console.log(cheaper("45", "5"));
console.log(label(35));
console.log(label(0));
Натиҷаи интизоршаванда
9
5
35 сомони
бесплатно
Ишора 1

Ду сатр «аз рӯи алифбо» муқоиса мешаванд: "9" < "100" дурӯғ аст, зеро «9» баъд аз «1» меояд. Қиматҳоро табдил диҳед: Number(a) < Number(b).

Ишора 2

Дар label як аломати = — ин таъинот аст: нарх ҳангоми ҳар даъват 0 мешавад. Муқоисаи === лозим аст.

Тартиби коди ғайримуттаҳид

ВАЗИФАИ 8.5

Хато: forEach ва await

миёна

Се таом пай дар пай пухта мешаванд, ва «Все блюда готовы!» бояд пас аз се сатри «… готов» пайдо шавад. Ҳоло сатри ниҳоӣ аввал чоп мешавад. Кодро ислоҳ кунед: forEach колбэкҳои async-ро интизор шуда наметавонад. Онро бо давраи интизоршаванда иваз кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function cook(dish) {
  await delay(10);
  return `${dish} готов`;
}

const dishes = ["плов", "самбуса", "лагман"];

async function main() {
  dishes.forEach(async (d) => {
    console.log(await cook(d));
  });
  console.log("Все блюда готовы!");
}

main();
Натиҷаи интизоршаванда
плов готов
самбуса готов
лагман готов
Все блюда готовы!
Ишора 1

forEach колбэкҳоро оғоз мекунад ва натиҷаи онҳоро интизор намешавад, барои ҳамин main фавран ба пеш меравад. Давраи for…of дар дохили функсияи async ҳар await-ро интизор мешавад.

Ишора 2

for (const d of dishes) { console.log(await cook(d)); }

ВАЗИФАИ 8.6

Хато: await-ро фаромӯш кардем

миёна

Бояд Итого: 80 (30 + 50) чоп шавад. Ба ҷои он матн бо «Promise» ҳосил мешавад. Функсияи getPrice ғайримуттаҳид аст. showTotal-ро ислоҳ кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const getPrice = (id) => new Promise((resolve) => setTimeout(() => resolve(id * 10), 20));

async function showTotal() {
  const a = getPrice(3);
  const b = getPrice(5);
  console.log("Итого:", a + b);
}

showTotal();
Натиҷаи интизоршаванда
Итого: 80
Ишора 1

Бе await дар тағйирёбанда худи промис меистад, на рақам. Ҷамъи ду промис онҳоро ба сатр табдил медиҳад.

Ишора 2

await-ро пеш аз ҳар getPrice(...) гузоред. Ё Promise.all-ро истифода баред, агар мехоҳед ҳарду нархро якбора гиред.

ВАЗИФАИ 8.7

Хато: таймерҳо дар давра

осон

Се таймер бояд таймер 1, таймер 2, таймер 3-ро чоп кунанд. Аммо онҳо як рақамро чоп мекунанд. Эълони тағйирёбандаи давраро ислоҳ кунед — як калимаро тағйир додан лозим аст.

Таҳрир кардан мумкин аст · Ctrl + Enter
for (var i = 1; i <= 3; i++) {
  setTimeout(() => console.log("таймер", i), i * 10);
}
Натиҷаи интизоршаванда
таймер 1
таймер 2
таймер 3
Ишора 1

Тағйирёбандаи var барои тамоми давра як аст, ва то лаҳзаи кори таймерҳо давра аллакай тамом шудааст (i 4 шуд).

Ишора 2

Бо let дар ҳар давр тағйирёбандаи худии i эҷод мешавад.

Мутатсия ва истинодҳои умумӣ

ВАЗИФАИ 8.8

Хато: sort сабадро вайрон мекунад

миёна

Функсияи cheapest бояд танҳо арзонтарин маҳсулотро ёбад, ва тартибро дар сабад тағйир додан мумкин нест: пас аз даъват дар сабад ҳамоно Чай, Нон, Плов аст. Ҳоло тартиб иваз мешавад. Як сатрро ислоҳ кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const cart = [
  { name: "Чай", price: 15 },
  { name: "Нон", price: 4 },
  { name: "Плов", price: 35 },
];

function cheapest(items) {
  return items.sort((a, b) => a.price - b.price)[0];
}

console.log(cheapest(cart).name);
console.log(cart.map((i) => i.name).join(", "));
Натиҷаи интизоршаванда
Нон
Чай, Нон, Плов
Ишора 1

sort худи массивро тағйир медиҳад (мутатсия мекунад), на массиви нав бармегардонад. Дар items истинод ба ҳамон массиве меистад, ки cart.

Ишора 2

Нусхаро мураттаб кунед: [...items].sort(…).

ВАЗИФАИ 8.9

Хато: истиноди умумӣ

душвор

Ду хато бо як сабаб — якчанд «тағйирёбанда» ба як массиви ягона менигаранд. (1) Тӯри аз се сатр: пас аз илова кардани «A1» ба аввалӣ ва «C1» ба сеюм дарозиҳо бояд 1,0,1 бошанд. (2) Нусхаи ҳолат copy набояд ба state таъсир расонад: тегҳо дар он бояд js бимонанд. Ҳарду қисмро ислоҳ кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const grid = new Array(3).fill([]);
grid[0].push("A1");
grid[2].push("C1");
console.log(grid.map((row) => row.length).join(","));

const state = { user: "Али", tags: ["js"] };
const copy = { ...state };
copy.tags.push("git");
console.log(state.tags.join(","));
Натиҷаи интизоршаванда
1,0,1
js
Ишора 1

new Array(3).fill([]) дар се ячейка як ва ҳамон массивро мегузорад. Се массиви гуногунро эҷод кардан лозим аст: Array.from({ length: 3 }, () => []) — функсия барои ҳар ячейка аз нав даъват мешавад.

Ишора 2

Spread-и { ...state } танҳо сатҳи болоро нусха мекунад, ва tags умумӣ мемонад. Онро ҳам нусха кунед: { ...state, tags: [...state.tags] }.

Муфассалтар хонед: learn.javascript.ru — мураттабсозии sort(fn), MDN — Array.prototype.sort.

09 ЗС: commit-ҳо, тавсифи PR ва савол дар бораи хато

Блоки ЗС-и дарс

ЗС ду корро хуб мекунад: commit-ро аз рӯи diff-и тайёр таҳия мекунад ва фарзияҳоро дар бораи хато пешниҳод менамояд. Аммо масъулият бар дӯши шумост: diff-ро худатон хондед, ва фарзияҳоро бо иҷро месанҷед, на бо суханони ӯ бовар мекунед. Қоидаҳои курс: аввал 15 дақиқа худатон кӯшиш кунед; кодеро, ки сатр ба сатр шарҳ дода наметавонед, нагузоред.

1. Commit ва тавсифи PR аз рӯи diff

Баромади git diff --staged-ро нусха кунед ва аз ЗС хоҳиш намоед, ки паёмро таҳия кунад. Бе контекст ӯ «update files» менависад:

commit бисоз
Инак diff: [баромади git diff --staged-ро гузоред]. Паёми commit-ро бо услуби Conventional Commits нависед (навъ: мавзӯъ, то 50 аломат, англисӣ, шакли амр, бе нуқта) ва тавсифи кӯтоҳи PR: чӣ анҷом шуд ва барои чӣ. Формати ҷавоб: аввал як сатри commit, баъд PR аз се бахш «Чӣ анҷом шуд», «Барои чӣ», «Чӣ тавр санҷидан». Он чизеро, ки дар diff нест, наандеш.

Дар ҷавоб чиро санҷидан лозим:

  • Навъ ба ҳақиқат мувофиқ аст: feat ё fix, на «ба эҳтимол».
  • Дар матн он чизе нест, ки дар diff нест (ЗС «самаранокиро беҳтар кардам» илова карданро дӯст медорад, дар ҳоле ки шумо ба он даст назадаед).
  • Паём мушаххас аст: онро аз санҷишҳои вазифаҳои 5.3 ва 9.2 гузаронед.
  • Агар дар diff ду андешаи гуногун бошад, беҳтар аст ба ду commit тақсим кунед, на ин ки аз ЗС хоҳиш намоед «зебо муттаҳид кунад».

2. Хато: ба ҷои «кор намекунад» — мисоли ҳадди ақали такроршаванда

дар назди ман сабад кор намекунад, ислоҳ кун
Хато: интизорам [чӣ], мегирам [чӣ]. Коди ҳадди ақал: [код]. Хато дар консол: [матн]. 3 фарзияи сабабро аз рӯи эҳтимол деҳ.

Мисоли ҳадди ақали такроршаванда (minimal reproducible example, MRE) — кӯтоҳтарин коде, ки хато дар он ҳанӯз намоён аст. Чӣ тавр онро ба даст овардан:

  1. Ҷойи мушкилдорро ба файли алоҳида ё ба консол дар ҳамин саҳифа нусха кунед.
  2. Ҳар чизи зиёдатиро порча-порча хориҷ кунед: услубҳо, функсияҳои ҳамсоя, майдонҳои зиёдатӣ. Пас аз ҳар қадам санҷед, ки хато ҷой дорад.
  3. Маълумоти оддиро ҷойгузин кунед ба ҷои дархостҳо ба сервер: массиви 2–3 унсур.
  4. Сабт кунед натиҷаи интизоршаванда ва воқеӣ, ҳамчунин матни хаторо калима ба калима.

Бонус: то шумо кодро содда мекардед, аксар вақт худатон хаторо меёбед. Ин муқаррарӣ ва хеле қиматнок аст. Усул «мурғобича» (rubber duck) ном дорад: мушкилро бо овози баланд чунон возеҳ шарҳ додан, ки ҷавоб худаш пайдо шавад.

Бо фарзияҳо чӣ тавр кор накардан лозим

ЗС се фарзия дод. Онҳоро пай дар пай, аз эҳтимолтарин месанҷед, ва пас аз ҳар кадом — иҷро. Агар якбора се «ислоҳ»-ро гузоред, намефаҳмед, кадомаш кор кард, ва ёд намегиред, ки ин хаторо дафъаи оянда пай бурдан лозим аст. Ҳамчунин ЗС гоҳо методҳои вуҷуднадоштаро бо боварӣ маслиҳат медиҳад: номи ношиносро дар MDN санҷед.

Ба варақаи кӯмакии промптҳо ду сатри ин дарсро илова кунед — «Commit-ҳо ва pull request» ва «Хато ба ҷои „кор намекунад“». Ҳарду маҳоратро бо код машқ мекунем.

Амал: мустаҳкам мекунем

ВАЗИФАИ 9.1

Промпт бо мисоли ҳадди ақал

миёна

bugPrompt(bug)-ро анҷом диҳед: он аз тавсифи хато промпти тайёр барои ЗС-ро аз рӯи формати курс ҷамъ мекунад — чор сатр (код аз сатри дуюм меояд). Агар дар консол хато набошад (bug.error муайян нашудааст), дар сатри хато нет менависем.

Таҳрир кардан мумкин аст · Ctrl + Enter
function bugPrompt(bug) {
  // сатрро аз сатрҳо баргардонед:
  // Баг: ожидаю «…», получаю «…».
  // Минимальный код:
  // <код>
  // Ошибка в консоли: …
  // Дай 3 гипотезы причины по вероятности.
}

console.log(bugPrompt({
  expected: "45",
  actual: "NaN",
  code: "const prices = [20, 25];\nlet sum = 0;\nfor (let i = 0; i <= prices.length; i++) sum += prices[i];\nconsole.log(sum);",
}));
console.log("---");
console.log(bugPrompt({ expected: "Привет, Али", actual: "Привет, undefined", code: "greet({ nam: 'Али' })", error: "ошибки нет" }));
Натиҷаи интизоршаванда
Баг: ожидаю «45», получаю «NaN».
Минимальный код:
const prices = [20, 25];
let sum = 0;
for (let i = 0; i <= prices.length; i++) sum += prices[i];
console.log(sum);
Ошибка в консоли: нет
Дай 3 гипотезы причины по вероятности.
---
Баг: ожидаю «Привет, Али», получаю «Привет, undefined».
Минимальный код:
greet({ nam: 'Али' })
Ошибка в консоли: ошибки нет
Дай 3 гипотезы причины по вероятности.
Ишора 1

Массиви панҷ сатрро ҷамъ кунед (дуюм — «Минимальный код:», сеюм — худи код) ва тавассути join("\n") муттаҳид намоед.

Ишора 2

Барои майдони ихтиёрӣ оператори ?? мувофиқ аст: bug.error ?? "нет" «нет»-ро бармегардонад, агар хато муайян нашуда бошад.

ВАЗИФАИ 9.2

Паёмҳои норавшан аз ЗС

осон

ЗС гоҳо commit-ҳои мисли «update» ё «fix: bug» пешниҳод мекунад. isVague(msg)-ро анҷом диҳед: паём норавшан аст, агар пас аз навъ ва дунуқта (агар бошанд) дар мавзӯъ камтар аз ду калима бошад ё калимае аз рӯйхати generic ҳаст.

Таҳрир кардан мумкин аст · Ctrl + Enter
const generic = ["stuff", "things", "changes", "bug", "bugs", "code", "files"];

function isVague(msg) {
  // 1) навъро хориҷ кунед: msg.replace(/^\w+(\([^)]*\))?:\s*/, "")
  // 2) калимаҳоро ҳисоб кунед ва generic-ро санҷед
  return false;
}

const ai = [
  "feat: add order filters",
  "update",
  "fix: bug",
  "chore: bump deps",
  "fix: update stuff",
  "docs: describe setup steps",
];
ai.forEach((m) => console.log(`${m} -> ${isVague(m) ? "vague" : "ok"}`));
Натиҷаи интизоршаванда
feat: add order filters -> ok
update -> vague
fix: bug -> vague
chore: bump deps -> ok
fix: update stuff -> vague
docs: describe setup steps -> ok
Ишора 1

Мавзӯъро чунин мегирем: msg.replace(/^\w+(\([^)]*\))?:\s*/, ""). Барои «update» (бе дунуқта) ин ивазкунӣ чизеро тағйир намедиҳад — ва ин дуруст аст.

Ишора 2

const words = subject.split(/\s+/).filter(Boolean); return words.length < 2 || words.some((w) => generic.includes(w));

10 Амал дар дарс: кор дар ҷуфтҳо

Ҳадаф: давраи пурраи кори гурӯҳиро гузаштан — шоха → PR → ревью → merge — ва се хатои ҷойгузоршударо дар лоиҳаи ҳамкор ёфтан. Дар ҷуфтҳо кор мекунем: донишҷӯи А ва донишҷӯи Б. Нишондиҳанда оид ба вақт барои 90 дақиқа: омодагӣ 10, давра бо PR 35, низоъ 10, шикори хатоҳо 25, таҳлил 10.

Қисми 1. Репозиторий ва нақшҳо (10 дақиқа)

  1. Донишҷӯи А дар GitHub репозиторийи pair-shop-ро (бо README) эҷод мекунад ва дар Settings → Collaborators Б-ро даъват менамояд. Б даъватро дар почта қабул мекунад.
  2. Ҳарду репозиторийро clone мекунанд:
    Терминал
    git clone https://github.com/ЛОГИН_А/pair-shop.git
    cd pair-shop
  3. Мувофиқа кунед, ки шумо чӣ мекунед: А — функсияи calcTotal ва discount-ро, Б — намоиши сабадро. Ҳар кас дар файли худ кор мекунад.

Қисми 2. Шоха, commit-ҳо, PR (35 дақиқа)

  1. Ҳар кас шохаи худро эҷод мекунад: А — feature/calc-total, Б — feature/cart-view.
  2. Дар шоха ҳадди аққал се commit созед, ҳар кадом — як андеша, паёмҳо аз рӯи Conventional Commits:
    Терминал
    git add cart.js
    git commit -m "feat: add calcTotal"
    git add cart.js
    git commit -m "feat: add 10 percent discount"
    git add README.md
    git commit -m "docs: describe cart module"
  3. Шохаро фиристед ва pull request-ро дар GitHub кушоед. Дар тавсиф — се бахш: «Что сделано», «Зачем», «Как проверить». Ревьюер — ҳамкор.
    Терминал
    git push -u origin feature/calc-total
  4. Ревью: PR-и ҳамкорро аз рӯи чек-листи қисми 3 бихонед. Ҳадди аққал ду шарҳ ба сатрҳои мушаххас гузоред: яке бо савол, яке бо пешниҳод. Коди шохаро иҷро кунед.
  5. Муаллиф ба шарҳҳо ҷавоб медиҳад ва кодро бо commit-ҳои нав дар ҳамон шоха ислоҳ мекунад. Ревьюер Approve-ро пахш мекунад.
  6. Муаллиф Merge pull request-ро пахш мекунад. Баъд ҳарду main-ро навсозӣ мекунанд:
    Терминал
    git switch main
    git pull
    git branch -d feature/calc-total

Қисми 3. Низои қасдӣ (10 дақиқа)

  1. Ҳарду шохаҳои навро аз main-и тоза эҷод мекунанд ва як сатри ягонаро дар README (масалан, сарлавҳа) — гуногун тағйир медиҳанд.
  2. А PR-и худро аввал медарорад. Б дар шохаи худ git pull origin main мекунад — низоъ пайдо мешавад.
  3. Б низоъро якҷоя бо А ҳал мекунад (дар бораи матни ниҳоӣ мувофиқа мекунанд), git add, git commit, git push мекунад. PR-и Б навсозӣ шуд ва дигар низоъ надорад.

Қисми 4. Се хатои ҷойгузоршуда (25 дақиқа)

Ҳар кас пинҳонӣ ба лоиҳаи худ се хатои навъҳои гуногун (аз харитаи қисми 8) ворид мекунад ва ба шохаи нави bugs/номҳо push мекунад. Ҳамкор онҳоро бо усули қисми 7 ислоҳ мекунад: такрор кардан, хаторо хондан, breakpoint ё логро гузоштан, ислоҳро бо commit-и алоҳидаи fix: … бо сабаб анҷом додан. Версияи тамринии чунин лоиҳа — вазифаи зер: онро ҳамчун ревьюи коди бегона бихонед.

Амал: мустаҳкам мекунем

ВАЗИФАИ 10.1

Се хато дар лоиҳаи бегона

душвор

Ҳамкор сабади мағозаи MakeFirstStep-ро навишт ва аз шумо ревью металабад. Тахфифи 10% аз 500 сомонӣ аз ҷумла амал мекунад, интиқол 15 сомонӣ аст ва танҳо ба нусхаи сабад илова мешавад. Дар код се хатои навъҳои гуногун пинҳон шудааст. Онҳоро ёбед, ислоҳ кунед ва дар шарҳ болои ҳар ислоҳ сабабро бо як сатр нависед. Рақамҳои интизоршаванда дар зер — ҷавобҳои дурустанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const products = [
  { id: 1, name: "Курс Frontend", price: 450 },
  { id: 2, name: "Футболка MakeFirstStep", price: 80 },
  { id: 3, name: "Блокнот", price: 20 },
];

function calcTotal(items) {
  let sum = 0;
  for (let i = 1; i < items.length; i++) {
    sum += items[i].price;
  }
  return sum;
}

function priceWithDiscount(items) {
  const total = calcTotal(items);
  return total > 500 ? total * 0.9 : total;
}

function withDelivery(items) {
  items.push({ id: 0, name: "Доставка", price: 15 });
  return items;
}

const cart = [products[0], products[1], products[2]];
const cart500 = [products[0], { id: 9, name: "Стикеры", price: 50 }];

console.log(calcTotal(cart));
console.log(priceWithDiscount(cart));
console.log(priceWithDiscount(cart500));
console.log(calcTotal(withDelivery(cart)));
console.log(cart.length);
Натиҷаи интизоршаванда
550
495
450
565
3
Ишора 1

Се хато: (1) давра аз i = 1 оғоз мешавад ва маҳсулоти якумро мегузарад; (2) дар priceWithDiscount ба ҷои >= аломати > меистад — дақиқан 500 тахфиф намегирад; (3) push сабади аслиро тағйир медиҳад.

Ишора 2

Худро бо рақамҳо санҷед: маҷмӯи сабад 550, бо тахфиф 495, сабад барои дақиқан 500 — 450, бо интиқол 565, пас аз ин дар сабади аслӣ ҳамоно 3 маҳсулот.

Чек-листи ҷуфт дар охир

Дар репозиторий: се ва зиёда commit бо паёмҳои бомаъно, як PR-и даровардашуда бо тавсиф ва шарҳҳои ревью, як низои ҳалшуда, се commit-и fix: бо тавсифи сабаб. Агар чизе нест, дар хона анҷом диҳед: ин ба вазифаи хонагӣ дохил мешавад.

11 Вазифаи хонагӣ

Репозиторийро тавре ороиш диҳед, ки нишон додани он ба корфармо шармовар набошад, ва се хаторо бо таҳлили сабаб ёбед. Метавонед лоиҳаи яке аз дарсҳои гузашта (масалан, лоиҳаи хурди №1) ё лоиҳаи ҷуфтро гиред.

Қисми 1. Репозиторий

  1. Репозиторий дар GitHub оммавӣ аст, номи бомаъно ва тавсиф дорад.
  2. Дар таърих ҳадди аққал 10 commit-и бомаъно аз рӯи Conventional Commits: ҳар кадом — як андеша, ягон «update», «fix», «final» нест. Таърихро бо фармони git log --oneline санҷед.
  3. Ҳадди аққал як шоха ба ҷуз main ва pull request-и даровардашуда бо тавсиф (чӣ анҷом шуд, барои чӣ, чӣ тавр санҷидан) ҳаст. Агар танҳо кор кунед, PR-ро аз шохаи худ ба main кушоед ва diff-ро мисли ревьюер худатон бинед.
  4. Дар реша .gitignore ҳаст (ҳадди аққал: node_modules/, .env, .DS_Store, *.log) ва дар репозиторий файлҳои партов нестанд.
  5. Дар реша README.md аз рӯи қолаби зер ҳаст.
README.md — қолаб
# Название проекта

Одно предложение: что это и для кого.

## Что умеет
- функция 1
- функция 2

## Как запустить
1. Склонируйте репозиторий
2. Откройте index.html в браузере (или npm install && npm run dev)

## Скриншот
![скриншот](screenshot.png)

## Что я использовал
HTML, CSS, JavaScript, Git/GitHub

## Автор
Имя, группа курса

Қисми 2. Се хато

  1. Дар лоиҳаи худ ё бегона се хатои воқеии навъҳои гуногун-ро ёбед: масалан, off-by-one, муқоисаи нодуруст, мутатсия, хатои тартиби коди ғайримуттаҳид. Агар наёфтед — онҳоро худатон ба нусхаи лоиҳа ворид кунед ва аз шахси шинос хоҳиш кунед, ки ёбад.
  2. Барои ҳар хато commit-и алоҳидаи fix: … бо ислоҳ созед.
  3. Дар репозиторий файли BUGS.md-ро эҷод кунед ва ҳар хаторо аз рӯи қолаби зер тасвир намоед.
  4. Яке аз се таҳлилро бо промпт ба ЗС дар формати «мисоли ҳадди ақали такроршаванда» ҳамроҳ кунед ва ҷавоби ЗС-ро якҷоя бо баҳои худатон ба файл гузоред: чӣ бо иҷро тасдиқ шуд, ва чӣ нодуруст баромад.
BUGS.md — қолаб
## Баг 1: итоговая сумма корзины меньше правильной

- **Симптом:** ожидал 550, получил 530.
- **Как воспроизвести:** добавить три товара и посмотреть сумму.
- **Причина:** цикл начинался с i = 1 и пропускал первый товар (off-by-one).
- **Как нашёл:** поставил breakpoint в calcTotal, увидел, что цикл идёт с индекса 1.
- **Исправление:** коммит `a1b2c3d` — `fix: count first item in total`.
- **Как не допустить снова:** проверять границы цикла на массиве из 1 и 0 элементов.

Чӣ супурдан лозим

  • Истинод ба репозиторийи оммавии GitHub.
  • Истинод ба pull request-и даровардашуда.
  • Дар репозиторий: README.md, .gitignore, BUGS.md.

Чӣ тавр баҳо медиҳем (0–10)

ЧӣХолҳо
10+ commit-и бомаъно аз рӯи формат, «update» ва «fix» бе шарҳ нест3
Шоха, pull request бо тавсиф, ба main дароварда шудааст2
.gitignore ва README.md аз рӯи қолаб ороиш дода шудаанд, файлҳои партов нестанд2
Се хатои навъҳои гуногун бо commit-ҳои алоҳидаи fix:2
Дар BUGS.md сабаб ва чӣ тавр ёфтан фаҳмост; як таҳлил бо ЗС ва санҷиш бо иҷро1
Пеш аз супурдан

Репозиторийро дар тирезаи хусусии браузер кушоед: оё ҳама чиз бе вуруд намоён аст? git log --oneline-ро санҷед: паёмҳоро мисли шахси ношинос бихонед. Агар ЗС бо код ё таҳлил кумак кард, шумо бояд дар ҳимоя ҳар сатрро шарҳ дода тавонед.

✓ Варақаи кӯмакии дарс

  • Се минтақа: папкаи корӣ → индекс (git add) → репозиторий (git commit); GitHub — нусха дар шабака (git push / git pull). Пеш аз add ҳамеша git status.
  • Шоха — хатчӯб ба commit. git switch -c feature/ном, кор, push. Ба main бевосита наменависем.
  • Давра дар ҷуфт: шоха → commit-ҳо → push → pull request → ревью (шарҳҳо, approve) → merge → хориҷ кардани шоха.
  • Низоъ: маркерҳои <<<<<<< / ======= / >>>>>>>. Натиҷаро интихоб кунед, маркерҳоро хориҷ намоед, иҷро кунед, add + commit. Ошуфтед — git merge --abort.
  • .gitignore: node_modules/, dist/, .env, *.log, .DS_Store. Сирре, ки ба таърих афтод, ошкоршуда ҳисоб мешавад: калидро иваз мекунем.
  • Commit: навъ(соҳа): чӣ анҷом шуд — feat, fix, docs, style, refactor, test, chore. То 50 аломат, ҳарфҳои хурд, бе нуқта, як андеша.
  • try / catch / finally: catch хаторо мегирад, finally ҳамеша иҷро мешавад. throw new Error("матн") хатои худиро мепартояд. catch-и холӣ — бад; хатоҳои бегонаро ба пеш мегузаронем.
  • Навъҳои хатоҳо: TypeError (на он қимат, null.x), ReferenceError (тағйирёбанда нест), SyntaxError (синтаксис / JSON-и бад), RangeError (берун аз доира).
  • Стек: аз боло ба поён мехонем: дар куҷо афтод, кӣ даъват кард. DevTools: Console (log, table, trace), Sources (breakpoint аз рӯи рақами сатр, F10 / F11 / F8, Watch, Scope), debugger;, Network (статус, ҷавоб, вақт).
  • Хатоҳои классикӣ: off-by-one (< / <=, индексҳо аз 0), муқоиса (===, сатрҳо бар муқобили рақамҳо, sort(fn)), async (await, for…of ба ҷои forEach), мутатсия ([...arr], Array.from), var дар давра.
  • Усули ислоҳ: такрор кардан → танг кардан → хаторо хондан → фарзия → як санҷиш → ислоҳ ва аз нав санҷидан.
  • Бо ЗС: commit ва PR — аз рӯи diff-и шумо, матнро месанҷем. Хато — мисоли ҳадди ақал + интизорӣ/воқеият + хатои калима ба калима + «3 фарзия»; пай дар пай бо иҷро месанҷем.