កំហុស និងការ Debug

មាតិកា

  1. កំហុសគឺជាព័ត៌មាន
  2. ការអានសារកំហុស
  3. កំហុសបួនដែលអ្នកនឹងជួបញឹកញាប់បំផុត
  4. try, catch, និង finally
  5. ការ Throw កំហុសផ្ទាល់ខ្លួន
  6. គម្រោង៖ Validate ទម្រង់ទំនាក់ទំនង
  7. ការ Debug ជាមួយ console
  8. DevTools Debugger
  9. Bug ដែលអ្នកចាប់ផ្តើមតែងជួប
  10. សាកល្បងដោយខ្លួនឯង

1. កំហុសគឺជាព័ត៌មាន

អ្នកសរសេរកម្មវិធីគ្រប់គ្នាឃើញកំហុសរាល់ថ្ងៃ រួមទាំងអ្នកដែលសរសេរកូដអស់ម្ភៃឆ្នាំផង។ កំហុសមិនមែនជាសញ្ញាបង្ហាញថាអ្នកអន់ខាងសរសេរកម្មវិធីទេ។ វាគឺជាកុំព្យូទ័របង្ហាញអ្នកឲ្យច្បាស់ថាវាមិនអាចធ្វើអ្វី ហើយជាទូទៅក៏បង្ហាញថានៅត្រង់ណាដែរ។

ជំនាញដែលត្រូវបណ្តុះក្នុងជំពូកនេះគឺការអានដោយស្ងប់ស្ងាត់។ ពេលមានអ្វីខូច កុំស្មាន ហើយកុំកែបន្ទាត់ដោយចៃដន្យ។ សូមអានសារ រកបន្ទាត់ ហើយសួរថា៖ តើខ្ញុំរំពឹងអ្វី ហើយតើមានអ្វីកើតឡើងជំនួសវិញ?

បញ្ហាមានពីរប្រភេទ៖

ប្រភេទពេលណាវាលេចឡើងឧទាហរណ៍
Error (exception)កម្មវិធីឈប់ ហើយ engine រាយការណ៍ប្រើ variable ដែលមិនមាន
Bug (logic error)កម្មវិធីដំណើរការ ប៉ុន្តែផ្តល់ចម្លើយខុសផលបូកខុសមួយឯកតា

Error ងាយស្រួល ព្រោះកុំព្យូទ័របង្ហាញវាឲ្យអ្នក។ Bug ពិបាកជាង ព្រោះគ្មានអ្វីត្អូញត្អែរទេ។ ទាំងពីរត្រូវបានដោះស្រាយដោយឧបករណ៍ខាងក្រោម។

2. ការអានសារកំហុស

បង្កើតឯកសារ broken.js ហើយដំណើរការវាដោយ node broken.js៖

function showTotal(prices) {
  return prices.reduce((sum, price) => sum + price, 0);
}

console.log(showTotal(undefined));

Node បោះពុម្ពអ្វីមួយដូចតទៅ (path នឹងខុសគ្នានៅលើកុំព្យូទ័ររបស់អ្នក)៖

/home/dara/broken.js:2
  return prices.reduce((sum, price) => sum + price, 0);
                ^

TypeError: Cannot read properties of undefined (reading 'reduce')
    at showTotal (/home/dara/broken.js:2:17)
    at Object.<anonymous> (/home/dara/broken.js:5:13)

វាមានបួនផ្នែក៖

  1. ទីតាំង៖ ឯកសារ broken.js បន្ទាត់ទី 2 ជាមួយសញ្ញា ^ ខាងក្រោមកន្លែងមានបញ្ហា។
  2. ប្រភេទ៖ TypeError។
  3. សារ៖ Cannot read properties of undefined (reading 'reduce')។ សូមអានវាជាប្រយោគ៖ "ខ្ញុំព្យាយាមអាន .reduce ពីអ្វីមួយដែលជា undefined។"
  4. Stack trace៖ បញ្ជីនៃការហៅ function ដែលនាំមកដល់ទីនេះ។

Stack trace អានពី ថ្មីបំផុតទៅចាស់បំផុត។ បន្ទាត់ខាងលើគឺកន្លែងដែលកំហុសកើតឡើង (ក្នុង showTotal បន្ទាត់ទី 2)។ បន្ទាត់បន្ទាប់គឺអ្នកដែលហៅវា (កូដកម្រិតកំពូល បន្ទាត់ទី 5)។ ដើម្បីរកមូលហេតុពិត សូមមើលការហៅនៅបន្ទាត់ទី 5៖ វាបានបញ្ជូន undefined។ កំហុសបានលេចឡើងនៅបន្ទាត់ទី 2 ប៉ុន្តែការខុសឆ្គងគឺនៅបន្ទាត់ទី 5។

នៅក្នុង browser ព័ត៌មានដដែលនេះលេចឡើងជាពណ៌ក្រហមក្នុង panel Console ហើយរាល់បន្ទាត់នៃ trace គឺជាតំណដែលបើកកូដ។

3. កំហុសបួនដែលអ្នកនឹងជួបញឹកញាប់បំផុត

JavaScript មានប្រភេទ error ដែលភ្ជាប់មកជាមួយច្រើន។ បួននេះគ្របដណ្តប់ភាគច្រើននៃអ្វីដែលអ្នកចាប់ផ្តើមឃើញ៖

Errorអត្ថន័យមូលហេតុទូទៅ
SyntaxErrorកូដមិនមែនជា JavaScript ត្រឹមត្រូវខ្វះវង់ក្រចក, = លើស, JSON ខុស
ReferenceErrorឈ្មោះមួយមិនមានវាយឈ្មោះ variable ខុស ឬប្រើវានៅក្រៅ scope របស់វា
TypeErrorតម្លៃមិនអាចប្រើបែបនោះបានហៅអ្វីដែលមិនមែនជា function, អាន property របស់ null ឬ undefined
RangeErrorលេខនៅក្រៅចន្លោះដែលអនុញ្ញាតnew Array(-1), recursion មិនចេះចប់

នេះគឺមួយសម្រាប់ប្រភេទនីមួយៗ៖

// SyntaxError: Unexpected token '='   (ឯកសារនឹងមិនដំណើរការទាល់តែសោះ)
const = 5;

// ReferenceError: username is not defined
console.log(username);

// TypeError: Cannot read properties of null (reading 'name')
const user = null;
console.log(user.name);

// RangeError: Invalid array length
const items = new Array(-1);

SyntaxError មានលក្ខណៈពិសេស៖ JavaScript អានឯកសារទាំងមូលមុននឹងដំណើរការ ដូច្នេះ syntax error បញ្ឈប់អ្វីៗទាំងអស់ ហើយគ្មានបន្ទាត់ណាដំណើរការទេ។ បីផ្សេងទៀតកើតឡើង ពេល កម្មវិធីកំពុងដំណើរការ ដូច្នេះបន្ទាត់មុនៗអាចត្រូវបានដំណើរការរួចហើយ។

TypeError: Cannot read properties of undefined គឺជា error ដែលជួបញឹកញាប់បំផុតនៅលើ web។ វាមានន័យថាតម្លៃដែលអ្នករំពឹងថាមាន បែរជាបាត់។ នៅក្នុង Array និង Object អ្នកបានឃើញ optional chaining, user?.name ដែលត្រឡប់ undefined ជំនួសឲ្យការ throw។

4. try, catch, និង finally

ពេលខ្លះ error ត្រូវបានរំពឹងទុក។ អ្នកប្រើអាចវាយទិន្នន័យខុស ឬឯកសារអាចបាត់។ ជំនួសឲ្យការទុកឲ្យកម្មវិធីឈប់ អ្នកអាច catch error បាន៖

try {
  const data = JSON.parse("{bad json");
  console.log(data);
} catch (error) {
  console.log("Could not read the data.");
  console.log(error.name, "-", error.message);
} finally {
  console.log("Done.");
}

លទ្ធផលគឺ៖

Could not read the data.
SyntaxError - Expected property name or '}' in JSON at position 1 (line 1 column 2)
Done.
  • កូដក្នុង try ដំណើរការធម្មតា។ ប្រសិនបើមានអ្វី throw JavaScript នឹងលោតត្រង់ទៅ catch ដោយរំលងកូដដែលនៅសល់ក្នុង block try។
  • catch (error) ទទួល error object។ វាមាន name, message និង stack។
  • finally ដំណើរការរាល់ពេល ទោះមាន error ឬអត់។ ប្រើវាសម្រាប់ការសម្អាត ដូចជាលាក់សារ "Loading..."។

ចំណាំមួយ៖ កុំប្រើ catch ទទេដែលលាក់ error។ catch {} ធ្វើឲ្យ bug មើលមិនឃើញ ហើយពិបាករកខ្លាំងជាងមុន។ សូម catch តែអ្វីដែលអ្នកអាចដោះស្រាយបាន ហើយរាយការណ៍ ឬ throw ឡើងវិញនូវអ្វីដែលនៅសល់។

try/catch ក៏ដំណើរការជាមួយ async/await ដែរ។ នៅក្នុង Asynchronous JavaScript await fetch(...) ដែលបរាជ័យអាចត្រូវបាន catch តាមរបៀបដូចគ្នា។

5. ការ Throw កំហុសផ្ទាល់ខ្លួន

អ្នកអាចបង្កើត error ដោយខ្លួនឯងជាមួយ throw។ នេះជារបៀបដែល function និយាយថា "ខ្ញុំមិនអាចបន្តជាមួយ input នេះបានទេ"៖

function setAge(age) {
  if (typeof age !== "number" || age < 0) {
    throw new Error("Age must be a number, 0 or higher.");
  }
  return age;
}

ត្រូវ throw Error object ជានិច្ច (new Error("...")) មិនមែន string ទេ។ Error object កត់ត្រា stack trace ចំណែក string មិនកត់ត្រាទេ។

សូមសរសេរសារដែលជួយអ្នកអាន។ ប្រៀបធៀប៖

សារមិនល្អសារប្រសើរជាង
"Invalid""Email must contain an @ sign."
"Error 5""Project title is required."

ប្រសិនបើអ្នកត្រូវការញែកបញ្ហាផ្សេងៗពីគ្នា សូមបង្កើតប្រភេទ error ផ្ទាល់ខ្លួនដោយ extend Error៖

class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

ឥឡូវ catch អាចពិនិត្យ error instanceof ValidationError ហើយចាត់ទុកកំហុសរបស់អ្នកប្រើខុសពី bug ពិតប្រាកដ។

6. គម្រោង៖ Validate ទម្រង់ទំនាក់ទំនង

ទំព័ររបស់ Dara មានទម្រង់ទំនាក់ទំនងពីជំពូក HTML។ Browser ពិនិត្យ required និង type="email" រួចហើយ ប៉ុន្តែអ្នកមិនគួរទុកចិត្តតែវាម្យ៉ាងទេ ហើយ script អនុញ្ញាតឲ្យអ្នកបង្ហាញសារដែលរួសរាយជាង។

ដំបូង តក្កវិជ្ជា validate ជា function ធម្មតា។ វា throw ValidationError សម្រាប់បញ្ហាដំបូងដែលវារកឃើញ៖

class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

function validateContact({ name, email, message }) {
  if (name.trim() === "") {
    throw new ValidationError("name", "Please enter your name.");
  }
  if (!email.includes("@") || !email.includes(".")) {
    throw new ValidationError("email", "Please enter a valid email address.");
  }
  if (message.trim().length < 10) {
    throw new ValidationError("message", "Your message must be at least 10 characters.");
  }
}

បន្ទាប់មកភ្ជាប់វាទៅទំព័រ។ នេះសន្មតថាទម្រង់មាន id="contact-form" ជាមួយ field ឈ្មោះ name, email និង message ហើយមាន <p id="form-error"> សម្រាប់បង្ហាញសារ៖

const form = document.querySelector("#contact-form");
const errorBox = document.querySelector("#form-error");

form.addEventListener("submit", (event) => {
  event.preventDefault();

  const data = {
    name: form.elements.name.value,
    email: form.elements.email.value,
    message: form.elements.message.value,
  };

  try {
    validateContact(data);
    errorBox.textContent = "Thank you! Your message looks good.";
    form.reset();
  } catch (error) {
    if (error instanceof ValidationError) {
      errorBox.textContent = error.message;
      form.elements[error.field].focus();
    } else {
      throw error;
    }
  } finally {
    console.log("Form checked at", new Date().toLocaleTimeString());
  }
});

សូមកត់សម្គាល់ else { throw error; }។ ប្រសិនបើ error មិនមែនជា ValidationError វាគឺជា bug ពិតប្រាកដ ដូច្នេះយើងទុកឲ្យវាបន្តទៅមុខ ជំនួសឲ្យការលាក់វា។ ដោយសារ site នេះគ្មាន backend ទម្រង់នេះមិនទាន់ផ្ញើអ្វីទេ។ ការផ្ញើត្រូវការ server ដែលនៅក្រៅស៊េរីនេះ។

7. ការ Debug ជាមួយ console

ឧបករណ៍ debug ដែលសាមញ្ញបំផុតគឺការបោះពុម្ពតម្លៃ។ អ្នកបានប្រើ console.log រួចហើយ ប៉ុន្តែ console មាន method ច្រើនទៀត៖

Methodប្រើសម្រាប់
console.log(x)លទ្ធផលទូទៅ
console.warn(x)អ្វីមួយគួរឲ្យសង្ស័យ (ពណ៌លឿង)
console.error(x)បញ្ហាមួយ (ពណ៌ក្រហម ជាមួយ stack trace)
console.table(array)array នៃ object ជាតារាង
console.group("name") / console.groupEnd()ចូលបន្ទាត់សារដែលទាក់ទងគ្នា
console.time("t") / console.timeEnd("t")វាស់រយៈពេលដែលកូដត្រូវការ
const projects = [
  { title: "Portfolio", tag: "html" },
  { title: "Quiz App", tag: "javascript" },
];

console.table(projects);
console.log("filtering by", "css");
console.log({ tag: "css" });

គន្លឹះ៖ ដាក់ variable ក្នុងដង្កៀបអង្កាញ់ console.log({ tag })។ វាបោះពុម្ពទាំងឈ្មោះ និងតម្លៃជាមួយគ្នា ដូច្នេះអ្នកដឹងជានិច្ចថាបន្ទាត់ណាបង្កើតលទ្ធផលណា។

ការបោះពុម្ពរហ័ស ប៉ុន្តែមានដែនកំណត់។ អ្នកត្រូវស្មានថាត្រូវបោះពុម្ពត្រង់ណា កែឯកសារ ហើយ reload។ សម្រាប់បញ្ហាពិបាកជាងនេះ សូមប្រើ debugger។

8. DevTools Debugger

Debugger ផ្អាកកម្មវិធីរបស់អ្នកនៅពាក់កណ្តាលផ្លូវ ដើម្បីឲ្យអ្នកមើលរាល់ variable ហើយដើរម្តងមួយជំហាន។

  1. បើក DevTools (F12) ហើយជ្រើស panel Sources (Chrome/Edge) ឬ Debugger (Firefox)។
  2. បើកឯកសារ script របស់អ្នកក្នុងបញ្ជីឯកសារ។
  3. ចុច លេខបន្ទាត់ ដើម្បីកំណត់ breakpoint។ សញ្ញាមួយលេចឡើង។
  4. ធ្វើឲ្យកូដដំណើរការ (ឧទាហរណ៍ submit ទម្រង់)។

ទំព័រនឹងឈប់នៅបន្ទាត់នោះ មុនពេលវាដំណើរការ។ ឥឡូវអ្នកអាច៖

ការគ្រប់គ្រងអ្វីដែលវាធ្វើ
Resume (F8)ដំណើរការរហូតដល់ breakpoint បន្ទាប់
Step over (F10)ដំណើរការបន្ទាត់នេះ ហើយឈប់នៅបន្ទាត់បន្ទាប់
Step into (F11)ចូលទៅក្នុង function ដែលកំពុងត្រូវបានហៅ
Step out (Shift+F11)បញ្ចប់ function នេះ ហើយត្រឡប់ទៅអ្នកហៅ

ពេលផ្អាក pane Scope រាយ variable ទាំងអស់ និងតម្លៃបច្ចុប្បន្នរបស់វា។ ដាក់ mouse លើ variable ក្នុងកូដដើម្បីមើលវា។ pane Call Stack គឺជា stack trace ដដែលដែលអ្នកបានឃើញពីមុន ប៉ុន្តែឥឡូវអាចចុចបាន។

អ្នកក៏អាចសរសេរការផ្អាកចូលក្នុងកូដផងដែរ៖

function validateContact({ name, email, message }) {
  debugger;
  // ...
}

ពេល DevTools បើក ការដំណើរការនឹងឈប់នៅ debugger។ ប្រសិនបើ DevTools បិទ គ្មានអ្វីកើតឡើងទេ។ សូមលុបបន្ទាត់ទាំងនេះមុនពេលផ្សព្វផ្សាយ។

វិធីសាស្ត្រល្អសម្រាប់ bug ណាមួយ៖

  1. ធ្វើឲ្យវាកើតឡើងម្តងទៀត។ រកជំហានដែលបង្កឲ្យមានវាជានិច្ច។
  2. បង្រួមវា។ តើបន្ទាត់ណាបង្ហាញតម្លៃខុសមុនគេ?
  3. បង្កើតការស្មានតែមួយ ផ្លាស់ប្តូរតែមួយ ហើយសាកល្បងម្តងទៀត។
  4. ពន្យល់ដោយសំឡេង (ប្រាប់មិត្តម្នាក់ ឬសូម្បីតែទៅកាន់ទាកៅស៊ូ)។ ការនិយាយចេញមកតែងតែបង្ហាញកំហុស។

9. Bug ដែលអ្នកចាប់ផ្តើមតែងជួប

រោគសញ្ញាមូលហេតុដែលអាចកើតមានដំណោះស្រាយ
x is not definedវាយខុស ឬ variable ត្រូវបានប្រកាសក្នុង block ផ្សេងពិនិត្យអក្ខរាវិរុទ្ធ និង scope
Cannot read properties of nullquerySelector រកមិនឃើញអ្វី (selector ខុស ឬ script ដំណើរការមុន HTML load)ពិនិត្យ selector; បន្ថែម defer ទៅ script tag
x is not a functionឈ្មោះខុស ឬ property មិនមែនជា functionlog តម្លៃ និងប្រភេទរបស់វា
"5" + 1 ឲ្យ "51"លាយអក្សរ និងលេខបំប្លែងដោយ Number("5")
if (x = 5) ពិតជានិច្ច= កំណត់តម្លៃ; === ប្រៀបធៀបប្រើ ===
0.1 + 0.2 ជា 0.30000000000000004លេខទសភាគត្រូវបានរក្សាទុកប្រហាក់ប្រហែលបង្គត់សម្រាប់បង្ហាញដោយ toFixed(2)
Loop មិនចេះចប់លក្ខខណ្ឌមិនដែលក្លាយជាមិនពិតពិនិត្យជំហានធ្វើបច្ចុប្បន្នភាព; log counter
ការផ្លាស់ប្តូរមិនលេចឡើងBrowser cache ឯកសារចាស់Hard refresh (Ctrl+Shift+R)
លទ្ធផល method របស់ array មិនត្រូវបានប្រើលទ្ធផលនៃ sort, map, filter មិនត្រូវបានរក្សាទុក ឬ returnកំណត់លទ្ធផលទៅ variable

Bug ភាគច្រើនរបស់អ្នកចាប់ផ្តើមធ្លាក់ក្នុងបីក្រុមប៉ុណ្ណោះ៖ ការវាយខុស ការសន្មតខុសអំពីតម្លៃ (វាជា null ឬ string មិនមែនអ្វីដែលអ្នកគិត) ឬ ដំណើរការខុសពេល (element មិនទាន់មាន)។ ការបោះពុម្ព ឬការផ្អាកដើម្បីមើលតម្លៃពិត ដោះស្រាយស្ទើរតែទាំងអស់។

10. សាកល្បងដោយខ្លួនឯង

  1. អាន trace។ រក្សាទុកឧទាហរណ៍ broken.js ពីផ្នែកទី 2។ ដំណើរការវា បន្ទាប់មកកែការហៅនៅបន្ទាត់ទី 5 ឲ្យបញ្ជូន [3, 4, 5]។ តើអ្នកត្រូវកែបន្ទាត់ណា ហើយហេតុអ្វីមិនមែនបន្ទាត់ទី 2?
  2. បង្កើត error នីមួយៗ។ សរសេរមួយបន្ទាត់ដែលបង្កើត SyntaxError, ReferenceError, TypeError និង RangeError។ អានសារនីមួយៗឮៗ។
  3. Catch និងរាយការណ៍។ សរសេរ parseAge(text) ដែលប្រើ Number(text) ហើយ throw new Error("Age must be a number.") ប្រសិនបើលទ្ធផលជា NaN។ ហៅវាក្នុង try/catch ជាមួយ "abc" និង "25"។
  4. បញ្ចប់ validator។ បន្ថែមការ validate ទម្រង់ទំនាក់ទំនងទៅទំព័ររបស់ Dara។ បន្ថែមច្បាប់មួយទៀត៖ ឈ្មោះត្រូវមានយ៉ាងតិច 2 តួអក្សរ។
  5. ប្រើ breakpoint។ ក្នុង handler របស់ទម្រង់ កំណត់ breakpoint នៅបន្ទាត់ validateContact(data);។ Submit ជាមួយឈ្មោះទទេ បន្ទាប់មក step into ចូលក្នុង function ហើយមើល variable name ក្នុង pane Scope។
  6. ដោះស្រាយ bug លាក់។ សរសេរ const total = "10" + 5; និង console.log(total)។ ពន្យល់លទ្ធផល បន្ទាប់មកកែឲ្យវាបោះពុម្ព 15។

ទំព័ររបស់អ្នកឥឡូវអាចដោះស្រាយ input មិនល្អដោយមិនខូច។ នៅក្នុងជំពូកបន្ទាប់ យើងបន្ថែម type ទៅ JavaScript ដើម្បីឲ្យកំហុសទាំងនេះជាច្រើនត្រូវបានចាប់បានមុនពេលកូដដំណើរការទៅទៀត៖ មូលដ្ឋានគ្រឹះ TypeScript។

ទិញកាហ្វេជូនខ្ញុំ

ចូលចិត្តមេរៀននេះមែនទេ? សូមពិចារណាទិញកាហ្វេជូនខ្ញុំមួយកែវ ដើម្បីគាំទ្រការបង្កើតមាតិកាឥតគិតថ្លៃបន្ថែមទៀត!