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

Дарси 11 · DOM ва интерфейс

Ҳолати барнома ва localStorage

Шумо рӯйхати корҳо навиштед, ҷадвали браузерро бастед — ва ҳама чиз гум шуд. Барои корбар ин фалокат аст: ӯ даҳ дақиқа сарф кард, аммо сайт ҳама чизро фаромӯш кард. Дар барномаҳои воқеӣ сабади мағозаи интернетӣ, мавзӯи торик ва дарси нохондашуда аз нав бор шудани саҳифа мегузаранд. Имрӯз шумо ҳамаи маълумоти барномаро дар як ҷо нигоҳ доштан, экранро аз рӯи ин маълумот кашидан ва онро дар браузер захира кардан меомӯзед.

  • Ҳолати барномаро бо як объект тавсиф кардан ва аз маълумоти «ҳосилшуда» фарқ кардан
  • Функсияи render(state)-ро навиштан, ки экранро танҳо аз рӯи ҳолат мекашад
  • Ҳолатро тавассути функсияҳои хурди амал тағйир додан: илова кардан, қайд кардан, нест кардан
  • Маълумотро тавассути localStorage якҷоя бо JSON.stringify ва JSON.parse захира кардан ва хондан
  • Вақте маълумот нест, вайрон ё кӯҳна аст, афтода намондан
  • Аз ЗС хоҳиш кардан, ки сохтори ҳолатро пешниҳод кунад ва онро бо сохтори худ муқоиса кардан
Бо ин саҳифа чӣ тавр кор кардан лозим

Коди дар заминаи торик бо тугмаи ▶ Запустить — зинда аст: тағйир диҳед ва иҷро кунед. Блокҳои «Амалиёт» худкор аз рӯи матн дар консол санҷида мешаванд. Дар якчанд вазифа саҳифаи хурд бо пешнамоиш ҳаст: дар чап код (ҷадвалҳои index.html, style.css, script.js), дар рост натиҷа. Ишораҳо пинҳон шудаанд — аввал худатон кӯшиш кунед.

Муҳим дар бораи localStorage. Коди ин саҳифа дар «қуттии санҷиш»-и ҷудо кор мекунад, ки дар он localStorage-и воқеӣ нест. Мо инро дар бахши 04 муфассал таҳлил мекунем ва ба шумо ивазкунаки дақиқро бо ҳамон API медиҳем. Дар компютери шумо (дар вазифаи хонагӣ) шумо воқеиро истифода мебаред.

01 Ҳолати барнома чист

Ҳолат (state) — ин ҳамаи маълумотест, ки барномаро ҳамин ҳозир тавсиф мекунанд ва метавонанд тағйир ёбанд: рӯйхати вазифаҳо, кадомашон иҷро шудаанд, филтри интихобшуда, мавзӯи тарҳ, маҳсулот дар сабад. Агар аз ҳолат «акс» гиред, аз рӯи он он чиро, ки корбар мебинад, пурра барқарор кардан мумкин аст.

Муқоиса аз ҳаёт

Лавҳа дар истгоҳи автобус дар Душанбе. Худи лавҳа чизеро «дар хотир надорад»: он танҳо ҷадвалеро нишон медиҳад, ки дар системаи диспетчер аст. Рейси Хуҷандро бекор карданд — диспетчер сабтро иваз кард, ва лавҳа аз нав кашида шуд. Ҷадвал дар система — ин ҳолат аст. Лавҳа — экран. Агар касе навиштаи лавҳаро дастӣ, системаро нарасонда, ислоҳ мекард, ҳангоми навсозии навбатӣ ислоҳ нест мешуд, ва дар толор бетартибӣ оғоз мегардид.

Навомӯзон чӣ тавр мекунанд: маълумот «пароканда» аст

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

Маълумот дар ҷойҳои гуногун · Ctrl + Enter
let ownerName = "Мадина";
let balance = 150;
let operations = 2;
let lastOperation = "пополнение";

function showWallet() {
  console.log(`${ownerName}: ${balance} сомони, операций: ${operations}, последняя — ${lastOperation}`);
}

showWallet();

То даме ки тағйирёбандаҳо чортоянд, ин тоқатпазир аст. Аммо вақте бист шуданд, ба саволҳои оддӣ ҷавоб додан мумкин нест: маҳз чиро захира кардан лозим? Чиро ба функсия додан? Ҳангоми баромадани корбар чиро тоза кардан?

Дар лоиҳаҳои воқеӣ чӣ тавр мекунанд: як объект

Ҳамаи маълумоти тағйирёбандаи барномаро дар як объект мегузоранд — одатан онро state меноманд.

Тамоми ҳолат дар як объект · Ctrl + Enter
const state = {
  ownerName: "Мадина",
  balance: 150,
  history: ["пополнение", "покупка"],
};

function showWallet(state) {
  const last = state.history[state.history.length - 1];
  console.log(`${state.ownerName}: ${state.balance} сомони, последняя операция — ${last}`);
}

showWallet(state);
console.log(JSON.stringify(state)); // тамоми барнома — бо як нигоҳ

Фоидаҳо фавран намоёнанд: функсияҳо state-ро мегиранд ва дигар чизеро намеҷӯянд; «чиро захира кардан» — ин танҳо state аст; ислоҳ осонтар аст — кифоя аст як объектро чоп кунед ва тамоми барномаро бубинед.

Амалкорбар «Добавить»-ро пахш кард
→
Ҳолатstate.todos як вазифа зиёд шуд
→
Экранrender(state) рӯйхатро аз нав кашид
Давраи асосии ҳар интерфейс: амал ҳолатро тағйир медиҳад, экран аз рӯи ҳолат кашида мешавад. Тамоми дарс — дар бораи ҳамин давра.

Чиро ба ҳолат гузоштан лозим, чиро не

Дар ҳолат далелҳои аслӣ-ро нигоҳ медоранд, ки онҳоро аз маълумоти дигар баровардан мумкин нест. Ҳар чизеро, ки аз ҳолат ҳисоб кардан мумкин аст, дар он нигоҳ доштан лозим нест — ин маълумоти ҳосилшуда аст.

Зиёдатӣ дар ҳолат

Ҷамъ ва ҳисобкунак аз рӯйхат «алоҳида зиндагӣ мекунанд» — яктоашро нав кардан осон фаромӯш мешавад, ва онҳо ихтилоф мекунанд.

JavaScript
const state = {
  items: [{ title: "Самбуса", price: 5, qty: 3 }],
  total: 15,        // ҳисоб кардан мумкин аст
  itemsCount: 1,    // ҳисоб кардан мумкин аст
};
Танҳо далелҳо

Дар ҳолат — маҳсулот. Ҷамъро функсия ҳисоб мекунад, ва он ҳамеша ба рӯз аст.

JavaScript
const state = {
  items: [{ title: "Самбуса", price: 5, qty: 3 }],
};
const getTotal = (state) =>
  state.items.reduce((sum, i) => sum + i.price * i.qty, 0);
Ба ҳолат
Рӯйхати вазифаҳо, филтри интихобшуда, мавзӯъ, маҳсулот дар сабад, номи корбар, рақами қадами ҷорӣ.
На ба ҳолат
Ҷамъи сабад, шумораи вазифаҳои иҷрошуда, сатрҳои тайёри HTML — ҳар чизе ки аз ҳолат ҳисоб кардан мумкин аст.
Бошуурона қарор медиҳем
Чизҳои муваққатӣ мисли матни дар майдон чопшаванда. Онҳоро дар ҳолат нигоҳ доштан мумкин аст, ё захира накардан — дар ин бора боз дар бахши ЗС сухан меронем.
Хатои маъмул

Нусха кардани адад аз ҳолат ба тағйирёбандаи маҳаллӣ ва тағйир додани нусха. Адад ва сатрҳо бо қимат нусха мешаванд: нусхаро тағйир додем — ҳолат бетағйир монд.

Ҳолат тағйир наёфт · Ctrl + Enter
const state = { visitors: 10 };

let visitors = state.visitors; // ададро нусха кардем
visitors = visitors + 1;       // нусхаро тағйир додем

console.log(visitors);         // → 11
console.log(state.visitors);   // → 10 — дар ҳолат чизе тағйир наёфт

Барои муфассалтар хондан: learn.javascript.ru — Объекты · Копирование объектов и ссылки

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

Маълумоти «пароканда»-ро ба ҳолат ҷамъ мекунем, нигоҳ накардани зиёдатиро меомӯзем ва хаторо меёбем.

ВАЗИФАИ 1.1

Ҳамён дар як объект

осон

Маълумоти ҳамён дар се тағйирёбанда пароканда аст. Онҳоро ба як объекти state бо ҳамон номҳои майдонҳо ҷамъ кунед ва тавассути JSON.stringify чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
let owner = "Мадина";
let balance = 150;
let currency = "сомони";

// const state = { ... }-ро бо майдонҳои owner, balance, currency созед


console.log(JSON.stringify(state));
Натиҷаи интизоршаванда
{"owner":"Мадина","balance":150,"currency":"сомони"}
Ишора 1

Объект дар қавсҳои фигурӣ навишта мешавад: { калид: қимат, калид: қимат }. Қиматҳоро аз тағйирёбандаҳои боло гирифтан мумкин аст.

Ишора 2

Агар калид ва тағйирёбанда яксон номида шаванд, мухтасар навиштан мумкин аст: { owner, balance, currency }.

ВАЗИФАИ 1.2

Сабад: ҳисоб мекунем, на нигоҳ медорем

осон

Функсияи getTotal(state)-ро нависед, ки ҷамъи сабадро аз рӯи state.items ҳисоб мекунад (нарх × миқдор). Ҷамъро ба ҳолат гузоштан лозим нест: харидор миқдорро тағйир медиҳад, ва функсия ҳамеша аз нав ҳисоб мекунад.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  items: [
    { title: "Самбуса", price: 5, qty: 3 },
    { title: "Чой", price: 8, qty: 2 },
    { title: "Нон", price: 4, qty: 1 },
  ],
};

// function getTotal(state) { ... }

console.log(getTotal(state));
state.items[2].qty = 3; // харидор боз нон гирифт
console.log(getTotal(state));
Натиҷаи интизоршаванда
35
43
Ишора 1

Тағйирёбандаи sum = 0-ро созед, бо давра аз state.items гузаред ва item.price * item.qty-ро илова кунед.

Ишора 2

Ё кӯтоҳтар: state.items.reduce((sum, item) => sum + item.price * item.qty, 0). return-ро фаромӯш накунед.

ВАЗИФАИ 1.3

Хаторо ёб: ҳисобкунаке, ки ҳисоб намекунад

миёна

Маъмур дар даромадгоҳи синф «+1»-ро пахш мекунад, аммо шумораи меҳмонон намеафзояд. Сабабро ёбед ва функсияи addVisitor-ро ислоҳ кунед: он бояд маҳз ҳолатро тағйир диҳад.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = { visitors: 0 };

function addVisitor(state) {
  let visitors = state.visitors;
  visitors = visitors + 1;
}

addVisitor(state);
addVisitor(state);
addVisitor(state);
console.log("Посетителей:", state.visitors);
Натиҷаи интизоршаванда
Посетителей: 3
Ишора 1

Мисоли болоро дар бораи «нусха бо қимат» хонед. Функсия маҳз чиро зиёд мекунад — ададро дар ҳолат ё тағйирёбандаи худро?

Ишора 2

Майдонро бевосита тағйир диҳед: state.visitors = state.visitors + 1; (ё state.visitors++).

02 Рендер аз ҳолат: render(state)

Ғояи навбатӣ — аз ҳама муҳим дар дарс. Экран зиндагии худро намекунад: он ҳар дафъа аз нав аз рӯи ҳолат кашида мешавад. Барои ин функсияи render-ро менависанд. Он ҳолатро мегирад ва нишон медиҳад. Дигар чизе намекунад: чизеро тағйир намедиҳад ва чизеро ҳал намекунад.

Барои он ки ҳоло ба HTML парешон нашавем, «экран» дар мисоли аввал консол мешавад:

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  todos: [
    { id: 1, text: "Купить нон", done: true },
    { id: 2, text: "Сделать домашку по JS", done: false },
  ],
};

function render(state) {
  console.log("--- Список дел ---");
  state.todos.forEach((todo) => {
    const mark = todo.done ? "[x]" : "[ ]";
    console.log(`${mark} ${todo.text}`);
  });
}

render(state);

Акнун ҳамин чиз дар саҳифаи воқеӣ. Ба сохтори скрипт диққат диҳед: дар боло ҳолат, баъд render, баъд амал ва коркардкунандаи клик. «Добавить блюдо»-ро пахш кунед ва бубинед, ки рӯйхат чӣ тавр тағйир меёбад:

Рендери аввал: рӯйхати таомҳо · Ctrl + Enter
<button id="add">Добавить блюдо</button>
<ul id="menu"></ul>
<p id="info"></p>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.

Қоидаҳои render-и хуб

  1. Танҳо ҳолатро мехонад. Маълумотро барои экран аз state мегирем, на аз DOM. Не list.children.length, балки state.todos.length.
  2. Чизеро тағйир намедиҳад. Дар дохили render push, state.x = … ва дигар тағйироти маълумот нест.
  3. Ҳамаро пурра мекашад. Контейнерро пок кардем ва аз нав сохтем. Барои рӯйхатҳо аз даҳҳо элемент ин фаврист, ва хатоҳо камтар: фикр кардан лозим нест, «маҳз чӣ тағйир ёфт».
  4. Баъди ҳар амал даъват мешавад. Ҳолатро тағйир додем — render()-ро даъват кардем. Фаромӯш кардем — экран «дурӯғ мегӯяд».
  5. Ҳолати холиро кашида метавонад. Агар вазифаҳо набошанд, «Список пуст»-ро нишон медиҳем, на холигии сафед.
Муқоиса аз ҳаёт

Ошпаз дар чойхона фармоишро аз рӯи чек ҷамъ мекунад, на аз рӯи хотира. Фармоиш тағйир ёфт — ба чек нигоҳ кард ва аз нав тайёр кард. Чек — ин ҳолат аст. Таом дар миз — экран. Бо чек баҳс кардан беҳуда аст: агар дар чек «бе пиёз» бошад, пиёз намешавад.

Ду сарчашмаи ҳақиқат

Шумораи вазифаҳо ҳам дар массив ва ҳам «дар саҳифа» нигоҳ дошта мешавад. Яктоашро нав кардан фаромӯш шуд — онҳо ихтилоф мекунанд.

JavaScript
// ❌ маълумотро аз DOM мегирем
const count = list.children.length;
if (count >= 5) {
  alert("Слишком много задач");
}
Як сарчашмаи ҳақиқат

Ҳақиқат — дар state. DOM — танҳо инъикоси он.

JavaScript
// ✓ маълумотро аз ҳолат мегирем
if (state.todos.length >= 5) {
  alert("Слишком много задач");
}
Хатои маъмул

Маълумотро тағйир додан ва фавран экранро дастӣ «ислоҳ кардан» (li.textContent = …, li.remove()) дар ҳар коркардкунанда. То даме ки элемент як аст — кор мекунад. Вақте филтрҳо, ҳисобкунакҳо ва захиракунӣ пайдо мешаванд, шумо гоҳ яктояшро, гоҳ дигаронро нав кардан фаромӯш мекунед. state-ро тағйир медиҳем, render()-ро даъват мекунем — ва тамом.

Барои муфассалтар хондан: learn.javascript.ru — DOM-дерево · MDN — Введение в DOM

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

render-ро барои консол менависем: ин ҳамон мантиқест, ки барои саҳифа.

ВАЗИФАИ 2.1

Аввалин render

осон

render(state)-ро нависед: барои ҳар вазифа сатри [x] матн (агар иҷро шуда бошад) ё [ ] матн (агар не)-ро чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  todos: [
    { id: 1, text: "Купить нон", done: true },
    { id: 2, text: "Сделать домашку по JS", done: false },
    { id: 3, text: "Позвонить маме", done: false },
  ],
};

// function render(state) { ... }

render(state);
Натиҷаи интизоршаванда
[x] Купить нон
[ ] Сделать домашку по JS
[ ] Позвонить маме
Ишора 1

state.todos-ро тавассути forEach ё for...of гузаред ва барои ҳар вазифа як сатр чоп кунед.

Ишора 2

Нишонаро бо оператори сегона интихоб кунед: todo.done ? "[x]" : "[ ]".

ВАЗИФАИ 2.2

Рӯйхати холӣ

осон

render-ро бо ҳолати холӣ нависед: агар вазифаҳо набошанд, Список пуст — добавьте первую задачу-ро чоп кунед ва дигар чизе не. Формати рӯйхати ғайрихолӣ пештара ([ ] ва [x]).

Таҳрир кардан мумкин аст · Ctrl + Enter
function render(state) {
  // коди шумо
}

render({ todos: [] });
render({ todos: [{ id: 1, text: "Выучить localStorage", done: false }] });
Натиҷаи интизоршаванда
Список пуст — добавьте первую задачу
[ ] Выучить localStorage
Ишора 1

state.todos.length === 0-ро дар худи оғози функсия санҷед.

Ишора 2

Дар дохили if паёмро чоп кунед ва return кунед — он гоҳ боқимонда код иҷро намешавад (баромади барвақт).

ВАЗИФАИ 2.3

Хаторо ёб: аз нав кашиданро фаромӯш кардем

миёна

Баъди илова кардани вазифаҳо экран кӯҳна мемонад. Кодро чунон ислоҳ кунед, ки render баъди ҳар амал даъват шавад — аммо даъватро як маротиба, дар дохили addTodo навиштан лозим, на баъди ҳар даъвати беруна.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = { todos: [{ id: 1, text: "Хлеб", done: false }] };

function render(state) {
  console.log("--- Экран ---");
  state.todos.forEach((todo) => console.log(`${todo.done ? "[x]" : "[ ]"} ${todo.text}`));
}

function addTodo(text) {
  state.todos.push({ id: state.todos.length + 1, text, done: false });
}

render(state);
addTodo("Молоко");
addTodo("Чой");
Натиҷаи интизоршаванда
--- Экран ---
[ ] Хлеб
--- Экран ---
[ ] Хлеб
[ ] Молоко
--- Экран ---
[ ] Хлеб
[ ] Молоко
[ ] Чой
Ишора 1

Ҳолат тағйир ёфт, аммо экран не. Кӣ бояд ба экран бигӯяд «аз нав кашид»?

Ишора 2

render(state);-ро ҳамчун сатри охир ба бадани addTodo илова кунед. Он гоҳ ҳар илова худкор экранро нав мекунад.

ВАЗИФАИ 2.4

Филтр ва ҳисобкунак

душвор

Дар ҳолат майдони filter пайдо шуд: "all", "active" (танҳо иҷронашудаҳо) ё "done" (танҳо иҷрошудаҳо). render-ро нависед: сатри аввал Фильтр: …, баъд вазифаҳои мувофиқ дар формати [x]/[ ], ва сатри охирин Осталось: N — шумораи вазифаҳои иҷронашуда дар тамоми рӯйхат, на танҳо дар байни намоёнҳо.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  filter: "all",
  todos: [
    { id: 1, text: "Нон", done: true },
    { id: 2, text: "Молоко", done: false },
    { id: 3, text: "Чой", done: true },
    { id: 4, text: "Зира", done: false },
  ],
};

// function render(state) { ... }

render(state);
state.filter = "active";
render(state);
state.filter = "done";
render(state);
Натиҷаи интизоршаванда
Фильтр: all
[x] Нон
[ ] Молоко
[x] Чой
[ ] Зира
Осталось: 2
Фильтр: active
[ ] Молоко
[ ] Зира
Осталось: 2
Фильтр: done
[x] Нон
[x] Чой
Осталось: 2
Ишора 1

Аввал рӯйхатро барои нишон додан гиред: тавассути filter вазифаҳои ба state.filter мувофиқро нигоҳ доред. Барои "all" филтр кардан лозим нест.

Ишора 2

Ҳисобкунак — ҳисоби алоҳида аз рӯи state.todos: state.todos.filter((t) => !t.done).length. Рӯйхати намоён ба он таъсир намерасонад.

03 Амалҳо: ҳолатро чӣ тавр дуруст тағйир додан

Ҳолатро функсияҳои хурди амал (actions) тағйир медиҳанд: addTodo, toggleTodo, removeTodo. Коркардкунандаҳои ҳодисаҳо худашон ба маълумот намедароянд — онҳо танҳо амали лозимиро даъват мекунанд. Ҳар амал се корро мекунад: ҳолатро тағйир медиҳад → render()-ро даъват мекунад (ва дар бахшҳои оянда — боз захира мекунад).

Ҳодисаклик ба тугма ё ба вазифа
→
АмалaddTodo(text)
toggleTodo(id)
removeTodo(id)
→
render()экран аз нав аз рӯи state кашида мешавад
Коркардкунанда → амал → рендер. Маълумот танҳо дар амалҳо тағйир меёбад.

Се усул барои массивҳо дар ҳолат

Ҳамаи тағйирот дар рӯйхат ба се амал мерасад. Мувофиқа мекунем, ки массивро на «дар ҷои худ» тағйир диҳем, балки навашро таъин кунем — ин тавр пайгирии он ки маҳз чӣ тағйир ёфт осонтар аст, ва маҳз ҳамин услуб дар React боз ба шумо лозим мешавад.

Чӣ мекунемУсулСабт
Илова карданspread ...state.todos = [...state.todos, newTodo];
Якторо тағйир доданmapstate.todos = state.todos.map((t) => t.id === id ? { ...t, done: !t.done } : t);
Нест карданfilterstate.todos = state.todos.filter((t) => t.id !== id);
Се амал ва render · Ctrl + Enter
const state = { nextId: 1, todos: [] };

function render() {
  console.log("--- Экран ---");
  if (state.todos.length === 0) console.log("(пусто)");
  state.todos.forEach((t) => console.log(`${t.id}. ${t.done ? "[x]" : "[ ]"} ${t.text}`));
}

function addTodo(text) {
  const todo = { id: state.nextId, text, done: false };
  state.nextId = state.nextId + 1;
  state.todos = [...state.todos, todo];
  render();
}

function toggleTodo(id) {
  state.todos = state.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t));
  render();
}

function removeTodo(id) {
  state.todos = state.todos.filter((t) => t.id !== id);
  render();
}

addTodo("Купить нон");
addTodo("Выучить JSON");
toggleTodo(1);
removeTodo(2);
Муқоиса аз ҳаёт

Касса дар мағоза дар Бозори Сабз. Харидор бо дасташ ба касса намедарояд — вай ба фурӯшанда мегӯяд: «илова кунед», «гиред», «қайд кунед». Фурӯшанда (амал) сабтро ба журнал (ҳолат) ворид мекунад ва натиҷаро дар дисплей (рендер) нишон медиҳад. Ҳамон амалҳои фаҳмо — ҳар дафъа. Касе дисплейро бо қалам ислоҳ намекунад.

id-ро аз куҷо гирифтан

Ҳар вазифа бояд id-и ягона дошта бошад: тавассути он мо онро барои қайд ё нест кардан меёбем. Васваса — гирифтани state.todos.length + 1. Он баъди аввалин нест кардан вайрон мешавад:

Тузоқ: id аз рӯи дарозии массив · Ctrl + Enter
let todos = [{ id: 1 }, { id: 2 }, { id: 3 }];

todos = todos.filter((t) => t.id !== 1);          // якумро нест кардем: ду то монд
todos = [...todos, { id: todos.length + 1 }];     // нав id = 3 гирифт

console.log(todos.map((t) => t.id).join(","));    // → 2,3,3 — ду id-и якхела!

Бинобар ин дар ҳолат ҳисобкунаки алоҳидаи nextId-ро нигоҳ медоранд: он танҳо меафзояд ва ҳеҷ гоҳ як рақамро ду маротиба намедиҳад.

Амалҳо дар саҳифаи воқеӣ

Вакилкуниро аз дарси 9 ба ёд оред: як коркардкунанда дар рӯйхат, ва элементи лозимиро тавассути event.target.closest(...) меёбем. Вазифаи лозимиро аз рӯи data-id муайян мекунем:

Пайванд: клик → id → амал
list.addEventListener("click", (event) => {
  const li = event.target.closest("li");
  if (!li) return;                     // клик берун аз вазифаҳо
  toggleTodo(Number(li.dataset.id));   // data-id — сатр, ба адад табдил медиҳем
});

Ба Number(...) диққат диҳед: қиматҳо аз dataset ҳамеша сатранд, ва id дар ҳолат — адад. Бе табдил t.id === id ҳеҷ гоҳ кор намекунад: 1 === "1" — ин false аст.

Хатои маъмул

Ҳолатро тағйир додан ва натиҷаро дар тағйирёбандаи алоҳида захира кардан, дубора ба state таъин карданро фаромӯш карда. Усулҳои map ва filter массиви аслиро тағйир намедиҳанд — онҳо навашро бармегардонанд.

Натиҷаи map гум шуд · Ctrl + Enter
const state = { todos: [{ id: 1, done: false }] };

state.todos.map((t) => ({ ...t, done: true })); // массиви навро ба ҷое нанавиштем
console.log(state.todos[0].done);               // → false — чизе тағйир наёфт

Барои муфассалтар хондан: learn.javascript.ru — Методы массивов · Делегирование событий

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

Амалҳоро барои рӯйхати корҳо ва сабад менависем. Ва дар вазифаи охири бахш — амал дар саҳифаи воқеӣ.

ВАЗИФАИ 3.1

Амали addTodo

осон

addTodo(state, text)-ро нависед: вазифаи { id, text, done: false }-ро бо id аз state.nextId месозад, онро ба state.todos илова мекунад ва nextId-ро ба 1 зиёд мекунад.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = { nextId: 1, todos: [] };

// function addTodo(state, text) { ... }

addTodo(state, "Купить нон");
addTodo(state, "Выучить JSON");
console.log(JSON.stringify(state));
Натиҷаи интизоршаванда
{"nextId":3,"todos":[{"id":1,"text":"Купить нон","done":false},{"id":2,"text":"Выучить JSON","done":false}]}
Ишора 1

Аввал объекти вазифаро ҷамъ кунед, id-ро аз state.nextId гирифта. Тартиби майдонҳо: id, text, done.

Ишора 2

Се сатр: todo-ро сохтан; state.todos = [...state.todos, todo];; state.nextId = state.nextId + 1;.

ВАЗИФАИ 3.2

Иҷрошуда қайд кардан

миёна

toggleTodo(state, id)-ро тавассути map нависед: дар вазифа бо id-и лозимӣ қимати done ба муқобил иваз мешавад, вазифаҳои боқимонда чун буданд мемонанд. Функсияи marks аллакай навишта шудааст — он нишон медиҳад, кадом вазифаҳо иҷро шудаанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  todos: [
    { id: 1, text: "Нон", done: false },
    { id: 2, text: "Молоко", done: false },
    { id: 3, text: "Чой", done: false },
  ],
};

const marks = (state) => state.todos.map((t) => (t.done ? "x" : "-")).join("");

// function toggleTodo(state, id) { ... }

toggleTodo(state, 2);
console.log(marks(state));
toggleTodo(state, 1);
console.log(marks(state));
toggleTodo(state, 2);
console.log(marks(state));
Натиҷаи интизоршаванда
-x-
xx-
x--
Ишора 1

Дар дохили map барои ҳар вазифа бипурсед: ин лозим аст? Агар ҳа — нусхаро бо done-и баръакс баргардонед, агар не — худи вазифаро.

Ишора 2

state.todos = state.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t));

ВАЗИФАИ 3.3

Нест кардан бе такрори id

миёна

removeTodo(state, id)-ро тавассути filter нависед. Функсияи addTodo аллакай тайёр аст ва id-ро аз nextId мегирад. Санҷед: баъди нест кардан ва илова кардани вазифаи нав ҳамаи id бояд ягона монанд.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  nextId: 4,
  todos: [
    { id: 1, text: "Нон", done: false },
    { id: 2, text: "Молоко", done: false },
    { id: 3, text: "Чой", done: false },
  ],
};

function addTodo(state, text) {
  state.todos = [...state.todos, { id: state.nextId, text, done: false }];
  state.nextId = state.nextId + 1;
}

// function removeTodo(state, id) { ... }

removeTodo(state, 2);
addTodo(state, "Зира");
console.log(state.todos.map((t) => t.id).join(","));
console.log("Задач:", state.todos.length);
Натиҷаи интизоршаванда
1,3,4
Задач: 3
Ишора 1

filter элементҳоеро нигоҳ медорад, ки барои онҳо функсия true баргардонд. Ҳангоми нест кардан кадом вазифаҳоро нигоҳ доштан лозим?

Ишора 2

state.todos = state.todos.filter((t) => t.id !== id); — ҳамаро ғайр аз вазифа бо ин id нигоҳ медорем.

ВАЗИФАИ 3.4

Сабад: тағйири миқдор

душвор

changeQty(state, id, delta)-ро нависед: delta-ро (метавонад манфӣ бошад) ба миқдори маҳсулот илова мекунад. Агар баъд аз ин миқдор 0 ё камтар шуд, маҳсулот аз сабад нест карда мешавад. Функсияи summary аллакай тайёр аст.

Таҳрир кардан мумкин аст · Ctrl + Enter
const state = {
  items: [
    { id: 1, title: "Самбуса", qty: 2 },
    { id: 2, title: "Чой", qty: 1 },
  ],
};

const summary = (state) =>
  state.items.map((i) => `${i.title}×${i.qty}`).join(", ") || "пусто";

// function changeQty(state, id, delta) { ... }

changeQty(state, 1, +1);
console.log(summary(state));
changeQty(state, 2, -1);
console.log(summary(state));
changeQty(state, 1, -5);
console.log(summary(state));
Натиҷаи интизоршаванда
Самбуса×3, Чой×1
Самбуса×3
пусто
Ишора 1

Ду қадам: аввал map — миқдорро дар маҳсулоти лозимӣ тағйир медиҳем; баъд filter — танҳо маҳсулотро бо qty > 0 нигоҳ медорем.

Ишора 2

state.items = state.items.map((i) => (i.id === id ? { ...i, qty: i.qty + delta } : i)).filter((i) => i.qty > 0);

ВАЗИФАИ 3.5

Ҳисобкунак дар саҳифа

миёна

Ҳисобкунаки меҳмононро ба итмом расонед: render ададро аз state.visitors дар элементи #count нишон медиҳад; тугмаи «+1» онро зиёд мекунад, ва тугмаи «−1» кам мекунад, аммо на аз сифр паст. Бояд ҳолатро тағйир дод, баъд render()-ро даъват кард. Сатрҳои поёни скрипт тугмаҳоро худашон мепахшанд ва санҷишро чоп мекунанд — онҳоро нест накунед.

Ҳисобкунаки меҳмононии курс · Ctrl + Enter
<p>Посетителей: <b id="count"></b></p>
<button id="plus">+1</button>
<button id="minus">−1</button>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
после +1: 3 3
после −1: 0 0
Ишора 1

Дар render як сатр кофист: countEl.textContent = state.visitors;. Боқимонда — дар коркардкунандаҳо, ва дар ҳар кадом дар охир render() даъват мешавад.

Ишора 2

На аз сифр паст: state.visitors = Math.max(0, state.visitors - 1); ё санҷиши if (state.visitors > 0).

04 localStorage: хотираи браузер

Ҳоло тамоми ҳолат дар тағйирёбанда зиндагӣ мекунад. Саҳифаро нав кардед — тағйирёбандаҳо аз нав сохта мешаванд, ва ҳолат сифр мешавад. Барои он ки маълумот аз нав бор шудан гузарад, онро ба ҷое гузоштан лозим аст, ки бо саҳифа гум намешавад. Барои ин браузер localStorage дорад — анбори хурди «калид → сатр», ки ба сайт вобаста аст.

Муқоиса аз ҳаёт

Анбори ашё дар вокзал. Шумо ҷомадонро месупоред ва рақамча мегиред. Меравед, дар шаҳр сайр мекунед, рӯзи дигар бармегардед — ҷомадон ба ҷояш. Рақамча — ин калид, ҷомадон — қимат. Аммо ба ин анбор танҳо ашёи бастабандишударо гузоштан мумкин аст: localStorage танҳо сатрҳоро мегирад. Ҳама чизи дигарро бояд бастабандӣ кард — дар ин бора дар зер.

Дар бораи localStorage чиро бояд донист

  • Ҷуфтҳои «калид — сатр»-ро нигоҳ медорад. Калидҳо низ сатранд.
  • Маълумот ҳангоми аз нав бор шудани саҳифа, бастани ҷадвал ва ҳатто аз нав оғоз кардани браузер нест намешавад.
  • Ҳар сайт анбори худро дорад: makefirststep.tj маълумоти example.com-ро намебинад.
  • Ҳаҷм — тақрибан 5 МБ барои як сайт: барои рӯйхати корҳо ё танзимот ин бо захира кифоя аст.
  • Корбар метавонад онро дар танзимот пок кунад ё дастӣ дар DevTools тағйир диҳад. Ба маълумоти аз он гирифташуда бовар кардан мумкин нест, ҳамон тавр ки ба воридот дар шакл.
  • Дар он ҷо паролҳо ва асрорро нигоҳ надоред: ҳар скрипт дар саҳифа метавонад онҳоро бихонад.

Усулҳо

УсулЧӣ мекунадНамуна
setItem(key, value)Қиматро (сатрро) аз рӯи калид менависад. Қимати кӯҳнаро иваз мекунад.localStorage.setItem("theme", "dark")
getItem(key)Қиматро мехонад. Агар калид набошад — null бармегардонад.localStorage.getItem("theme")
removeItem(key)Як сабтро нест мекунад.localStorage.removeItem("theme")
clear()Ҳамаи сабтҳои ин сайтро нест мекунад.localStorage.clear()
Муҳим: дар қуттии санҷиши ин курс localStorage-и воқеӣ нест

Тамоми коди ин саҳифа дар тирезаи ҷудогона-«қуттии санҷиш» иҷро мешавад — мо ҳамин тавр компютери шуморо аз коди тасодуфии зараровар муҳофизат мекунем. Чунин тиреза «сайти худро» надорад, бинобар ин браузер дастрасии онро ба localStorage манъ мекунад. Худатон санҷед:

Дар қуттии санҷиш хато медиҳад · Ctrl + Enter
localStorage.setItem("theme", "dark");
console.log(localStorage.getItem("theme"));

Хатои дастрасӣ — гуноҳи шумо нест ва хатои дарс нест. То шумо ҳамоно машқ карда тавонед, мо ивазкунак медиҳем: объекти storage бо ҳамон усулҳои setItem, getItem, removeItem, clear. Он маълумотро дар хотира то даме ки коди шумо кор мекунад, нигоҳ медорад. Ва барои он ки «аз нав бор кардани саҳифа»-ро дар қуттии санҷиш ҳам нишон додан мумкин бошад, мо функсияи оғози барномаро якчанд маротиба пай дар пай дар як скрипт даъват мекунем: анбор мемонад, аммо тағйирёбандаҳои барнома не.

Ивазкунаки storage — ҳамон API, ки localStorage дорад · Ctrl + Enter
// ── ивазкунаки localStorage ─────────────────────────────────────
// Дар қуттии санҷиш localStorage-и воқеӣ дастнорас аст — дар компютери шумо
// window.localStorage-ро истифода баред. Боқимонда код тағйир намеёбад.
function createStorage() {
  let data = {};
  return {
    getItem(key) {
      return Object.hasOwn(data, key) ? data[key] : null;
    },
    setItem(key, value) {
      data[key] = String(value); // мисли воқеӣ: ҳама чиз ба сатр табдил меёбад
    },
    removeItem(key) {
      delete data[key];
    },
    clear() {
      data = {};
    },
  };
}
const storage = createStorage();
// ─────────────────────────────────────────────────────────────

storage.setItem("theme", "dark");
console.log(storage.getItem("theme"));    // → dark
console.log(storage.getItem("language")); // → null — чунин калид нест
storage.removeItem("theme");
console.log(storage.getItem("theme"));    // → null
Чӣ тавр ба localStorage-и воқеӣ гузаштан

Тамоми кодро дар дарс тавассути тағйирёбандаи storage нависед. Дар компютери шумо, дар файли оддии HTML, кифоя аст ивазкунакро бо як сатр иваз кунед — боқимонда бе тағйирот кор мекунад:

Дар компютери худатон
const storage = window.localStorage;

Мехоҳед ҳоло воқеиро кӯшиш кунед? Ягон сайтро (ҳатто ҳамин)-ро кушоед, F12 → ҷадвали Console-ро пахш кунед ва localStorage.setItem("test", "привет"), баъд localStorage.getItem("test")-ро ворид кунед. Сабтҳоро дар ҷадвали Application (дар Firefox — Storage) → Local Storage дидан мумкин аст. Дар ҳамон ҷо онҳоро таҳрир кардан ва нест кардан мумкин аст.

Ҳама чиз ба сатр табдил меёбад

Ин тузоқи асосӣ аст. Адад гузоштем — сатр гирифтем. Объект гузоштем — матни "[object Object]" гирифтем.

Тузоқ: танҳо сатрҳо · Ctrl + Enter
function createStorage() {
  let data = {};
  return {
    getItem: (key) => (Object.hasOwn(data, key) ? data[key] : null),
    setItem: (key, value) => { data[key] = String(value); },
    removeItem: (key) => { delete data[key]; },
    clear: () => { data = {}; },
  };
}
const storage = createStorage();

storage.setItem("visits", 5);
const visits = storage.getItem("visits");
console.log(typeof visits);   // → string, на number
console.log(visits + 1);      // → 51 — сатрҳо мечаспанд!
console.log(Number(visits) + 1); // → 6 — ин тавр дуруст аст

storage.setItem("user", { name: "Фарход" });
console.log(storage.getItem("user")); // → [object Object] — маълумот гум шуд

Ададҳоро тавассути Number(...) бармегардонем. Ва барои объектҳо ва массивҳо абзори махсус ҳаст — JSON. Он маҳз барои «бастабандӣ»-и объект ба сатр ва «кушодан»-и бозгашт лозим аст. Бахши навбатӣ ба он бахшида шудааст.

«Аз нав бор кардан» дар қуттии санҷиш

Инак мо аз нав бор карданро чӣ тавр тақлид мекунем: функсияи openSite — ин «кушодани саҳифа» аст. Ҳар даъват аз сифр оғоз мекунад, аммо анбор боздидҳои гузаштаро дар хотир дорад:

Ҳисобкунаки боздидҳо: ҳар даъват — «боркунии нав» · Ctrl + Enter
function createStorage() {
  let data = {};
  return {
    getItem: (key) => (Object.hasOwn(data, key) ? data[key] : null),
    setItem: (key, value) => { data[key] = String(value); },
    removeItem: (key) => { delete data[key]; },
    clear: () => { data = {}; },
  };
}
const storage = createStorage();

function openSite() {
  const saved = storage.getItem("visits");        // бори аввал — null
  const visits = saved === null ? 1 : Number(saved) + 1;
  storage.setItem("visits", visits);
  console.log(`Добро пожаловать! Это визит №${visits}`);
}

openSite(); // корбар сайтро кушод
openSite(); // саҳифаро аз нав бор кард
openSite(); // рӯзи дигар кушод
Хатои маъмул

Фаромӯш кардан, ки ҳангоми оғози аввал getItem null бармегардонад. Агар фавран Number(null) + 1 кунед, 1 мешавад — тасодуфан кор мекунад, аммо null.length ё JSON.parse(...).items аллакай меафтанд. Ҳамеша фикр кунед: «Ва агар дар ин ҷо холӣ бошад-чӣ?».

Барои муфассалтар хондан: learn.javascript.ru — LocalStorage, sessionStorage · MDN — Window.localStorage

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

Дар вазифаҳои зер ивазкунаки тайёри storage-ро истифода баред (он дар сатрҳои аввал навишта шудааст — тағйир додани он лозим нест).

ВАЗИФАИ 4.1

Навиштан ва хондан

осон

Дар анбор калиди academy-ро бо қимати MakeFirstStep захира кунед. Баъд онро хонед ва чоп кунед. Баъд калиди teacher-ро, ки нест, хонед ва чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

// коди шумо
Натиҷаи интизоршаванда
MakeFirstStep
null
Ишора 1

Навиштан: storage.setItem("academy", "MakeFirstStep"). Хондан: storage.getItem("academy") — натиҷаро тавассути console.log чоп кардан лозим.

Ишора 2

Барои калиде, ки нест, getItem null бармегардонад — истисно нахоҳад буд.

ВАЗИФАИ 4.2

Пешгӯӣ кунед: адад ё сатр?

осон

Аввал чопро пешгӯӣ кунед ва иҷро кунед. Сатри дуюм ҳоло 905-ро нишон медиҳад, аммо 95 лозим аст. Кодро ислоҳ кунед: сатри аввал бояд ҳамоно string-ро чоп кунад.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

storage.setItem("score", 90);
const score = storage.getItem("score");
console.log(typeof score);
console.log(score + 5);
Натиҷаи интизоршаванда
string
95
Ишора 1

Аз getItem ҳамеша сатр меояд. Ва "90" + 5 — ин часпонидани сатрҳост.

Ишора 2

Қиматро пеш аз ҷамъ ба адад табдил диҳед: Number(score) + 5.

ВАЗИФАИ 4.3

Ҳисобкунаки боздидҳо

миёна

openSite()-ро нависед: аз анбор калиди visits-ро мехонад (агар набошад — боздидҳо 0 ҳисоб мекунем), ба 1 зиёд мекунад, захира мекунад ва Визит №N-ро чоп мекунад. Баъди се «кушодан» чоп кунед, ки дар анбор чӣ ҳаст.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

// function openSite() { ... }

openSite();
openSite();
openSite();
console.log("В хранилище:", storage.getItem("visits"));
Натиҷаи интизоршаванда
Визит №1
Визит №2
Визит №3
В хранилище: 3
Ишора 1

saved = storage.getItem("visits")-ро хонед. Агар saved === null — ин боздиди аввал аст, вагарна Number(saved) плюс 1.

Ишора 2

Ададро нав тавассути storage.setItem("visits", visits) нависед — ивазкунак худаш онро ба сатр табдил медиҳад.

ВАЗИФАИ 4.4

Мавзӯи торик, ки интихобро дар хотир дорад

миёна

toggleTheme()-ро нависед: калиди theme-ро мехонад (агар набошад — мавзӯъ light), ба муқобил иваз мекунад (light ↔ dark), захира мекунад ва мавзӯи навро чоп мекунад. Дар охир калидро нест кунед ва боварӣ ҳосил кунед, ки хондан null медиҳад.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

// function toggleTheme() { ... }

toggleTheme();
toggleTheme();
toggleTheme();
storage.removeItem("theme");
console.log(storage.getItem("theme"));
Натиҷаи интизоршаванда
dark
light
dark
null
Ишора 1

Мавзӯи ҷорӣ: storage.getItem("theme") || "light" — агар null омад, "light" гирифта мешавад.

Ишора 2

Мавзӯи нав: current === "light" ? "dark" : "light". Онро захира кунед ва чоп кунед.

05 JSON: ҳолатро ба сатр бастабандӣ мекунем

Бо JSON шумо дар дарси 7 шинос шудед. Дар ин ҷо он вазифаи мушаххасро ҳал мекунад: localStorage танҳо сатрҳоро мегирад, ва ҳолати мо — объект бо массивҳост. Бинобар ин пеш аз навиштан мо объектро ба сатр бастабандӣ мекунем (JSON.stringify), ва баъди хондан — боз мекунем (JSON.parse).

state{ todos: [...] }
stringify →
сатр'{"todos":[...]}'
setItem →
localStorageкалид → сатр
Захиракунӣ: объект → сатри JSON → анбор. Хондан ба самти муқобил меравад: getItem → parse → объект.
Ба он ҷо ва бозгашт · Ctrl + Enter
const state = {
  filter: "all",
  todos: [{ id: 1, text: "Купить нон", done: false }],
};

const raw = JSON.stringify(state);    // объект → сатр
console.log(raw);
console.log(typeof raw);              // → string

const restored = JSON.parse(raw);     // сатр → объект
console.log(restored.todos[0].text);  // → Купить нон
console.log(restored === state);      // → false — ин нусхаи нав аст, на ҳамон объект
Муқоиса аз ҳаёт

Кӯч. Диванро якбора ба мошин ҷо намекунанд, бинобар ин онро ҷудо мекунанд, ба қуттиҳо мепечонанд ва мебаранд. Дар хонаи нав мувофиқи дастур боз ҷамъ мекунанд. stringify — «ҷудо кардан ва печонидан», parse — «ҷамъ кардан». Диван ҳамон аст, аммо ин аллакай нусхаи нави ҷамъшуда аст, на ҳамон худаш.

Ҷуфти функсияҳои saveState / loadState

То ки JSON.stringify ва setItem-ро дар даҳ ҷо нанависем, онҳоро дар ду функсия пинҳон мекунем. Тамоми барнома ба анбор танҳо тавассути онҳо муроҷиат мекунад:

Қолаб: захира ва хондани ҳолат
const KEY = "todo-app:v1";   // калид: номи барнома + версияи формат

function saveState(state) {
  storage.setItem(KEY, JSON.stringify(state));
}

function loadState() {
  const raw = storage.getItem(KEY);
  return raw === null ? null : JSON.parse(raw);
}

Калид бо префикси todo-app: аз низоъ бо барномаҳои дигар дар ҳамон сайт муҳофизат мекунад, ва :v1 вақте лозим мешавад, ки шумо формати ҳолатро тағйир диҳед: версияи нав калиди навро хонда метавонад ва ба маълумоти кӯҳна пешпо нахӯрад.

Чӣ аз бастабандӣ намегузарад

JSON — танҳо матн бо қиматҳои оддӣ аст. На ҳар чизе ки дар объект аст, онро ба он табдил додан мумкин аст:

Чӣ дар объектБаъди stringify → parse чӣ мешавад
Сатрҳо, адад, true/false, null, массивҳо, объектҳоБе тағйирот
undefinedМайдон нест мешавад
Функсияҳо ва усулҳоМайдон нест мешавад
Санаи new Date()Сатр мешавад (формати ISO), на сана
NaN, Infinitynull мешаванд
Чӣ дар роҳ гум шуд · Ctrl + Enter
const task = {
  title: "Сдать проект",
  deadline: new Date(0),    // сана → сатр
  note: undefined,          // нест мешавад
  remind() { console.log("!"); }, // нест мешавад
};

const copy = JSON.parse(JSON.stringify(task));
console.log(JSON.stringify(copy));
console.log(typeof copy.deadline);  // → string
console.log(typeof copy.remind);    // → undefined

Хулоса: дар ҳолат маълумоти «оддӣ»-ро нигоҳ доред — сатрҳо, адад, қиматҳои булӣ, массивҳо ва объектҳо. Санаҳо — ҳамчун адад (Date.now()) ё сатр. Функсияҳоро ба ҳолат умуман намегузоранд: функсияҳо — амалҳоянд, онҳо дар код зиндагӣ мекунанд.

Хатоҳои JSON

Қоидаҳои формат сахтанд: калидҳо ва сатрҳо танҳо дар нохунакҳои дугона, бе вергул баъди элементи охирин, бе шарҳҳо. Вайрон кардед — JSON.parse истиснои SyntaxError-ро мепартояд, ва барнома меафтад. Мо муқобилат кардани инро дар бахши навбатӣ меомӯзем.

Ин код хато медиҳад · Ctrl + Enter
const raw = "{'text': 'нон'}";       // нохунакҳои ягона — ин JSON нест
const data = JSON.parse(raw);        // ✖ SyntaxError
console.log(data);
Маслиҳат

Барои зуд «дида баромадани» ҳолати калон дар консол, JSON.stringify(state, null, 2)-ро даъват кунед — чоп бо ақибнишинӣ мешавад, ду фосила барои ҳар сатҳ. Барои захира дар localStorage ақибнишинӣ лозим нест.

Барои муфассалтар хондан: learn.javascript.ru — Формат JSON, метод toJSON · MDN — JSON

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

Бастабандӣ мекунем, мекушоем ва меёбем, ки дар роҳ чӣ гум мешавад.

ВАЗИФАИ 5.1

Танзимотро бастабандӣ кунед

осон

Объекти танзимотро ба сатри JSON табдил диҳед. Худи сатрро, баъд навъи онро чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const settings = {
  theme: "dark",
  fontSize: 16,
  favorites: ["курутоб", "шакароб"],
};

// коди шумо: сатрро гиред ва онро ва навъи онро чоп кунед
Натиҷаи интизоршаванда
{"theme":"dark","fontSize":16,"favorites":["курутоб","шакароб"]}
string
Ишора 1

Усули лозимӣ — JSON.stringify(settings). Натиҷаро ба тағйирёбанда захира кунед.

Ишора 2

Навъ чунин чоп мешавад: console.log(typeof raw).

ВАЗИФАИ 5.2

Маълумотро кушоед

осон

Аз анбор сатр омад. Онро кушоед ва чоп кунед: ном, шумораи тегҳо ва навъи майдони age.

Таҳрир кардан мумкин аст · Ctrl + Enter
const raw = '{"name":"Фарход","age":21,"tags":["js","css","html"]}';

// коди шумо
Натиҷаи интизоршаванда
Фарход
3
number
Ишора 1

const data = JSON.parse(raw); — баъд аз ин data объекти оддӣ аст.

Ишора 2

data.name, data.tags.length ва typeof data.age — се даъвати console.log.

ВАЗИФАИ 5.3

Пешгӯӣ кунед: чӣ гум мешавад

миёна

Аввал дар коғаз пешгӯӣ кунед, ки чор сатр чӣ чоп мекунанд. Иҷро кунед ва муқоиса кунед. Баъд сатри панҷумро илова кунед: санаро аз сатр тавассути new Date(copy.deadline) барқарор кунед ва соли онро тавассути getUTCFullYear() чоп кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
const task = {
  title: "Сдать проект",
  done: false,
  deadline: new Date(0),
  note: undefined,
  remind() {
    console.log("!");
  },
};

const copy = JSON.parse(JSON.stringify(task));
console.log(JSON.stringify(copy));
console.log(typeof copy.deadline);
console.log("note" in copy);
console.log(typeof copy.remind);
// сатри панҷум — аз они шумо
Натиҷаи интизоршаванда
{"title":"Сдать проект","done":false,"deadline":"1970-01-01T00:00:00.000Z"}
string
false
undefined
1970
Ишора 1

Ҷадвали болоро ба ёд оред: сана ба сатр табдил меёбад, ва undefined ва функсияҳо нест мешаванд.

Ишора 2

console.log(new Date(copy.deadline).getUTCFullYear()); — санаи нав аз сатр ҷамъ мешавад.

ВАЗИФАИ 5.4

saveState ва loadState

миёна

Ҷуфти saveState(state) ва loadState()-ро бо калиди todo-app:v1 нависед. Санҷиш: ҳолатро захира кунед, онро дар хотира «вайрон кунед» ва боварӣ ҳосил кунед, ки loadState() варианти захирашударо баргардонд.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

const KEY = "todo-app:v1";

// function saveState(state) { ... }
// function loadState() { ... }

let state = {
  todos: [
    { id: 1, text: "Нон", done: true },
    { id: 2, text: "Чой", done: false },
  ],
};

saveState(state);
state = { todos: [] }; // «аз нав бор кардан»: дар хотира ҳама чиз гум шуд

const restored = loadState();
console.log(restored.todos.length);
console.log(restored.todos[0].text);
Натиҷаи интизоршаванда
2
Нон
Ишора 1

Дар saveState — як сатр: storage.setItem(KEY, JSON.stringify(state)).

Ишора 2

Дар loadState: return JSON.parse(storage.getItem(KEY)); — барои ин вазифа маълумот аллакай ҳаст, ҳолати холиро дар бахши навбатӣ таҳлил мекунем.

06 Маълумоти холӣ, вайроншуда ва кӯҳнашуда

Коде, ки «вақте ҳама чиз хуб аст» кор мекунад, нисфи кор аст. Нисфи дигар — вақте маълумот нест, вайрон шудааст ё бо версияи кӯҳнаи барнома навишта шуда буд, чӣ мешавад. Бо localStorage чунин чиз пайваста рух медиҳад:

  • Оғози аввал. Корбар бори аввал омад — дар анбор холист, getItem null бармегардонад.
  • Хасу хошок. Касе сабтро дар DevTools ислоҳ кард, сабт қатъ шуд, барномаи дигар таҳти ҳамон калид чизи худро навишт.
  • Формати кӯҳна. Дирӯз дар ҳолат майдони filter набуд, имрӯз шумо онро илова кардед, ва дар браузери корбар акси дирӯза мемонад.
Муқоиса аз ҳаёт

Анбор дар курс пеш аз оғози машғулот. Маъмур ҷевонро барои маркерҳо мекушояд: мабодо ҷевон холӣ бошад (рӯзи аввал), маркерҳо хушк шуда бошанд (маълумоти вайроншуда) ё ба ҷои онҳо стикерҳо аз семестри гузашта бошанд (формати кӯҳна)? Маъмури хуб воҳима намекунад: вай маҷмӯаи эҳтиётӣ дорад. Маҷмӯаи эҳтиётӣ дар код — ин ҳолати пешфарз аст.

Анбори холӣ

Агар калид набошад, getItem null бармегардонад. Инро ошкоро санҷед ва ҳолати пешфарзро баргардонед:

Оғози аввал: null → ҳолати пешфарз · Ctrl + Enter
function createStorage() {
  let data = {};
  return {
    getItem: (key) => (Object.hasOwn(data, key) ? data[key] : null),
    setItem: (key, value) => { data[key] = String(value); },
    removeItem: (key) => { delete data[key]; },
    clear: () => { data = {}; },
  };
}
const storage = createStorage();

const KEY = "todo-app:v1";
const DEFAULT_STATE = { nextId: 1, todos: [] };

function loadState() {
  const raw = storage.getItem(KEY);
  if (raw === null) return { ...DEFAULT_STATE, todos: [] }; // оғози аввал
  return JSON.parse(raw);
}

console.log(JSON.stringify(loadState())); // анбор холӣ аст

Ба { ...DEFAULT_STATE, todos: [] } диққат диҳед: мо нусхаи пешфарзро бармегардонем, на худи объектро. Вагарна ҳамаи тағйирот ба «маҷмӯаи эҳтиётӣ» меафтад, ва ҳангоми барқароркунии навбатӣ он аллакай холӣ нахоҳад буд.

JSON-и вайроншуда: try / catch

Агар дар анбор сатре бошад, ки JSON-и дуруст нест, JSON.parse истисно мепартояд. Онро тавассути try / catch мегиранд (шумо инро дар дарси 7 дидед) ва ба ҳолати пешфарз бармегарданд:

try / catch дар атрофи JSON.parse · Ctrl + Enter
function safeParse(raw) {
  try {
    return JSON.parse(raw);
  } catch (error) {
    console.log("Данные повреждены:", error.name);
    return null;
  }
}

console.log(safeParse('{"ok":true}'));  // → { ok: true }
console.log(safeParse("{oops"));        // → Данные повреждены: SyntaxError, потом null

Шакли нодуруст: месанҷем, ки дар дохил чӣ ҳаст

Сатр метавонад JSON-и дуруст бошад, аммо тамоман на он чизе, ки мо интизор будем: адади 42, сатр, null, объект ба ҷои массив. Бинобар ин баъди parse шакли маълумотро месанҷем:

Санҷиши шакли маълумот · Ctrl + Enter
function isValidTodos(value) {
  return Array.isArray(value);
}

console.log(isValidTodos([]));            // → true
console.log(isValidTodos({ a: 1 }));      // → false — объект, на массив
console.log(isValidTodos(JSON.parse("42"))); // → false — адад
console.log(isValidTodos(JSON.parse("null"))); // → false
Тузоқи ҷолиб

JSON.parse(null) намеафтад, балки null бармегардонад — зеро null аввал ба сатри "null" табдил меёбад, ва ин JSON-и дуруст аст. Дар натиҷа JSON.parse(storage.getItem(key)) ҳангоми анбори холӣ «кор мекунад», аммо null бармегардонад, ва аллакай сатри навбатии state.todos бо TypeError меафтад. Бинобар ин санҷиши raw === null ҳамеша лозим аст.

Ҳолат кӯҳна шуд: қиматҳои пешфарзро омехта мекунем

Вақте дар ҳолат майдонҳои нав пайдо мешаванд, аксҳои кӯҳна онҳоро надоранд. Ҳал — аз ҳолати пешфарз оғоз кардан ва он чиро, ки дар анбор ёфтем, болои он «гузоштан»:

Омезиш: маълумоти кӯҳна + майдонҳои нав · Ctrl + Enter
const DEFAULT_STATE = { filter: "all", theme: "light", todos: [] };

// акси дирӯза: майдонҳои filter ва theme ҳанӯз набуданд
const saved = { todos: [{ id: 1, text: "Нон", done: false }] };

const state = { ...DEFAULT_STATE, ...saved };
console.log(JSON.stringify(state));
// → майдонҳои filter ва theme бо пешфарз гирифта шуданд, todos — аз захирашуда

Ҳама чиз якҷоя: loadState-и боэътимод

  1. Сатрро баровардем тавассути getItem. Агар null — ҳолати пешфарзро баргардонем.
  2. Кушодем дар дохили try / catch. Хато — ҳолати пешфарзро баргардонем.
  3. Шаклро санҷидем: ин умуман объект аст (на адад, сатр, null, массив)? Не — ҳолати пешфарз.
  4. Омехтем қиматҳои пешфарзро барои майдонҳои намерасида ва майдонҳои муҳимро санҷидем: масалан, todos бояд массив бошад.
Қолаби боркунии боэътимод
function loadState() {
  const raw = storage.getItem(KEY);
  if (raw === null) return createDefaultState();     // 1. холӣ

  let saved;
  try {
    saved = JSON.parse(raw);                         // 2. JSON-и вайроншуда
  } catch {
    return createDefaultState();
  }

  if (typeof saved !== "object" || saved === null || Array.isArray(saved)) {
    return createDefaultState();                     // 3. ин объекти ҳолат нест
  }

  const state = { ...createDefaultState(), ...saved }; // 4. намерасидаро илова кардем
  if (!Array.isArray(state.todos)) state.todos = [];   //    ва майдони муҳимро таъмир кардем
  return state;
}

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

Барои муфассалтар хондан: learn.javascript.ru — Обработка ошибок, try…catch · MDN — Array.isArray()

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

Афтоданро намеомӯзем. Ивазкунаки storage аллакай ба ҳар вазифа дохил шудааст.

ВАЗИФАИ 6.1

Оғози аввал

осон

loadTodos()-ро нависед: массиви вазифаҳоро аз калиди todos бармегардонад, ва агар калид набошад — массиви холӣ. Функсияи saveTodos аллакай тайёр аст.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

function saveTodos(todos) {
  storage.setItem("todos", JSON.stringify(todos));
}

// function loadTodos() { ... }

console.log(loadTodos().length); // оғози аввал
saveTodos(["Нон", "Чой"]);
console.log(loadTodos().length);
Натиҷаи интизоршаванда
0
2
Ишора 1

Аввал raw = storage.getItem("todos")-ро хонед ва санҷед, ки оё он null нест.

Ишора 2

if (raw === null) return []; ва баъд return JSON.parse(raw);

ВАЗИФАИ 6.2

Маълумоти вайроншуда

миёна

Дар анбор касе хасу хошок навишт. loadTodos()-ро пурра кунед: агар JSON.parse афтод, Данные повреждены — начинаем с пустого списка-ро чоп кунед ва массиви холиро баргардонед.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

function loadTodos() {
  const raw = storage.getItem("todos");
  if (raw === null) return [];
  // коди шумо: маълумоти вайроншударо коркард кунед
  return JSON.parse(raw);
}

storage.setItem("todos", "{oops");
console.log(loadTodos().length);
storage.setItem("todos", '["Нон","Чой"]');
console.log(loadTodos().length);
Натиҷаи интизоршаванда
Данные повреждены — начинаем с пустого списка
0
2
Ишора 1

Танҳо сатри JSON.parse-ро дар try { ... } catch { ... } печонед.

Ишора 2

Дар try — return JSON.parse(raw);, дар catch — console.log(...) бо паём ва return [];.

ВАЗИФАИ 6.3

JSON-и дуруст, аммо на он чиз

миёна

Акнун дар анбор JSON-и дуруст ҳаст, аммо на массив. Санҷиши шаклро ба итмом расонед: loadTodos() бояд танҳо массив баргардонад — дар ҳамаи ҳолатҳои дигар массиви холӣ.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

function loadTodos() {
  const raw = storage.getItem("todos");
  if (raw === null) return [];
  try {
    const data = JSON.parse(raw);
    // коди шумо: санҷед, ки data массив аст
    return data;
  } catch {
    return [];
  }
}

for (const raw of ['{"a":1}', "42", '["Нон"]', "null"]) {
  storage.setItem("todos", raw);
  console.log(loadTodos().length);
}
Натиҷаи интизоршаванда
0
0
1
0
Ишора 1

Барои санҷиши «ин массив аст?» функсияи тайёри Array.isArray(data) ҳаст. Санҷиши оддии typeof дар ин ҷо кӯмак намекунад: массив низ «объект» аст.

Ишора 2

return Array.isArray(data) ? data : [];

ВАЗИФАИ 6.4

loadState-и боэътимод

душвор

Функсияи пурраи loadState()-ро аз рӯи қолаби дарс ҷамъ кунед. Ҳолати пешфарз: { filter: "all", theme: "light", todos: [] }. Қоидаҳо: холӣ, JSON-и вайроншуда ё на объект (адад, сатр, null, массив) — ҳолати пешфарзро баргардонед. Вагарна майдонҳои намерасидаро омехта кунед, ва агар todos на массив баромад — онро ба [] иваз кунед (майдонҳои дигари захирашуда, масалан filter, мемонанд).

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

const KEY = "todo-app:v1";
const createDefaultState = () => ({ filter: "all", theme: "light", todos: [] });

// function loadState() { ... }

console.log(JSON.stringify(loadState()));                 // 1. холӣ

storage.setItem(KEY, '{"todos":[{"id":1,"text":"Нон","done":false}]}');
console.log(JSON.stringify(loadState()));                 // 2. формати кӯҳна

storage.setItem(KEY, "}{");
console.log(JSON.stringify(loadState()));                 // 3. хасу хошок

storage.setItem(KEY, '{"filter":"done","todos":"oops"}');
console.log(JSON.stringify(loadState()));                 // 4. todos навъи нодуруст

storage.setItem(KEY, "42");
console.log(JSON.stringify(loadState()));                 // 5. на объект
Натиҷаи интизоршаванда
{"filter":"all","theme":"light","todos":[]}
{"filter":"all","theme":"light","todos":[{"id":1,"text":"Нон","done":false}]}
{"filter":"all","theme":"light","todos":[]}
{"filter":"done","theme":"light","todos":[]}
{"filter":"all","theme":"light","todos":[]}
Ишора 1

Қадамҳои қолабро пайравӣ кунед: null → try / catch → санҷиши шакл → омезиш. Ҳар ҳолати «бад» бо return createDefaultState() анҷом меёбад.

Ишора 2

Омезиш: const state = { ...createDefaultState(), ...saved };. Баъд аз он Array.isArray(state.todos)-ро санҷед — агар не, state.todos = []; гузоред. Барои ҳолати «на объект» typeof saved !== "object" || saved === null-ро фавран баъди parse санҷед.

07 Блоки ЗС: талаб мекунем, ки сохтори ҳолатро пешниҳод кунад

Муҳимтарин лаҳза дар ҳар барнома — кадом маълумотро ва бо кадом намуд нигоҳ медоред. Хато дар ин ҷо гарон меояд: сохтори ҳолатро аз нав сохтан, вақте ки render, амалҳо ва захиракунӣ аллакай навишта шудаанд, — ҳамон аст, ки пойдевори хонаи тайёрро аз нав созем. Бинобар ин маҳз ба ин ҷо ЗС-ро ҷалб кардан мувофиқ аст — аммо ҳамчун андешаи дуюм, на ҳамчун муаллиф.

Қоидаҳои курс, ки дар ин ҷо махсусан муҳиманд

1) Аввал 15 дақиқа худатон: state-и худро дар коғаз ё дар код кашед. 2) Танҳо баъд промпт. 3) Коде, ки сатр ба сатр шарҳ дода наметавонед, ба лоиҳа ворид накунед. 4) Қарорро шумо қабул мекунед: ЗС пешниҳод мекунад, шумо муқоиса мекунед ва интихоб мекунед.

Тартиби кор

  1. Версияи худӣ. Барномаро бо як ҷумла тавсиф кунед ва объекти state-ро тарҳрезӣ кунед. Қайд кунед, ки аз он чиро дар localStorage захира мекунед.
  2. Промпт аз рӯи формула: контекст → вазифа → маҳдудиятҳо → формати ҷавоб → санҷиш.
  3. Муқоиса. Тафовутҳоро ба ҷадвал нависед: майдон — дар ман — дар ЗС — чӣ ҳал кардам ва чаро.
  4. Санҷиш. Барои ҳар ҷои баҳсбарангез аз худ бипурсед: «агар дар ин ҷо холӣ бошад ё хасу хошок бошад, чӣ мешавад?».

Промпти бад ва хуб

Фикр кун, ки маълумотро барои барномаи ман чӣ тавр нигоҳ дорам

Чаро бад аст: ЗС на барномаро медонад, на сатҳи шуморо, на ин ки маълумот дар куҷо нигоҳ дошта мешавад. Вай ё маслиҳати умумӣ медиҳад, ё фавран меъмории азимро дар фреймворке, ки шумо надоред.

Контекст: ман навомӯз ҳастам, бо JavaScript-и холис бе китобхонаҳо менависам, лоиҳаи таълимӣ — ҳисобкунаки одатҳо. Корбар метавонад одат илова кунад, онро имрӯз иҷрошуда қайд кунад, нест кунад ва филтри «ҳама / иҷронашуда»-ро иваз кунад. Маълумот бояд аз нав бор шудани саҳифа гузарад. Вазифа: сохтори объекти ҳолатро пешниҳод кун ва шарҳ деҳ, ки аз он чиро ба localStorage гузоштан лозим. Маҳдудиятҳо: танҳо сатрҳо, адад, қиматҳои булӣ, массивҳо ва объектҳо (он чи аз JSON мегузарад); он чиро, ки ҳисоб кардан мумкин аст, нигоҳ надор; ҳар одат id-и ягона дорад. Формати ҷавоб: аввал намунаи объекти state ҳамчун коди JS; баъд рӯйхати майдонҳо — ба ҳар сатр «майдон — чаро лозим»; баъд чиро захира мекунем ва чиро не. Санҷиш: 2 ҳолатро ном бар, ки дар онҳо маълумоти захирашуда метавонад нодуруст бошад, ва чӣ тавр бо онҳо мубориза бурдан.

Ин варианти густурдаи қолаби ёддошти курс аст: «Сохтори объекти ҳолатро барои барномаи [тавсиф] пешниҳод кун. Шарҳ деҳ, чаро чунин майдонҳо ва чиро ба localStorage гузорам». Мо контекст, маҳдудиятҳо ва санҷишро илова кардем — ва ҷавобе гирифтем, ки онро муқоиса кардан мумкин аст.

Чек-лист: ҷавоби ЗС-ро чӣ тавр санҷидан

  • Оё маълумоти ҳосилшуда нест. Агар дар ҳолат total, doneCount ё HTML-и тайёр бошад — онҳоро ҳисоб кардан мумкин аст, пас зиёдатианд.
  • id-ҳои ягона. Онҳо аз куҷо мегиранд? nextId ҳаст, ё ЗС length + 1-ро пешниҳод мекунад (мо дидем, ки он чӣ анҷом меёбад)?
  • Навъҳои оддӣ. Оё санаҳо, функсияҳо, undefined нестанд, ки аз JSON намегузаранд?
  • Чиро захира кардан. Оё муваққатиро захира кардан лозим — масалан, матни дар майдон ҳамин ҳозир чопшаванда? Аксар вақт не: баъди аз нав бор шудан он лозим нест.
  • Версияи формат. Оё ЗС калиди намуди app:v1-ро пешниҳод кард? Ҳангоми тағйири сохтор чӣ мешавад?
  • Маълумоти холӣ ва вайроншуда. ЗС аксар вақт оғози аввал ва маълумоти вайроншударо «фаромӯш мекунад» — инро худатон илова кунед.

Ҷадвали муқоиса

СаволВерсияи манВерсияи ЗСҚарор ва сабаб
Одатҳоро чӣ тавр нигоҳ доштан?habits: []habits: [{ id, title, doneDates }]Массиви объектҳоро бо id мегирам — барои нест кардан лозим аст
Ҳисобкунаки id дар куҷост?нестnextIdnextId-ро илова мекунам — ҳифз аз такрор
Чиро захира кардан?тамоми ҳолатҳама чиз, ғайр аз матни дар майдони воридкунӣРозӣ ҳастам: майдони воридкуниро захира намекунам

Агар ҳама чиз мувофиқ омад — олӣ, шумо мисли тарроҳи ботаҷриба фикр мекунед. Агар фарқ дошта бошад — ин қиматноктарин ҷо аст: фаҳмед, далели кӣ қавитар аст. Ҳамин тавр ЗС меомӯзонад, на танҳо ба ҷои шумо мекунад.

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

Аввал сохтори худӣ, баъд санҷиши «ҷавоби ЗС» бо хатоҳо.

ВАЗИФАИ 7.1

Сохтори ман дар муқобили сохтори ЗС

миёна

Ҳолатро барои барномаи «Ҷадвали курс» фикр кунед (машғулотҳо бо ном, рӯзи ҳафта ва вақт; «ширкат кард»-ро қайд кардан мумкин аст; рӯзи интихобшуда барои дидан ҳаст). Аввал версияи худро ҳамчун объекти state нависед ва онро тавассути JSON.stringify(state, null, 2) чоп кунед. Баъд ба ЗС промптро аз рӯи намунаи боло фиристед, ҷадвали муқоисаро дар дафтар пур кунед ва қайд кунед, ки чиро тағйир додед ва чаро. Ин вазифаро омӯзгор месанҷад — вақте тамом кардед, худатон қайд кунед.

Таҳрир кардан мумкин аст · Ctrl + Enter
// 15 дақиқа худатон, бе ЗС
const state = {
  // сохтори шумо
};

console.log(JSON.stringify(state, null, 2));
Ишора 1

Фикр кунед: дар ин ҷо «далелҳои аслӣ» кадоманд (машғулотҳо, қайдҳо, рӯзи интихобшуда), ва чиро ҳисоб кардан мумкин аст (чанд машғулот иштирок шуд, рӯйхати машғулотҳо барои рӯзи интихобшуда)?

Ишора 2

Ҳар машғулот id лозим дорад, ва дар решаи ҳолат — nextId. Рӯзи ҳафтаро осонтар бо сатр ё адади 1–7 нигоҳ доштан аст, на бо сана.

ВАЗИФАИ 7.2

«Ҷавоби ЗС»-ро санҷед: се хато

душвор

ЗС захиракунии рӯйхати одатҳоро навишт, аммо дар код се хато ҳаст — ҳама аз чек-лист. Онҳоро ёбед ва чунон ислоҳ кунед, ки чоп мисли зер бошад. Функсияҳо ва ҳолатро тағйир додан мумкин аст; се блоки чопи поёнро нарасонед.

Таҳрир кардан мумкин аст · Ctrl + Enter
// ивазкунаки localStorage — тағйир надиҳед
const storage = (() => { let d = {}; return {
  getItem: (k) => (Object.hasOwn(d, k) ? d[k] : null), setItem: (k, v) => { d[k] = String(v); },
  removeItem: (k) => { delete d[k]; }, clear: () => { d = {}; } }; })();

const habits = { nextId: 1, items: [] };

function addHabit(title) {
  habits.items = [...habits.items, { id: habits.items.length + 1, title }];
}

function save() {
  storage.setItem("habits", habits);
}

function load() {
  return JSON.parse(storage.getItem("habits"));
}

addHabit("Зарядка");
addHabit("Чтение");
addHabit("Вода");
habits.items = habits.items.filter((h) => h.id !== 1); // «Зарядка»-ро нест кардем
addHabit("Сон");
console.log(habits.items.map((h) => h.id).join(","));

save();
console.log(load().items.length);

storage.clear(); // браузери «тоза»
console.log(load().items.length);
Натиҷаи интизоршаванда
2,3,4
3
0
Ишора 1

Баъди ҳар ислоҳ иҷро кунед ва хаторо хонед. Мушкилоти аввал аз рӯи сатр бо id намоён аст (такрор!), дуюм — аз рӯи он ки маҳз чӣ ба анбор навишта мешавад, сеюм — аз рӯи он ки load() ҳангоми анбори холӣ чӣ бармегардонад.

Ишора 2

1) id-ро аз habits.nextId гиред ва онро зиёд кунед. 2) Пеш аз навиштан — JSON.stringify(habits). 3) Дар load ҳангоми null ба ҷои JSON.parse(null) { nextId: 1, items: [] }-ро баргардонед.

08 Амалиёт дар дарс: рӯйхати корҳо, ки ҳамаро дар хотир дорад

Ҳамаро якҷоя ҷамъ мекунем: рӯйхати корҳо бо ҳолат, рендер, амалҳо ва захиракунӣ. Дар се марҳила мекунем, ва ҳар кадом бо санҷиш дар консол анҷом меёбад. Дар боло болои код — ҷадвалҳои index.html, style.css, script.js. Дар script.js ҳал мекунем: шарҳҳои TODO-ро ҷӯед.

Марҳилаи 1ҳолат + render + амалҳо
→
Марҳилаи 2saveState / loadState, «аз нав бор кардан»
→
Марҳилаи 3маълумоти вайроншуда + нест кардан
Ҳар марҳила худ ба худ кор мекунад. То санҷиш нагузашта бошад, ба марҳилаи оянда нагузаред.
Дар бораи «аз нав бор кардан» дар қуттии санҷиш

Аз нав бор кардани воқеии саҳифаро дар ин ҷо иҷро кардан мумкин нест: он ивазкунаки storage-ро низ нест мекунад. Бинобар ин сатрҳои поёни скрипт онро худашон тақлид мекунанд: экранро пок мекунанд, тағйирёбандаи state-ро фаромӯш мекунанд ва боз boot()-ро даъват мекунанд. Баъд аз ин танҳо он чизе зинда монда метавонад, ки дар storage аст — мисли браузери воқеӣ. Санҷиши худкори поёнро нарасонед.

Марҳилаи 1. Ҳолат, render ва амалҳо

Аввал бе захиракунӣ: як ҳолат, як функсияи render, амалҳои addTodo ва toggleTodo ва ду коркардкунанда. Шакл вазифа илова мекунад, клик ба вазифа «иҷро шуд»-ро иваз мекунад (вакилкунӣ аз дарси 9).

Амалиёт: марҳилаи 1

ВАЗИФАИ 8.1

Марҳилаи 1: рӯйхати корҳо аз ҳолат

миёна

Ҳар чизеро, ки бо TODO қайд шудааст, ба итмом расонед: ҳисобкунаки «Осталось: N» дар render, амалҳои addTodo ва toggleTodo, коркардкунандаҳои шакл ва рӯйхат. Матни холӣ (аз ҷумла аз танҳо фосилаҳо) илова нашавад.

Рӯйхати корҳо, марҳилаи 1 · Ctrl + Enter
<form id="form">
  <input id="text" placeholder="Что нужно сделать?" autocomplete="off">
  <button>Добавить</button>
</form>
<ul id="list"></ul>
<p id="counter"></p>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
2 2
done Осталось: 1
Ишора 1

Ҳисобкунак: const left = state.todos.filter((t) => !t.done).length;, баъд counter.textContent = "Осталось: " + left;.

Ишора 2

Амалҳо — мисли бахши 03: addTodo state.todos = [...state.todos, {...}] мекунад ва nextId-ро зиёд мекунад; toggleTodo map-ро истифода мебарад; ҳар ду бо render() анҷом меёбанд. Дар коркардкунандаи шакл event.preventDefault()-ро фаромӯш накунед.

Марҳилаи 2. Захира ва барқарор мекунем

Акнун хотира илова мекунем. Се чиз лозим аст: ду функсияи saveState ва loadState, даъвати saveState() дар охири ҳар амал ва функсияи boot() — «оғози саҳифа»: ҳолатро бор кардан ва экранро кашидан. Дар лоиҳаи воқеӣ сатри охири скрипт танҳо boot(); аст.

Амалиёт: марҳилаи 2

ВАЗИФАИ 8.2

Марҳилаи 2: рӯйхат аз нав бор шудан мегузарад

миёна

saveState (навиштани JSON.stringify(state) ба storage аз рӯи калиди KEY) ва loadState (агар анбор холӣ бошад — { nextId: 1, todos: [] }-ро баргардонед, вагарна кушоед)-ро амалӣ кунед. saveState()-ро дар addTodo ва toggleTodo — пеш аз render() даъват кунед. Боқимонда коди марҳилаи 1 аллакай тайёр аст.

Рӯйхати корҳо, марҳилаи 2 · Ctrl + Enter
<form id="form">
  <input id="text" placeholder="Что нужно сделать?" autocomplete="off">
  <button>Добавить</button>
</form>
<ul id="list"></ul>
<p id="counter"></p>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
до перезагрузки: 2 Осталось: 1
после: 2 done Осталось: 1
Ишора 1

saveState — як сатр бо storage.setItem. loadState — қолабро аз бахши 05 ба ёд оред, танҳо ба ҷои null ҳолати оғозӣ бармегардад.

Ишора 2

const raw = storage.getItem(KEY); return raw === null ? { nextId: 1, todos: [] } : JSON.parse(raw);. Дар амалҳо: saveState(); пеш аз render();.

Марҳилаи 3. Ҳифз аз хасу хошок ва нест кардан

Барномае, ки аз як сабти шикаста меафтад, барнома нест. loadState-ро боэътимод месозем (қолаб аз бахши 06) ва тугмаи «×»-ро барои нест кардани вазифа илова мекунем. Тугма дар дохили li аст, ва клик ба li аллакай вазифаро иваз мекунад — бинобар ин дар коркардкунандаи умумӣ аввал месанҷем, ки маҳз ба куҷо пахш карданд.

Амалиёт: марҳилаи 3

ВАЗИФАИ 8.3

Марҳилаи 3: боркунии боэътимод ва нест кардан

душвор

1) Дар loadState коркард кунед: анбори холӣ, JSON-и вайроншуда, ва шакли нодуруст — ҳолат бояд объекте бошад, ки todos дар он массив, ва nextId — адад аст; вагарна ҳолати пешфарзро баргардонед. 2) removeTodo(id)-ро тавассути filter ва коркарди тугмаи «×»-ро дар клик ба рӯйхат ба итмом расонед — пахши «×» бояд вазифаро нест кунад, на иваз.

Рӯйхати корҳо, марҳилаи 3 · Ctrl + Enter
<form id="form">
  <input id="text" placeholder="Что нужно сделать?" autocomplete="off">
  <button>Добавить</button>
</form>
<ul id="list"></ul>
<p id="counter"></p>
Пешнамоиш
Барои дидани натиҷа «Иҷро кардан»-ро пахш кунед.
Натиҷаи интизоршаванда
битые данные: 0
Нон, Зира Осталось: 2
после перезагрузки: 2
неверная форма: 0
Ишора 1

Боркунии боэътимод — се return createDefaultState() дар се ҳолати гуногун аст. Санҷиши шакл: typeof saved === "object" && saved !== null && Array.isArray(saved.todos) && typeof saved.nextId === "number".

Ишора 2

Клик ба рӯйхат: if (event.target.matches(".remove")) removeTodo(id); else toggleTodo(id);. Дар дохили removeTodo — state.todos = state.todos.filter((t) => t.id !== id);, баъд saveState() ва render().

Шумо чӣ сохтед

Ин скелетест, ки қариб ҳамаи интерфейсҳо бар он такя мекунанд: ҳолат → амалҳо → render → захиракунӣ. Трекери вазифаҳо ва одатҳо аз лоиҳаи хурд дар дарси 12 — ҳамон скелет бо майдонҳои дигар аст. Кӯшиш кунед ба он филтри «ҳама / фаъол / иҷрошуда»-ро (вазифаи 2.4) илова кунед ва филтри интихобшударо захира кунед.

09 Вазифаи хонагӣ: ба лоиҳаи дарси 9 захиракуниро илова кунед

Лоиҳаи худро аз дарси 9 (саҳифаи интерактивӣ бо ҳодисаҳо ва вакилкунӣ) гиред ва ба он дар хотир доштани маълумот-ро омӯзонед. Дар ин ҷо шумо дар компютери худатон кор мекунед — яъне воқеии localStorage-ро истифода мебаред: const storage = window.localStorage;.

Чӣ бояд кард

  • Дар лоиҳа як ҳолат — объекти state ҳаст. Дар ҳеҷ ҷо маълумоте нест, ки танҳо дар DOM «зиндагӣ» мекунад.
  • Экранро функсияи render() мекашад. Коркардкунандаҳои ҳодисаҳо амалҳоро даъват мекунанд, ва онҳо — render()-ро.
  • saveState() ва loadState() ҳастанд; калид бо префикси лоиҳа ва версия, масалан myproject:v1.
  • Ҳолат баъди ҳар амал захира мешавад ва ҳангоми кушодани саҳифа барқарор мешавад: ҷадвалро бастем, дубора кушодем — ҳама чиз ба ҷояш.
  • loadState() аз инҳо мегузарад: анбори холӣ, JSON-и вайроншуда, шакли нодурусти маълумот ва майдонҳои нави намерасида.
  • Тугмаи «Сбросить» ҳаст, ки маълумоти барномаро пок мекунад (removeItem аз рӯи калиди шумо) ва ҳолати оғозиро бармегардонад.
  • Дар ҳолат маълумоти ҳосилшуда, функсияҳо ва объектҳои сана нестанд.

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

  1. Якчанд элемент илова кунед, саҳифаро нав кунед (F5) — ҳама чиз ба ҷояш?
  2. DevTools (F12) → Application → Local Storage-ро кушоед. Калиди худро ёбед, нигоҳ кунед, қимат чӣ гуна менамояд.
  3. Қиматро дастӣ вайрон кунед: қавси охиринро нест кунед. Саҳифаро нав кунед — барнома бояд бо ҳолати оғозӣ оғоз шавад, на бо хато дар консол биафтад.
  4. Калидро нест кунед — саҳифа бояд «мисли бори аввал» кушода шавад.
  5. «Сбросить»-ро пахш кунед — маълумот ҳам аз экран, ҳам аз анбор нест шуд?
ЗС дар вазифаи хонагӣ

Қоидаи курс: 15 дақиқа худатон. Сохтори ҳолати худро дар файли NOTES.md нависед. Баъд аз ЗС мувофиқи намунаи бахши 07 пурсед ва ба ҳамон файл ҷадвали «аз они ман / ЗС / қарор»-ро илова кунед. Дар код танҳо он чиро гузоред, ки сатр ба сатр шарҳ дода метавонед. Ҳатман пурсед: «2 ҳолатро ном бар, ки дар онҳо маълумоти захирашуда нодуруст мешавад» — ва онҳоро бо даст санҷед.

Чиро супоред

  • Папкаи лоиҳа (архив ё истинод), ки дар он захиракунӣ кор мекунад.
  • Файли NOTES.md: сохтори ҳолати шумо, калид дар localStorage, чӣ захира мешавад ва чӣ не, ҷадвали муқоиса бо ЗС.
  • Сабти кӯтоҳи экран (то 30 сония): маълумот илова кардем → саҳифаро нав кардем → маълумот ба ҷояш → қиматро вайрон кардем → барнома наафтод.

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

МеъёрХолҳо
Як ҳолати state ва render(), бе «ҳақиқат дар DOM»2
Маълумот баъди аз нав бор шудан захира ва барқарор мешавад3
Кори дуруст ҳангоми анбори холӣ ва вайроншуда2
Сохтори тоза: навъҳои оддӣ, id-ҳои ягона, калид бо версия, тугмаи барқароркунӣ1
NOTES.md ва муқоиса бо ЗС: қарор бо суханони худӣ шарҳ дода шудааст2
Агар саҳифа ҳамчун файл кушода шуда бошад

Дар аксари браузерҳо localStorage барои файлҳои file:// низ кор мекунад, аммо файлҳои гуногун метавонанд маълумоти умумӣ ё алоҳида дошта бошанд — ин аз браузер вобаста аст. Боэътимодтар аст лоиҳаро тавассути Live Server дар VS Code оғоз кунед: он гоҳ он суроғаи оддии намуди localhost:5500 ва анбори худро дорад.

✓ Ёддошти дарс

  • Ҳолат — ҳамаи маълумоти тағйирёбандаи барнома дар як объекти state. Танҳо далелҳои аслиро нигоҳ доштан; он чи ҳисоб кардан мумкин аст (ҷамъ, ҳисобкунак), бо функсия ҳисоб мекунем.
  • render(state) экранро пурра ва танҳо аз рӯи ҳолат мекашад: чизеро тағйир намедиҳад, маълумотро аз DOM намехонад, рӯйхати холиро кашида метавонад.
  • Давра: ҳодиса → амал (addTodo, toggleTodo, removeTodo) → state-ро тағйир додем → render() → saveState().
  • Массивҳо дар ҳолат: илова кардан — [...arr, item], тағйир додан — map, нест кардан — filter. Натиҷаро дубора ба state таъин мекунем. Id-ро аз nextId мегирем, на аз length + 1.
  • localStorage: setItem(k, v), getItem(k) (калид нест — null), removeItem(k), clear(). Танҳо сатрҳоро нигоҳ медорад, баъди аз нав бор шудан зинда мемонад, тақрибан 5 МБ, барои ҳар сайт худӣ. Асрорро нигоҳ надоред.
  • Дар қуттии санҷиши курс localStorage-и воқеӣ дастнорас аст — ивазкунаки storage-ро бо ҳамон API истифода мебарем. Дар компютери худатон: const storage = window.localStorage;. Воқеиро дар DevTools → Application → Local Storage санҷидан мумкин аст.
  • JSON: JSON.stringify(state) пеш аз навиштан, JSON.parse(raw) баъди хондан. Намегузаранд: undefined, функсияҳо, санаҳо (сатр мешаванд).
  • loadState-и боэътимод: null → ҳолати пешфарз; JSON.parse дар try / catch; санҷиши шакл; { ...defaults, ...saved } барои майдонҳои нав. Калид бо префикс ва версия: app:v1.
  • ЗС: аввал сохтори ҳолати худӣ (15 дақиқа), баъд промпт аз рӯи формула ва муқоиса дар ҷадвал. Санҷидан: оё маълумоти ҳосилшуда нест, id аз куҷо аст, чиро захира кардан, бо маълумоти холӣ ва вайроншуда чӣ мешавад.