معالجة الأخطاء في JavaScript: try و catch و throw
مهما كان كودك متقناً، ستحدث أشياء خارجة عن إرادتك: ينقطع الإنترنت، ويرسل الخادم بيانات تالفة، ويكتب المستخدم حروفاً في خانة الأرقام. إذا لم تستعد لذلك، يتوقف برنامجك عند أول مشكلة، ويرى المستخدم صفحة مجمدة. في هذا الدرس ستتعلم كيف تتوقع الأخطاء وتتعامل معها بهدوء.
ماذا يحدث عند الخطأ؟
Section titled “ماذا يحدث عند الخطأ؟”console.log("قبل الخطأ");const user = null;console.log(user.name);console.log("بعد الخطأ");قبل الخطأTypeError: Cannot read properties of null (reading 'name')السطر الأخير لم يُنفَّذ. عندما يقع خطأ بدون معالجة، يتوقف تنفيذ السكربت عند ذلك السطر، وكل ما بعده يُهمل. في صفحة ويب معناه أن الأزرار قد تتوقف عن الاستجابة.
try و catch: حاول، وإن فشلت
Section titled “try و catch: حاول، وإن فشلت”try { const data = JSON.parse("{ بيانات تالفة }"); console.log(data);} catch (error) { console.log("تعذرت قراءة البيانات");}
console.log("البرنامج مستمر");تعذرت قراءة البياناتالبرنامج مستمرtry: ضع فيها الكود الذي قد يفشل.catch: تُنفَّذ فقط إذا وقع خطأ داخلtry. وإذا لم يقع خطأ تُتخطى.- بعد الكتلتين يكمل البرنامج طبيعياً.
تخيّل try مثل لاعب سيرك يمشي على الحبل، و catch هي الشبكة تحته: إذا مشى بسلام لم تُستخدم الشبكة، وإذا سقط التقطته وأكمل العرض.
انتبه: عند وقوع الخطأ يقفز التنفيذ فوراً إلى catch، وباقي أسطر try لا تُنفَّذ. لذلك لم يُطبع data في المثال.
كائن الخطأ
Section titled “كائن الخطأ”المتغير بين قوسي catch كائن يحمل معلومات عن الخطأ:
try { const price = 100; price = 200;} catch (error) { console.log(error.name); console.log(error.message);}TypeErrorAssignment to constant variable.| الخاصية | معناها |
|---|---|
error.name |
نوع الخطأ |
error.message |
وصف نصي لما حدث |
error.stack |
مسار الاستدعاءات التي أدت إلى الخطأ، ويفيد المبرمج في التتبع |
أنواع الأخطاء الشائعة
Section titled “أنواع الأخطاء الشائعة”| النوع | متى يحدث | مثال |
|---|---|---|
ReferenceError |
استخدام اسم غير معرّف | console.log(total) و total غير موجود |
TypeError |
عملية على نوع لا يدعمها | null.name أو استدعاء شيء ليس دالة |
SyntaxError |
نص لا يتبع القواعد | JSON.parse لنص غير صالح |
RangeError |
قيمة خارج المدى المسموح | new Array(-1) |
finally: في كل الأحوال
Section titled “finally: في كل الأحوال”كتلة finally تُنفَّذ دائماً: نجح الكود أم فشل.
function loadData(text) { console.log("إظهار دائرة التحميل"); try { const data = JSON.parse(text); console.log("وصلت البيانات:", data.title); } catch (error) { console.log("فشل التحميل"); } finally { console.log("إخفاء دائرة التحميل"); }}
loadData('{"title":"مرحباً"}');loadData("نص تالف");إظهار دائرة التحميلوصلت البيانات: مرحباًإخفاء دائرة التحميلإظهار دائرة التحميلفشل التحميلإخفاء دائرة التحميلفي الحالتين أُخفيت دائرة التحميل. finally مكان مناسب لكود التنظيف: إخفاء مؤشر التحميل، وإعادة تفعيل زر، وإغلاق اتصال.
throw: ارمِ خطأك الخاص
Section titled “throw: ارمِ خطأك الخاص”JavaScript ترمي الأخطاء عند مخالفة قواعدها. لكن برنامجك له قواعده أيضاً: العمر لا يكون سالباً، والكمية لا تزيد على المخزون. تستطيع أن ترمي خطأ بنفسك عند مخالفتها:
function divide(a, b) { if (b === 0) { throw new Error("لا يمكن القسمة على صفر"); } return a / b;}
try { console.log(divide(10, 2)); console.log(divide(5, 0)); console.log("هذا السطر لن يُنفَّذ");} catch (error) { console.log("خطأ:", error.message);}5خطأ: لا يمكن القسمة على صفرthrowتوقف الدالة فوراً وترمي الخطأ إلى من استدعاها.new Error("...")تنشئ كائن خطأ، والنص هوmessage.- الخطأ يصعد من دالة إلى التي استدعتها حتى يجد
tryوcatchتلتقطه. وإن لم يجد توقف البرنامج.
التحقق من مدخلات المستخدم
Section titled “التحقق من مدخلات المستخدم”function parseAge(input) { const age = Number(input);
if (input.trim() === "") { throw new Error("العمر مطلوب"); } if (Number.isNaN(age)) { throw new Error("اكتب العمر بالأرقام"); } if (age < 0 || age > 120) { throw new Error("العمر يجب أن يكون بين 0 و 120"); } return age;}
const inputs = ["14", "", "أربعة عشر", "200"];
for (const input of inputs) { try { console.log("عمر صحيح:", parseAge(input)); } catch (error) { console.log("مرفوض:", error.message); }}عمر صحيح: 14مرفوض: العمر مطلوبمرفوض: اكتب العمر بالأرقاممرفوض: العمر يجب أن يكون بين 0 و 120لاحظ أن try و catch داخل الحلقة، ولذلك لم يوقف المدخل الخاطئ فحص باقي المدخلات.
متى لا أستخدم try و catch؟
Section titled “متى لا أستخدم try و catch؟”لا تلفّ كل شيء بـ try. استخدمها مع ما هو خارج عن سيطرتك: بيانات من خادم، ومدخلات مستخدم، وتخزين المتصفح. أما أخطاء الكود نفسه، مثل اسم متغير خاطئ، فحلها أن تصلح الكود لا أن تخفي الخطأ.
وأسوأ ما تفعله هو catch فارغة:
try { JSON.parse("نص تالف");} catch (error) { // لا شيء!}الخطأ ابتُلع بصمت. البرنامج لا يعمل كما يجب، ولا أحد يعرف السبب. على الأقل اطبع الخطأ بـ console.error(error).
جرّب بنفسك
Section titled “جرّب بنفسك”الناتج
أضف شرطاً ثالثاً يرفض السحب إذا زاد المبلغ على 300 في المرة الواحدة، وجرّبه.
أخطاء شائعة
Section titled “أخطاء شائعة”- كتلة catch فارغة: تخفي المشكلة ولا تحلها. اطبع الخطأ، أو أخبر المستخدم.
- رمي نص بدل كائن خطأ:
throw "خطأ"يعمل، لكنك تفقدmessageوstack. اكتب دائماًthrow new Error("..."). - وضع البرنامج كله في try واحدة: يصعب معرفة أي جزء فشل. اجعل
tryصغيرة حول الجزء الخطر فقط.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
متى تُنفَّذ كتلة finally؟
finally تُنفَّذ في كل الأحوال، ولذلك تناسب كود التنظيف مثل إخفاء مؤشر التحميل.
سؤال
ما الطريقة الصحيحة لرمي خطأ خاص؟
throw ترمي الخطأ، و new Error تنشئ كائن خطأ يحمل الرسالة ومسار الاستدعاءات.
الخلاصة
Section titled “الخلاصة”- الخطأ غير المعالج يوقف السكربت عند سطره.
tryللكود الخطر، وcatchلما تفعله عند الفشل، وfinallyلما يُنفَّذ دائماً.- كائن الخطأ فيه
nameوmessageوstack. throw new Error("...")ترمي خطأك الخاص عند مخالفة قواعد برنامجك.- استخدم
tryمع ما هو خارج عن سيطرتك، ولا تتركcatchفارغة.