404 Not Found

404 Not Found


nginx

الأخطاء والتصحيح في JavaScript

كتابة كود خالٍ من الأخطاء مستحيل — حتى مبتكر JavaScript أنتج خطأ typeof null === "object" في 10 أيام فقط. لذا تعلم العثور على الأخطاء ومعالجتها أهم من تعلم كتابة الكود.

📖 ملخص

أنواع الأخطاء الشائعة

نوع الخطأ السبب مثال
SyntaxError صيغة غير صالحة if (true { — أقواس غير متطابقة
ReferenceError الوصول إلى متغير غير موجود console.log(notExist)
TypeError العمل على نوع خاطئ undefined.toString()
RangeError القيمة خارج النطاق الصالح new Array(-1)

SyntaxError يُ catch قبل تنفيذ الكود (مرحلة التحليل). الأخطاء الأخرى هي أخطاء وقت التشغيل.

try...catch...finally

استخدم try...catch ل catch أخطاء الوقت التشغيل ومنع تعطل برنامجك:

HTML
<script>
try {
  const data = JSON.parse('{bad json}');
} catch (error) {
  console.log("فشل التحليل:", error.message);
} finally {
  console.log("عملية التنظيف");
}
</script>

كائن error في catch لديه خاصيتان مستخدمتان بشكل شائع:

throw — إلقاء الأخطاء يدوياً

try...catch يمكنه فقط catch أخطاء الوقت التشغيل، لكن أخطاء المنطق التجاري تحتاج إلى إلقائها يدوياً:

HTML
<script>
function setAge(age) {
  if (typeof age !== "number" || age < 0) {
    throw new Error("العمر يجب أن يكون رقماً موجباً");
  }
  console.log("تم تعيين العمر إلى: " + age);
}

try {
  setAge(-5);
} catch (e) {
  console.log(e.message);
}
</script>

throw يمكن أن تلقي أي قيمة، لكن يُنصح باستخدام new Error() أو فرعياتها — تتضمن معلومات تتبع المكدس.

رسائل الأخطاء المخصصة

استخدم فرعيات Error أو خصائص مخصصة لتوفير معلومات أكثر ثراءً عن الأخطاء:

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

try {
  throw new ValidationError("email", "تنسيق بريد إلكتروني غير صالح");
} catch (e) {
  console.log(e.name + " [" + e.field + "]: " + e.message);
}
</script>

أدوات المتصفح (F12)

اللوحة الغرض
Console عرض السجلات، تنفيذ الكود، رؤية الأخطاء
Sources تعيين نقاط توقف، تنفيذ سطر بسطر، فحص المتغيرات
Network فحص طلبات الشبكة، حالة الاستجابة، أوقات التحميل
Elements عرض/تعديل DOM والأنماط

تصحيح نقاط التوقف هو أقوى تقنية تصحيح: انقر على رقم السطر في لوحة Sources لتعيين نقطة توقف، حدث الصفحة، يتوقف الكود عند تلك النقطة — تنفيذ سطر بسطر، فحص المتغيرات، وفحص مكدس الاتصال.

عائلة console

الطريقة الغرض
console.log() إخراج عام
console.warn() تحذير (أصفر)
console.error() خطأ (أحمر)
console.table() عرض المصفوفات/الكائنات كجدول
console.time() / console.timeEnd() قياس الوقت
console.group() / console.groupEnd() إخراج مجمع
console.clear() مسح وحدة التحكم

console.table مفيد بشكل خاص لعرض مصفوفات الكائنات — أكثر قابلية للقراءة من log.

نصائح التحيح


مثال: try/catch لتحليل JSON

HTML
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
  <meta charset="UTF-8">
  <title>تحليل JSON</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .demo { max-width: 600px; margin: 16px auto; }
    textarea { width: 100%; height: 120px; padding: 12px; font-size: 14px; font-family: monospace; border: 2px solid #ccc; border-radius: 6px; resize: vertical; box-sizing: border-box; direction: ltr; }
    button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; margin: 8px 4px; }
    .parse { background: #4a90d9; color: #fff; }
    .parse:hover { background: #357abd; }
    .bad { background: #d9534f; color: #fff; }
    .bad:hover { background: #c9302c; }
    .result { margin-top: 16px; padding: 16px; border-radius: 8px; }
    .success { background: #d4edda; border: 2px solid #5cb85c; }
    .error-box { background: #f8d7da; border: 2px solid #d9534f; }
    .error-type { font-weight: bold; color: #721c24; }
    .error-msg { margin-top: 4px; color: #721c24; }
  </style>
</head>
<body>
  <h2 style="text-align:center;">معالجة أخطاء تحليل JSON</h2>
  <div class="demo">
    <textarea id="jsonInput">{
  "name": "أليس",
  "age": 20,
  "skills": ["JavaScript", "HTML", "CSS"]
}</textarea>
    <div style="text-align:center;">
      <button class="parse" id="parseBtn">حلل JSON</button>
      <button class="bad" id="badBtn">أدرج JSON غير صالح</button>
    </div>
    <div id="result"></div>
  </div>
  <script>
    function parseJSON(jsonStr) {
      try {
        const data = JSON.parse(jsonStr);
        return { success: true, data: data };
      } catch (error) {
        return { success: false, name: error.name, message: error.message };
      }
    }

    document.getElementById("parseBtn").addEventListener("click", function() {
      var input = document.getElementById("jsonInput").value;
      var result = parseJSON(input);
      var output = document.getElementById("result");

      if (result.success) {
        output.className = "result success";
        output.innerHTML = "<strong>نجح التحليل!</strong><pre>" +
          JSON.stringify(result.data, null, 2) + "</pre>";
      } else {
        output.className = "result error-box";
        output.innerHTML = '<div class="error-type">' + result.name +
          '</div><div class="error-msg">' + result.message + '</div>';
      }
    });

    document.getElementById("badBtn").addEventListener("click", function() {
      document.getElementById("jsonInput").value = '{name: "أليس", age: 20}';
    });
  </script>
</body>
</html>
▶ جرّب الكود

مثال: أخطاء مخصصة والتحقق من النماذج

HTML
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
  <meta charset="UTF-8">
  <title>أخطاء مخصصة</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .form { max-width: 440px; margin: 16px auto; padding: 24px; background: #f9f9f9; border-radius: 12px; }
    .row { margin: 12px 0; }
    label { display: block; margin-bottom: 4px; font-weight: bold; }
    input { width: 100%; padding: 10px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; box-sizing: border-box; }
    input.valid { border-color: #5cb85c; }
    input.invalid { border-color: #d9534f; }
    .field-error { color: #d9534f; font-size: 13px; margin-top: 2px; min-height: 20px; }
    button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; width: 100%; margin-top: 8px; }
    button:hover { background: #357abd; }
    .global-msg { margin-top: 12px; padding: 12px; border-radius: 6px; text-align: center; }
    .global-ok { background: #d4edda; color: #155724; }
    .global-fail { background: #f8d7da; color: #721c24; }
  </style>
</head>
<body>
  <h2 style="text-align:center;">التحقق من الأخطاء المخصصة</h2>
  <div class="form">
    <div class="row">
      <label>اسم المستخدم (3–20 حرف):</label>
      <input type="text" id="username" placeholder="أدخل اسم المستخدم" />
      <div class="field-error" id="usernameErr"></div>
    </div>
    <div class="row">
      <label>العمر (0–150):</label>
      <input type="text" id="age" placeholder="أدخل العمر" />
      <div class="field-error" id="ageErr"></div>
    </div>
    <div class="row">
      <label>البريد الإلكتروني:</label>
      <input type="text" id="email" placeholder="أدخل البريد الإلكتروني" />
      <div class="field-error" id="emailErr"></div>
    </div>
    <button id="submitBtn">إرسال</button>
    <div class="global-msg" id="globalMsg"></div>
  </div>
  <script>
    class ValidationError extends Error {
      constructor(field, message) {
        super(message);
        this.name = "ValidationError";
        this.field = field;
      }
    }

    function validateUsername(value) {
      if (!value.trim()) throw new ValidationError("username", "اسم المستخدم لا يمكن أن يكون فارغاً");
      if (value.trim().length < 3) throw new ValidationError("username", "اسم المستخدم يجب أن يكون 3 أحرف على الأقل");
      if (value.trim().length > 20) throw new ValidationError("username", "اسم المستخدم يجب أن يكون 20 حرف على الأكثر");
    }

    function validateAge(value) {
      if (!value.trim()) throw new ValidationError("age", "العمر لا يمكن أن يكون فارغاً");
      var num = Number(value);
      if (isNaN(num)) throw new ValidationError("age", "الرجاء إدخال رقم صالح");
      if (num < 0 || num > 150) throw new ValidationError("age", "نطاق العمر: 0–150");
      if (!Number.isInteger(num)) throw new ValidationError("age", "العمر يجب أن يكون عدداً صحيحاً");
    }

    function validateEmail(value) {
      if (!value.trim()) throw new ValidationError("email", "البريد الإلكتروني لا يمكن أن يكون فارغاً");
      if (!value.includes("@")) throw new ValidationError("email", "البريد الإلكتروني يجب أن يحتوي على @");
      if (!value.includes(".")) throw new ValidationError("email", "تنسيق بريد إلكتروني غير صالح");
    }

    var validators = {
      username: validateUsername,
      age: validateAge,
      email: validateEmail
    };

    function clearErrors() {
      ["username", "age", "email"].forEach(function(field) {
        document.getElementById(field).className = "";
        document.getElementById(field + "Err").textContent = "";
      });
      document.getElementById("globalMsg").innerHTML = "";
      document.getElementById("globalMsg").className = "global-msg";
    }

    document.getElementById("submitBtn").addEventListener("click", function() {
      clearErrors();
      var hasError = false;

      ["username", "age", "email"].forEach(function(field) {
        var value = document.getElementById(field).value;
        try {
          validators[field](value);
          document.getElementById(field).className = "valid";
        } catch (e) {
          hasError = true;
          document.getElementById(field).className = "invalid";
          document.getElementById(field + "Err").textContent =
            e.name + ": " + e.message;
        }
      });

      var msg = document.getElementById("globalMsg");
      if (hasError) {
        msg.className = "global-msg global-fail";
        msg.textContent = "النموذج فيه أخطاء — الرجاء الإصلاح وإعادة المحاولة";
      } else {
        msg.className = "global-msg global-ok";
        msg.textContent = "جميع الحقول عبرت التحقق!";
      }
    });
  </script>
</body>
</html>
▶ جرّب الكود

مثال: مرجع سريع لأنواع الأخطاء وطرق console

HTML
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
  <meta charset="UTF-8">
  <title>أنواع الأخطاء و Console</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .section { margin: 20px 0; }
    table { border-collapse: collapse; width: 100%; max-width: 700px; direction: ltr; }
    td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
    th { background: #4a90d9; color: #fff; }
    .trigger { background: #f8d7da; }
    .safe { background: #d4edda; }
    button { padding: 6px 16px; border: none; border-radius: 4px; cursor: pointer; margin: 2px; font-size: 14px; }
    .btn-err { background: #d9534f; color: #fff; }
    .btn-safe { background: #5cb85c; color: #fff; }
    .btn-info { background: #5bc0de; color: #fff; }
    .output { margin-top: 12px; padding: 12px; background: #1a1a2e; color: #e0e0e0; border-radius: 6px; font-family: monospace; font-size: 13px; min-height: 60px; max-height: 200px; overflow-y: auto; direction: ltr; text-align: left; }
    .log-line { margin: 2px 0; }
    .log-warn { color: #f0ad4e; }
    .log-error { color: #d9534f; }
    .log-info { color: #5bc0de; }
  </style>
</head>
<body>
  <h2>عرض أنواع الأخطاء وطرق console</h2>

  <div class="section">
    <h3>أنواع الأخطاء الشائعة</h3>
    <table>
      <tr><th>نوع الخطأ</th><th>كود التفعيل</th><th>نتيجة try/catch</th></tr>
      <tr class="trigger"><td>SyntaxError</td><td><code>JSON.parse("{bad")</code></td><td id="r1"></td></tr>
      <tr class="trigger"><td>ReferenceError</td><td><code>notExistVar</code></td><td id="r2"></td></tr>
      <tr class="trigger"><td>TypeError</td><td><code>undefined.toString()</code></td><td id="r3"></td></tr>
      <tr class="trigger"><td>RangeError</td><td><code>new Array(-1)</code></td><td id="r4"></td></tr>
    </table>
  </div>

  <div class="section">
    <h3>طرق console (إخراج محاكي)</h3>
    <button class="btn-info" onclick="simLog('log', 'سجل عادي')">console.log</button>
    <button class="btn-info" onclick="simLog('warn', 'رسالة تحذير')">console.warn</button>
    <button class="btn-err" onclick="simLog('error', 'رسالة خطأ')">console.error</button>
    <button class="btn-info" onclick="simLog('info', 'رسالة معلومات')">console.info</button>
    <button class="btn-safe" onclick="simTable()">console.table</button>
    <button class="btn-safe" onclick="simTime()">console.time</button>
    <button class="btn-info" onclick="clearSim()">مسح</button>
    <div class="output" id="simConsole"></div>
  </div>

  <script>
    var tests = [
      function() { JSON.parse("{bad"); },
      function() { notExistVar; },
      function() { var x; x.toString(); },
      function() { new Array(-1); }
    ];

    var ids = ["r1", "r2", "r3", "r4"];
    var names = ["SyntaxError", "ReferenceError", "TypeError", "RangeError"];

    tests.forEach(function(fn, i) {
      try {
        fn();
        document.getElementById(ids[i]).textContent = "لم يتم التفعيل (غير متوقع)";
      } catch (e) {
        document.getElementById(ids[i]).innerHTML =
          '<span class="safe">' + e.name + ": " + e.message + '</span>';
      }
    });

    function simLog(type, msg) {
      var cls = type === "warn" ? "log-warn" :
                type === "error" ? "log-error" : "log-info";
      var prefix = type === "warn" ? "⚠" :
                   type === "error" ? "✖" : "ℹ";
      addLine(prefix + " " + msg, cls);
      if (type !== "error") {
        console[type](msg);
      }
    }

    function simTable() {
      var data = [
        { name: "أليس", age: 20, score: 90 },
        { name: "بوب", age: 22, score: 85 },
        { name: "تشارلي", age: 21, score: 95 }
      ];
      addLine("console.table:", "log-info");
      data.forEach(function(d) {
        addLine("  " + d.name + " | " + d.age + " | " + d.score, "log-info");
      });
      console.table(data);
    }

    function simTime() {
      console.time("demo");
      var sum = 0;
      for (var i = 0; i < 1000000; i++) { sum += i; }
      console.timeEnd("demo");
      addLine("⏱ console.time/timeEnd: تم 1,000,000 عملية جمع", "log-info");
    }

    function addLine(text, cls) {
      var div = document.getElementById("simConsole");
      div.innerHTML += '<div class="log-line ' + (cls || "") + '">' + text + '</div>';
      div.scrollTop = div.scrollHeight;
    }

    function clearSim() {
      document.getElementById("simConsole").innerHTML = "";
    }
  </script>
</body>
</html>
▶ جرّب الكود

❓ أسئلة شائعة

س هل يمكن لـ try...catch التقاط SyntaxError؟
ج لا. SyntaxError تُطرح أثناء مرحلة التحليل قبل بدء التنفيذ، لذا بلوك try لا يصل أبداً. ومع ذلك، إذا حدث SyntaxError وقت التشغيل (مثل فشل JSON.parse، أو new Function("bad syntax"))، يمكن التقاطه.
س متى أستخدم finally؟
ج عندما تحتاج عمليات التنظيف أن تعمل بغض النظر عن حدوث خطأ — إغلاق الملفات، تحرير الموارد، إخفاء حالات التحميل. لمعظم السيناريوهات البسيطة، catch وحده كافٍ.
س ما الفرق بين throw و return؟
ج return للتدفق الطبيعي — المتصل يتعامل مع القيمة المرجعة. throw للتدفق غير الطبيعي — يتخطى الكود اللاحق ويرتفع في مكدس الاتصال بحثاً عن catch. استخدم throw للأخطاء، return للنتائج الطبيعية.

📝 تمارين

  1. اكتب دالة safeDivide(a, b) تستخدم try...catch لمعالجة القسمة على صفر (تلميح: القسمة على صفر في JS لا تلقي خطأ — تحتاج للتحقق والإلقاء بنفسك). تُرجع النتيجة أو رسالة خطأ.
  2. اكتب دالة safeJSONParse(str) تحلل سلسلة JSON. عند الفشل، تُرجع { success: false, error: <رسالة الخطأ> }. عند النجاح، تُرجع { success: true, data: <نتيجة التحليل> }.
  3. اكتب دالة retry(fn, times): نفّذ fn، وإذا ألقت خطأ، أعد المحاولة حتى times مرات. إذا فشلت جميع المحاولات، اطرح الخطأ الأخير. نفّذ باستخدام try...catch.
100%