IlmHamroh
JavaScript Full-stack/12-qism. JavaScript: ilg'or mavzular va kod sifati40/44-dars21 daqiqa
Mundarija (41)

JavaScript tuzoqlari: kursda uchragan eng xavfli xatolar bitta ro'yxatda

Qisqacha: JavaScript tuzog'i — kod xato bermaydi, lekin jimgina noto'g'ri natija qaytaradi. Eng xavflilari: typeof null — "object", == turlarni yashirincha o'zgartiradi, "5" + 2 — "52", 0.1 + 0.2 — 0.30000000000000004, [10, 9, 1].sort() — [1, 10, 9], return dan keyin qator tugasa — undefined, metodni ajratib chaqirsangiz this yo'qoladi, forEach esa async callback'ni kutmaydi. Davo uchta: === va aniq o'girish (Number(...)), nusxa bilan ishlash (toSorted, structuredClone) va har tuzoqni ushlaydigan vosita — ESLint va test.

Bu darsda

  • 08–12-qismlarda uchragan eng xavfli tuzoqlarni bitta xaritada ko'rasiz va har birini qaysi darsda o'rganganingizni eslaysiz.
  • Har tuzoqni uch belgisi bilan taniysiz: g'alati natija, xato yo'qligi va "menda ishlayapti".
  • Har biri uchun bitta qisqa davo yozasiz.
  • Qaysi tuzoqlarni ESLint o'zi ushlashini haqiqiy chiqishda ko'rasiz.
  • vazifalar kodini tuzoqlar nuqtai nazaridan tekshirasiz.

Oldin bilishingiz kerak: Yashirin coercion qoidalari ichidan, sort va taqqoslash funksiyasi, this yo'qolishi va uni tiklash, Ketma-ket va parallel bajarish, ESLint va Prettier.

1. Nega bu kerak?

«Bahor»ning stajyor dasturchisi Sardor bir haftada uchta xato tuzatdi. Uchalasida ham konsolda qizil yozuv yo'q edi.

Birinchisi: savatdagi "2 ta osh" "21 ta" bo'lib qoldi. Forma maydonidan kelgan "2" satriga 1 qo'shilgan edi. Ikkinchisi: menyuni narx bo'yicha saralasa, ko'k choy (5 000) eng oxiriga tushdi. Uchinchisi: chegirma 0 so'm bo'lsa, tizim baribir 10 foiz chegirma berdi.

Uchala xatoni ham JavaScript "to'g'ri" bajardi. U Sardor yozgan narsani qildi, Sardor o'ylagan narsani emas. Bunday joylar tuzoq deyiladi. Tuzoq — tilning qonuniy, lekin kutilmagan xatti-harakati.

Bu tuzoqlarning hammasini kursda alohida darslarda ko'rgansiz. Lekin ular har xil qismlarda tarqoq yotibdi. Bu dars — ularni bitta "tibbiy ma'lumotnoma"ga yig'adi. Har tuzoq uchun uch narsa: belgisi (qanday ko'rinadi), sababi (nega bunday) va davosi (qanday yozish kerak). Batafsil tushuntirish esa havoladagi darsda turibdi.

Shifokor o'xshatishi: tajribali shifokor kasallikni birinchi belgisidan taniydi. U har safar kitobni boshidan o'qimaydi — belgilar ro'yxati uning boshida. Tajribali dasturchi ham "21" ni ko'rishi bilan "satr qo'shildi" deb o'ylaydi.

flowchart TD
  A["Natija g'alati, xato yo'q"] --> B{"Satr va son aralashdimi?"}
  B -- "ha" --> C["===, Number(), ??"]
  B -- "yo'q" --> D{"Massiv yoki obyekt o'zgardimi?"}
  D -- "ha" --> E["toSorted, spread, structuredClone"]
  D -- "yo'q" --> F{"Metod boshqa joyda chaqirildimi?"}
  F -- "ha" --> G["this: bind yoki arrow"]
  F -- "yo'q" --> H{"async bormi?"}
  H -- "ha" --> I["for...of + await, Promise.all"]
  H -- "yo'q" --> J["Kasr sonmi? Butun so'mda hisoblang"]

Bu diagramma — tez tashxis yo'li. Natija g'alati, lekin xato yo'q bo'lsa, savollarni yuqoridan pastga bering. Endi har guruhni alohida ko'ramiz.

2. Turlar va taqqoslash

2.1 typeof null — "object"

js
console.log(typeof null); // object
console.log(typeof []); // object
console.log(Array.isArray([])); // true

Belgisi: typeof qiymat === "object" tekshiruvidan null ham o'tib ketadi. Keyingi qatorda null.price — TypeError.

Sababi: JavaScript'ning birinchi versiyasidagi xato. Uni tuzatish millionlab saytlarni sindirardi, shuning uchun u abadiy qoldi (typeof va turni to'g'ri aniqlash).

Davosi: obyektni tekshirganda null ni alohida chiqaring: typeof x === "object" && x !== null. Massiv uchun — Array.isArray. vazifalar dagi paket.js aynan shunday tekshiradi.

2.2 == — yashirin o'girish bilan taqqoslash

js
console.log(0 == ""); // true
console.log("0" == false); // true
console.log(null == undefined); // true
console.log(0 === ""); // false

Belgisi: bo'sh maydon "" "nol" deb hisoblanadi, "0" esa "yolg'on".

Sababi: == turlari har xil bo'lsa, ikkala tomonni bir turga o'tkazadi. Qoidalari uzun va kutilmagan (Tenglik: ==, === va Object.is).

Davosi: har doim ===. Yagona qulay istisno — x == null: u null va undefined ni birga tekshiradi. Lekin jamoada bu istisnoga ruxsat bormi — ESLint sozlamasi hal qiladi.

2.3 + satrni yopishtiradi, - esa songa aylantiradi

js
const quantity = "2"; // forma maydonidan — har doim satr
console.log(quantity + 1); // 21
console.log(quantity - 1); // 1
console.log(Number(quantity) + 1); // 3
console.log("10" < "9"); // true

Belgisi: Sardorning "21 ta osh" xatosi. Ikkinchi belgi: ikki satr son kabi emas, lug'atdagi kabi taqqoslanadi. "10" < "9", chunki "1" belgisi "9" dan oldin keladi.

Sababi: + operatorida bir tomon satr bo'lsa, ikkinchisi ham satrga aylanadi va ular yopishtiriladi. -, *, / esa faqat sonlar bilan ishlaydi, shuning uchun satrni songa aylantiradi (Yashirin coercion qoidalari ichidan).

Davosi: tashqaridan kelgan qiymatni (forma, URL, dataset, JSON) kirishdayoq aniq o'giring: Number(...) (Turlarni aniq o'zgartirish). vazifalar da Number(nishon.closest("li").dataset.id) — shuning uchun. Usiz "4" === 4 — false bo'lardi va vazifa topilmasdi.

2.4 NaN o'ziga teng emas

js
console.log(NaN === NaN); // false
console.log(Number.isNaN(Number("osh"))); // true
console.log([NaN].includes(NaN)); // true
console.log([NaN].indexOf(NaN)); // -1

Belgisi: if (natija === NaN) hech qachon ishlamaydi.

Sababi: standart shunday deydi: NaN hech narsaga, hatto o'ziga ham teng emas (Number turi JS'da). indexOf === bilan qidiradi, includes esa NaN ni alohida biladi.

Davosi: Number.isNaN(x). Eski global isNaN("osh") ham true beradi — u avval satrni songa aylantiradi, shuning uchun noaniq.

2.5 || nolni "yo'q" deb biladi

js
const discount = 0; // admin ataylab 0 qo'ydi
console.log(discount || 10); // 10
console.log(discount ?? 10); // 0

Belgisi: Sardorning uchinchi xatosi. 0, "" yoki false qiymatlari "standart qiymat" bilan almashib ketadi.

Sababi: || har qanday falsy qiymatda o'ng tomonni oladi (Boolean va truthy/falsy qiymatlar). ?? esa faqat null va undefined da (??, ?. va mantiqiy tayinlash).

Davosi: "qiymat berilmagan bo'lsa" ma'nosida — doim ??.

Tekshirib ko'ring: const quantity = "3"; console.log(quantity * 2 + 1); nima chiqaradi?

Javob

7. Avval *: u faqat sonlar bilan ishlaydi, "3" — 3 ga aylanadi, 3 * 2 = 6. Keyin 6 + 1 — ikkalasi ham son, yopishtirish yo'q. Agar quantity + 1 bo'lganida — "31" chiqardi.

3. Sonlar

3.1 0.1 + 0.2

js
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
console.log((1.005).toFixed(2)); // 1.00
console.log(typeof (1.5).toFixed(2)); // string

Belgisi: chek oxirida 2000.3000000000002 so'm. Yoki 1.005 ni yaxlitlasangiz, 1.01 emas, 1.00 chiqadi.

Sababi: 0.1 ni ikkilik sanoq tizimida aniq yozib bo'lmaydi. Kompyuter unga eng yaqin sonni saqlaydi, 1.005 esa aslida 1.00499999… (Number turi JS'da). Ikkinchi tuzoq: toFixed satr qaytaradi. Unga son qo'shsangiz, yana + bilan yopishtirish boshlanadi.

Davosi: pulni butun sonda hisoblang — so'mda yoki tiyinda (Pul va aniq hisob-kitob). Kasr sonlarni === bilan emas, farq bilan solishtiring: Math.abs(a - b) < Number.EPSILON. Ko'rsatish uchun — Intl.NumberFormat (Intl.NumberFormat).

3.2 map(parseInt)

js
console.log(["1", "2", "3"].map(parseInt)); // [ 1, NaN, NaN ]
console.log(["1", "2", "3"].map(Number)); // [ 1, 2, 3 ]

Belgisi: birinchi element to'g'ri, qolganlari NaN.

Sababi: map callback'ga uchta argument beradi: element, indeks, massiv. parseInt ning ikkinchi parametri — sanoq tizimi. Natijada parseInt("2", 1) chaqiriladi: 1 lik sanoq tizimi yo'q, shuning uchun NaN (Callback imzosi va massivning HOF metodlari).

Davosi: callback'ni aniq yozing: .map((str) => parseInt(str, 10)) yoki shunchaki .map(Number).

4. Sintaksis: ASI

js
function getDishName() {
  return
    "Osh";
}
console.log(getDishName()); // undefined

Belgisi: funksiya qiymat qaytarishi kerak edi, lekin undefined qaytardi. Xato yo'q.

Sababi: ASI (Automatic Semicolon Insertion — nuqta-vergulni avtomatik qo'yish). return dan keyin qator tugasa, JavaScript u yerga yashirincha ; qo'yadi. "Osh" esa hech qachon bajarilmaydigan kodga aylanadi (Sintaksis asoslari, return va erta qaytish). Teskari tuzoq ham bor: [ yoki ( bilan boshlanadigan qator oldingi qatorga yopishib ketadi (Massiv destructuring).

Davosi: qaytariladigan qiymat return bilan bitta qatorda boshlansin. Uzun bo'lsa — qavs ochib qo'ying: return ( va keyingi qatorda qiymat. Prettier buni avtomatik shunday formatlaydi.

5. Massivlar va mutatsiya

5.1 sort() sonlarni satr kabi saralaydi va asl massivni o'zgartiradi

js
const prices = [35000, 5000, 28000];
console.log(prices.sort()); // [ 28000, 35000, 5000 ]
console.log(prices); // [ 28000, 35000, 5000 ]

const menu = [35000, 5000, 28000];
const sorted = menu.toSorted((a, b) => a - b);
console.log(sorted); // [ 5000, 28000, 35000 ]
console.log(menu); // [ 35000, 5000, 28000 ]

Belgisi: Sardorning ikkinchi xatosi — ko'k choy oxirida. Ikkinchi belgi: saralagandan keyin asl ro'yxat ham "aralashib" qoldi.

Sababi: taqqoslash funksiyasisiz sort har elementni satrga aylantiradi: "5000" "35000" dan keyin keladi, chunki "5" belgisi "3" dan keyin. Ustiga-ustak, sort massivni joyida o'zgartiradi (sort va taqqoslash funksiyasi).

Davosi: har doim taqqoslash funksiyasi bilan: sonlar — (a, b) => a - b, matn — a.localeCompare(b, "uz"). Asl massiv kerak bo'lsa — toSorted (Nusxa bilan o'zgartirish). vazifalar dagi royxat.js .toSorted(faollarOldin) ishlatadi.

5.2 Havola: "nusxa" aslini o'zgartirdi

js
const order = { dish: "Osh", extras: ["salat"] };
const copy = { ...order };
copy.dish = "Manti";
copy.extras.push("non");
console.log(order.dish); // Osh
console.log(order.extras); // [ 'salat', 'non' ]

Belgisi: bitta mijozning buyurtmasini o'zgartirdingiz — ikkinchisiniki ham o'zgardi.

Sababi: obyekt va massiv o'zgaruvchida havola sifatida saqlanadi (Havola semantikasi). Spread esa sayoz nusxa yasaydi: birinchi qavat yangi, ichidagi massiv — o'sha eski massiv (Sayoz va chuqur nusxalash).

Davosi: ichma-ich ma'lumotni yangilaganda har qavatni nusxalang (Ichma-ich ma'lumotni immutable yangilash) yoki butun nusxa uchun structuredClone(order).

5.3 for...in massivda

js
const dishes = ["osh", "manti"];
for (const i in dishes) {
  console.log(i + 1);
}

Konsolda:

text
01
11

Belgisi: "1, 2" kutgan edingiz, 01 va 11 chiqdi.

Sababi: for...in obyekt kalitlarini beradi, kalitlar esa doim satr: "0", "1". Keyin yana + bilan yopishtirish (for...of va for...in).

Davosi: massivda — for...of. Indeks kerak bo'lsa — for (const [i, dish] of dishes.entries()).

5.4 Emoji ikki "belgi"

js
console.log("👍".length); // 2
console.log([..."👍"].length); // 1

Izoh maydoniga "50 belgigacha" chegara qo'ydingiz. Emoji bilan yozgan mehmonga 25 ta emoji ham ko'p bo'lib qoladi. Sababi — length belgilarni emas, UTF-16 bo'laklarini sanaydi (Unicode va emoji). Davosi — [...text].length yoki Intl.Segmenter (Intl.Collator va Intl.Segmenter).

Tekshirib ko'ring: const a = [3, 20, 100]; const b = a.sort(); — a va b nima bo'ladi?

Javob

Ikkalasi ham [ 100, 20, 3 ] — va bu bitta massiv. sort satr bo'yicha saraladi ("1" < "2" < "3"), asl massivni o'zgartirdi va o'sha massivni qaytardi. b === a — true.

6. Funksiyalar va this

6.1 Metodni ajratib chaqirish

js
class Cashier {
  name = "Bahor";
  getName() {
    return this.name;
  }
}
const cashier = new Cashier();
const detached = cashier.getName;

console.log(cashier.getName()); // Bahor
try {
  detached();
} catch (error) {
  console.log(error.message);
}

Konsolda:

text
Bahor
Cannot read properties of undefined (reading 'name')

Tarjimasi: "undefined ning xususiyatlarini o'qib bo'lmaydi (name ni o'qishda)".

Belgisi: cashier.getName() ishlaydi, lekin funksiyani o'zgaruvchiga, callback'ga (setTimeout(cashier.getName, 100)) yoki tinglovchiga bersangiz — this yo'qoladi.

Sababi: this funksiya qayerda yozilganiga emas, qanday chaqirilganiga qarab aniqlanadi. detached() da nuqtadan oldin obyekt yo'q — this undefined (this: to'rt qoida, this yo'qolishi va uni tiklash).

Davosi: cashier.getName.bind(cashier) yoki arrow o'ram: () => cashier.getName(). vazifalar dagi api.js da shunga yaqin holat bor: obyekt.fetch(...) ko'rinishida chaqirsangiz, brauzer this ni tekshirib, Illegal invocation xatosini beradi. Shuning uchun u yerda oddiy fetchFn(url, sorov) chaqiruvi va bu haqda izoh turibdi.

6.2 var va sikldagi closure

js
const tasks = [];
for (var i = 1; i <= 3; i++) {
  tasks.push(() => i);
}
console.log(tasks.map((task) => task())); // [ 4, 4, 4 ]

Belgisi: uchta funksiya uchta har xil son qaytarishi kerak edi — hammasi 4.

Sababi: var blok scope'ga ega emas: uchala funksiya bitta i ni eslab qoladi. Ular chaqirilganda sikl tugagan, i esa 4 (Closure tuzoqlari).

Davosi: let — har aylanishda yangi i. Natija [ 1, 2, 3 ]. Umuman, var ni yangi kodda ishlatmang (var va nega undan voz kechildi).

Tekshirib ko'ring: button.addEventListener("click", cashier.getName) — tugma bosilganda this cashier bo'ladimi?

Javob

Yo'q. Metod ajratib berildi: brauzer uni cashier. siz chaqiradi. Tinglovchi ichida this — tugmaning o'zi bo'ladi. this.name xato ham bermaydi: tugmaning o'z name xususiyati bor (odatda bo'sh satr ""). Natija — "Bahor" o'rniga jim bo'sh satr, ya'ni yana tuzoq. Davosi o'sha: () => cashier.getName() yoki bind(cashier).

7. Asinxron tuzoqlar

7.1 forEach async callback'ni kutmaydi

js
const wait = (ms) => new Promise((done) => setTimeout(done, ms));

async function prepare(dishes) {
  const ready = [];
  dishes.forEach(async (dish) => {
    await wait(50);
    ready.push(dish);
  });
  console.log("tayyor:", ready.length);
}

await prepare(["osh", "manti"]);

Konsolda:

text
tayyor: 0

Belgisi: "hammasi tayyor" xabari ish tugashidan oldin chiqadi. Hisob 0, ro'yxat bo'sh.

Sababi: forEach callback qaytargan Promise'ga qaramaydi. U callback'ni chaqiradi va darhol keyingisiga o'tadi (Ketma-ket va parallel bajarish).

Davosi: ketma-ket kerak bo'lsa — for...of ichida await. Parallel kerak bo'lsa — await Promise.all(dishes.map(async (dish) => …)):

js
const wait = (ms) => new Promise((done) => setTimeout(done, ms));

async function prepare(dishes) {
  const ready = [];
  for (const dish of dishes) {
    await wait(50);
    ready.push(dish);
  }
  console.log("tayyor:", ready.length);
}

await prepare(["osh", "manti"]); // tayyor: 2

7.2 Unutilgan await

saqla(royxat) deb yozdingiz, await saqla(royxat) emas. Kod keyingi qatorga o'tib ketadi, xato bo'lsa — hech kim ushlamaydi. Bu suzib yurgan Promise (floating promise) — async / await darsida ko'rgan edik. Belgisi: konsolda Uncaught (in promise) yoki Node'da jarayon qulashi. Davosi: har Promise'ni await qiling, return qiling yoki .catch bering. Ataylab kutmasangiz — izohda nega ekanini yozing.

8. 12-qismning o'z tuzoqlari

8.1 g flagli regex xotirali

js
const PHONE = /\+998/g;
const notes = ["+998 90", "+998 91", "+998 93"];
console.log(notes.filter((text) => PHONE.test(text)));
// [ '+998 90', '+998 93' ]

Belgisi: uchala izohda ham telefon bor, lekin ikkitasi topildi. Bir chaqiruvda true, keyingisida xuddi shu matnga false.

Sababi: g flagli regex lastIndex da qayerda to'xtaganini eslab qoladi. Keyingi test o'sha joydan qidiradi (Flaglar va lastIndex tuzog'i).

Davosi: "bormi?" savoli uchun g qo'ymang. Hamma moslik kerak bo'lsa — match yoki matchAll.

8.2 Oy 0 dan boshlanadi

js
const booking = new Date(2026, 10, 5); // "5-oktabr" deb yozilgan edi
console.log(booking.toDateString()); // Thu Nov 05 2026

Belgisi: bron bir oy keyinga tushdi.

Sababi: new Date(yil, oy, kun) da oy 0 dan sanaladi: 0 — yanvar, 9 — oktabr (Date asoslari). Yana ikki qarindosh tuzoq: "2026-10-05" satri UTC deb o'qiladi, setterlar esa sanani joyida o'zgartiradi (Date va vaqt zonalari).

Davosi: sanani ISO satridan, vaqt va zona bilan yarating: new Date("2026-10-05T12:00:00+05:00"). Yangi loyihalarda — Temporal yoki sinalgan kutubxona (Temporal API va sana kutubxonalari).

8.3 JSON.stringify jimgina tashlab yuboradi

js
const todo = { text: "Osh", deadline: undefined, score: NaN };
console.log(JSON.stringify(todo)); // {"text":"Osh","score":null}

undefined xususiyati yo'qoldi, NaN esa null bo'ldi. Serverga yuborilgan ma'lumot siz o'ylagandek emas (JSON asoslari, JSON chuqur).

Davosi: yuborishdan oldin qiymatni tekshiring (Number.isNaN), "yo'q" qiymat kerak bo'lsa — undefined emas, null yozing: u JSON'da saqlanadi.

Tekshirib ko'ring: const re = /osh/g; console.log(re.test("osh"), re.test("osh")); nima chiqaradi va nega?

Javob

true false. Birinchi test moslik topdi va lastIndex ni 3 ga surdi. Ikkinchi test 3-o'rindan qidirdi — u yerda hech narsa yo'q, false qaytardi va lastIndex ni 0 ga qaytardi. Davosi: "bormi?" savoli uchun g siz /osh/.

9. Hammasi bitta jadvalda

Tuzoq Belgisi Davosi
typeof null null "obyekt" bo'lib o'tadi x !== null, Array.isArray
==, + "21", 0 == "" ===, Number(...) kirishda
NaN === NaN ishlamaydi Number.isNaN
|| 0 almashib ketadi ??
0.1 + 0.2 …0000004, toFixed satr butun so'm, Intl.NumberFormat
map(parseInt) [1, NaN, NaN] map(Number)
ASI undefined qaytdi return bilan bir qatorda
sort() 5000 oxirida, asl o'zgardi toSorted((a, b) => a - b)
Havola nusxa aslini o'zgartirdi spread har qavatda, structuredClone
this undefined ning xususiyati bind, arrow o'ram
forEach + async "tayyor" erta chiqdi for...of, Promise.all
g + test goh true, goh false test uchun g siz
Oy bir oy siljidi ISO satr, Temporal

Jadvalni eslab qolish shart emas. Muhimi — g'alati natijani ko'rganda "bu tuzoqlardan biri bo'lishi mumkin" deb o'ylash. Keyin shu jadvalga qaytasiz.

10. Tuzoqlarni vositalarga topshiring

Hamma tuzoqni boshda ushlab turish qiyin. Yaxshi xabar: ularning yarmini ESLint siz uchun qidiradi. Quyidagi faylda beshta tuzoq bor. Biz uni vazifalar dagi ESLint 10.12.0 va @eslint/js 10.0.1 bilan, recommended + ikki qo'shimcha qoida (eqeqeq va require-atomic-updates) bilan tekshirdik:

js
// tuzoq.js
export function getPrice(dish) {
  return
    dish.price;
}

export function checkType(value) {
  if (value == null) return false;
  return typeof value === "array";
}

export function check(num) {
  return num === NaN;
}

export const tooBig = 9007199254740993;

let stock = 3;
export async function book(wait) {
  stock = stock - (await wait());
}

npx eslint tuzoq.js ning haqiqiy chiqishi:

text
   4:5   error  Unreachable code                                                                            no-unreachable
   8:13  error  Expected '===' and instead saw '=='                                                         eqeqeq
   9:27  error  Invalid typeof comparison value                                                             valid-typeof
  13:10  error  Use the isNaN function to compare with NaN                                                  use-isnan
  16:23  error  This number literal will lose precision at runtime                                          no-loss-of-precision
  20:3   error  Possible race condition: `stock` might be reassigned based on an outdated value of `stock`  require-atomic-updates

✖ 6 problems (6 errors, 0 warnings)

Qatorma-qator tarjima:

  • Unreachable code — "yetib bo'lmaydigan kod": ASI tuzog'i, dish.price hech qachon bajarilmaydi.
  • Expected '===' and instead saw '==' — "=== kutilgan edi, == ko'rildi".
  • Invalid typeof comparison value — "typeof bilan noto'g'ri qiymat solishtirilmoqda": typeof hech qachon "array" qaytarmaydi.
  • Use the isNaN function to compare with NaN — "NaN bilan solishtirish uchun isNaN ishlating".
  • This number literal will lose precision at runtime — "bu son ishlash paytida aniqligini yo'qotadi": Number.MAX_SAFE_INTEGER (9 007 199 254 740 991) dan katta, oxirgi raqami yo'qoladi (Number turi JS'da).
  • Possible race condition — "poyga holati bo'lishi mumkin": stock eski qiymat asosida qayta yozilmoqda. Bu tuzoqni keyingi darslardan birida — Nozik xatolar da — o'zi takrorlab ko'ramiz.

E'tibor bering: sort(), this, forEach + async va havola tuzoqlarini ESLint'ning standart qoidalari ushlamaydi. Ular uchun — test (Birinchi avtomatik test) va JSDoc bilan tur tekshiruvi (JSDoc va // @ts-check). TypeScript bilan ishlaydigan typescript-eslint qoidalari esa forEach ga async callback berilganini ham ko'rsatadi — buni 15-qismda ko'ramiz.

11. Ko'p uchraydigan xatolar

11.1 Tuzoqni "JavaScript'ning xatosi" deb tashlab qo'yish

"Bu JavaScript'ning g'alati joyi" — va kod o'sha holda qoladi.

G'alati joy — sizning kodingizdagi xato. Til o'zgarmaydi, kodni o'zgartirasiz: aniq o'girish, ===, nusxa.

11.2 Belgini davolash, sababni emas

"21" chiqdi — parseInt(total) bilan "to'g'rilash". 0.30000000000000004 — toFixed(2) bilan yashirish.

Sabab joyida tuzating: satrni maydondan o'qiganda songa aylantiring, pulni boshidan butun so'mda hisoblang.

11.3 Bitta tuzoqni bilib, qarindoshini unutish

sort ga funksiya berdingiz, lekin u hamon asl massivni o'zgartiradi. Shu massivni boshqa joy ham ishlatayotgan bo'lsa (masalan, ekrandagi asl tartib), u ham buziladi. React kabi freymvorklarda (17-qismda) bu holat yangilanmay qolishiga olib keladi.

Har tuzoqning ikki tomoni bor: sort — satr bo'yicha va joyida; Date — oy 0 dan va setter joyida.

12. Mashqlar

1-mashq (oson): Natijani ayting

Har qator nima chiqaradi? Avval o'ylang, keyin yozing.

  • console.log("7" + 3) →
  • console.log("7" - 3) →
  • console.log(typeof null) →
  • console.log([1, 2, 10].sort().at(-1)) →
Yechim

"73" — + da bir tomon satr, yopishtirish. 4 — - songa aylantiradi. "object" — tarixiy xato. 2 — taqqoslash funksiyasisiz satr bo'yicha saralanadi: [ 1, 10, 2 ], chunki "10" "2" dan oldin keladi ("1" < "2"). Oxirgisi — 2.

2-mashq (o'rta): Uchta tuzoqni toping

Funksiya savat jamini hisoblashi kerak. discount — so'mda, 0 bo'lishi mumkin. Kodda uchta tuzoq bor. Ularni toping va tuzating. Ishora: «Turlar va taqqoslash» bo'limidagi forma qiymatlari va ||, «Massivlar va mutatsiya» bo'limidagi sort.

js
function calculateTotal(cart, discount) {
  const prices = cart.map((row) => row.price * row.quantity);
  const maxPrice = prices.sort().at(-1);
  const total = prices.reduce((sum, n) => sum + n, 0);
  return { total: total - (discount || 5000), maxPrice };
}

const cart = [
  { price: 35000, quantity: "2" },
  { price: 5000, quantity: "3" },
];
console.log(calculateTotal(cart, "0"));
// { total: 85000, maxPrice: 70000 }
console.log(calculateTotal(cart, 0));
// { total: 80000, maxPrice: 70000 }

Birinchi chaqiruv to'g'ri, ikkinchisi — 5 000 so'm ortiqcha chegirma. Nega?

Yechim
  1. discount || 5000: son 0 falsy, shuning uchun 5 000 chegirma berildi. Forma maydonidan kelgan "0" esa bo'sh bo'lmagan satr — truthy, shuning uchun birinchi chaqiruv tasodifan to'g'ri ishladi. Davo: kirishda Number(discount) va "berilmagan bo'lsa" ma'nosida — undefined tekshiruvi.
  2. row.quantity — satr. 35000 * "2" tasodifan ishladi (* songa aylantiradi), lekin kimdir keyin + bilan qo'shsa, buziladi. Davo: kirishda Number(row.quantity).
  3. prices.sort() — satr bo'yicha va joyida. Bu yerda tasodifan to'g'ri ("15000" < "70000"), lekin [9000, 70000] da 9 000 "eng qimmat" chiqardi. Davo: Math.max(...prices).
js
function calculateTotal(cart, discount) {
  const prices = cart.map(
    (row) => row.price * Number(row.quantity),
  );
  const maxPrice = Math.max(...prices);
  const total = prices.reduce((sum, n) => sum + n, 0);
  const discountAmount =
    discount === undefined ? 5000 : Number(discount);
  return { total: total - discountAmount, maxPrice };
}

const cart = [
  { price: 35000, quantity: "2" },
  { price: 5000, quantity: "3" },
];
console.log(calculateTotal(cart, "0"));
// { total: 85000, maxPrice: 70000 }
console.log(calculateTotal(cart, 0));
// { total: 85000, maxPrice: 70000 }
console.log(calculateTotal(cart));
// { total: 80000, maxPrice: 70000 }

Nega bu yerda ?? emas? Number(undefined) — NaN, ?? esa NaN ni "yo'q" deb bilmaydi. Shuning uchun avval undefined ni tekshirdik, keyin o'girdik. Tuzoqlarning eng yomon tomoni ham shu mashqda ko'rindi: siz sinagan holatda ("0") kod ishlaydi.

3-mashq (qiyin): this va forEach ni tuzating

Oshxona klassi buyurtmalarni ketma-ket tayyorlaydi. Kod tayyor: 0 chiqaradi va bitta joyda this yo'qoladi. Ikkala tuzoqni tuzating, natija tayyor: 2 bo'lsin. Ishora: «Metodni ajratib chaqirish» va «forEach async callback'ni kutmaydi» bo'limlari.

js
class Kitchen {
  readyDishes = [];
  async prepare(dish) {
    await new Promise((done) => setTimeout(done, 20));
    this.readyDishes.push(dish);
  }
  async prepareAll(dishes) {
    dishes.forEach(this.prepare);
    console.log("tayyor:", this.readyDishes.length);
  }
}
await new Kitchen().prepareAll(["osh", "manti"]);
Yechim

Ikki tuzoq: forEach(this.prepare) metodni ajratib beradi (this — undefined, 20 ms dan keyin TypeError), va forEach Promise'larni kutmaydi.

js
class Kitchen {
  readyDishes = [];
  async prepare(dish) {
    await new Promise((done) => setTimeout(done, 20));
    this.readyDishes.push(dish);
  }
  async prepareAll(dishes) {
    for (const dish of dishes) {
      await this.prepare(dish); // nuqta bilan — this joyida
    }
    console.log("tayyor:", this.readyDishes.length);
  }
}
await new Kitchen().prepareAll(["osh", "manti"]);
// tayyor: 2

Parallel varianti: await Promise.all(dishes.map((dish) => this.prepare(dish))) — arrow this ni tashqi metoddan oladi.

4-mashq: Vazifalar qadami — tuzoqlar auditi

vazifalar v3.1 kodini o'zgartirmaymiz — uni tekshiramiz. assets/js/ dagi fayllarda quyidagilarni qidiring (VS Code'da Ctrl+Shift+F) va har biriga bir gapli javob yozing: kod bu tuzoqdan qanday qochgan?

  1. sort — qayerda saralanadi va qanday?
  2. dataset.id — satrmi yoki sonmi, qayerda o'giriladi?
  3. == — bormi?
  4. typeof … === "object" — null hisobga olinganmi?
  5. forEach — async callback bilanmi?

Javoblarni kurs/mashqlar/12/40-tuzoqlar/audit.md ga yozing.

Yechim
  1. royxat.js: .toSorted(faollarOldin) — nusxa bilan va taqqoslash funksiyasi bilan. faollarOldin mantiqiy qiymatlarni aniq songa aylantiradi: Number(a.bajarildi) - Number(b.bajarildi).
  2. dataset qiymati doim satr. asosiy.js va render.js da Number(...closest("li").dataset.id) — kirishdayoq songa. Usiz top(id) dagi === hech narsa topmasdi.
  3. Yo'q — hamma joyda === va !==. Buni #38 dagi ESLint recommended ta'minlamaydi (eqeqeq unda yo'q), bu — kod uslubi.
  4. Ha: holat.js va paket.js da typeof qiymat === "object" && qiymat !== null.
  5. Yo'q: paket.js va render.js dagi forEach callback'lari sinxron. Asinxron ishlar await bilan ketma-ket (sinxronla, serverdaBajar).
bash
git add 12/40-tuzoqlar/audit.md
git commit -m "12/40: vazifalar tuzoqlar auditi"

13. Real ishda

  • Kod review. Tajribali dasturchilar PR'da aynan shu jadvaldagi joylarni ko'zdan kechiradi: forma qiymati qayerda songa aylanadi? sort funksiyasiz emasmi? forEach ichida await yo'qmi?
  • Linter sozlamasi. Ko'p jamoalar eqeqeq, no-implicit-coercion, require-atomic-updates kabi qoidalarni recommended ga qo'shadi. TypeScript'ga o'tgach, no-floating-promises va no-misused-promises (typescript-eslint) qo'shiladi — 15-qismda.
  • Intervyu. "typeof null nima?", "[10, 9, 1].sort() nima qaytaradi?", "0.1 + 0.2 === 0.3 nega false?", "this qachon yo'qoladi?" — boshlang'ich darajadagi JavaScript intervyusining eng ko'p so'raladigan savollari. Javobning o'zi emas, sababi va davosi baholanadi.

Xulosa

  • Tuzoq — xato bermaydigan, lekin jimgina noto'g'ri natija: belgisi g'alati qiymat va "menda ishlayapti".
  • Turlar: ===, tashqi qiymatni kirishda Number(...), standart qiymat uchun ??, NaN uchun Number.isNaN.
  • Sonlar: pul — butun so'mda; toFixed satr qaytaradi; map(parseInt) emas, map(Number).
  • Massiv va obyekt: sort — funksiya bilan va toSorted; spread sayoz; for...in massivda emas.
  • Funksiya va asinxron: metodni ajratsangiz this yo'qoladi; forEach await ni kutmaydi; var sikl closure'ida bitta.
  • ESLint ==, ASI, typeof xatosi, NaN va poyga holatining bir turini ushlaydi; qolganini test ushlaydi.

Keyingi dars: JS versiyalari va brauzer mosligi — yangi imkoniyatni ishlatishdan oldin u qaysi brauzerlarda ishlashini tekshirish, Baseline va polyfill.

Manbalar

  • MDN: "Equality comparisons and sameness", "Type coercion", Array.prototype.sort(), this — developer.mozilla.org
  • ESLint qoidalari: eqeqeq, no-unreachable, valid-typeof, use-isnan, no-loss-of-precision, require-atomic-updates — eslint.org/docs/latest/rules
  • ECMAScript 2026 (ECMA-262, 17-nashr), "Abstract Equality Comparison" (IsLooselyEqual) — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript tuzoqlari: kursda uchragan eng xavfli xatolar bitta ro'yxatda — IlmHamroh