بغض النظر عن مدى روعتنا في البرمجة، في بعض الأحيان تحتوي نصوصنا على أخطاء. قد تحدث بسبب أخطائنا، أو إدخال مستخدم غير متوقع، أو استجابة خاطئة من الخادم، ولآلاف الأسباب الأخرى.
عادة، "يموت" البرنامج النصي (يتوقف فورًا) في حالة حدوث خطأ، عند طباعته على وحدة التحكم.
ولكن هناك بناء جملة try...catch
يسمح لنا "باكتشاف" الأخطاء حتى يتمكن البرنامج النصي من القيام بشيء أكثر منطقية بدلاً من الموت.
تحتوي بنية try...catch
على كتلتين رئيسيتين: try
، ثم catch
:
يحاول { // شفرة... } مسك (يخطئ) { // معالجة الأخطاء }
يعمل مثل هذا:
أولاً، يتم تنفيذ الكود الموجود في try {...}
.
إذا لم تكن هناك أخطاء، فسيتم تجاهل catch (err)
: يصل التنفيذ إلى نهاية try
ويستمر، متخطيًا catch
.
في حالة حدوث خطأ، يتم إيقاف تنفيذ try
، ويتدفق التحكم إلى بداية catch (err)
. سيحتوي المتغير err
(يمكننا استخدام أي اسم له) على كائن خطأ يحتوي على تفاصيل حول ما حدث.
لذا، فإن الخطأ داخل كتلة try {...}
لا يقتل البرنامج النصي - لدينا فرصة للتعامل معه في catch
.
دعونا نلقي نظرة على بعض الأمثلة.
مثال خالي من الأخطاء: يظهر alert
(1)
و (2)
:
يحاول { تنبيه ("بدء تشغيل المحاولة")؛ // (1) <-- // ...لا توجد أخطاء هنا تنبيه ("نهاية المحاولة")؛ // (2) <-- } مسك (يخطئ) { تنبيه ("تم تجاهل الالتقاط لعدم وجود أخطاء")؛ // (3) }
مثال به خطأ: يظهر (1)
و (3)
:
يحاول { تنبيه ("بدء تشغيل المحاولة")؛ // (1) <-- لالالا. // خطأ، لم يتم تعريف المتغير! تنبيه ("نهاية المحاولة (لم يتم الوصول إليها مطلقًا)")؛ // (2) } مسك (يخطئ) { تنبيه ("حدث خطأ!")؛ // (3) <-- }
try...catch
يعمل فقط مع أخطاء وقت التشغيل
لكي تعمل try...catch
، يجب أن يكون الكود قابلاً للتشغيل. بمعنى آخر، يجب أن يكون جافا سكريبت صالحًا.
لن ينجح الأمر إذا كانت التعليمات البرمجية خاطئة من الناحية النحوية، على سبيل المثال، تحتوي على أقواس متعرجة غير متطابقة:
يحاول { {{{{{{{{{{{{ } مسك (يخطئ) { تنبيه ("لا يستطيع المحرك فهم هذا الرمز، فهو غير صالح")؛ }
يقرأ محرك JavaScript التعليمات البرمجية أولاً، ثم يقوم بتشغيلها. تسمى الأخطاء التي تحدث في مرحلة القراءة بأخطاء "وقت التحليل" وهي غير قابلة للاسترداد (من داخل هذا الرمز). ذلك لأن المحرك لا يستطيع فهم الكود.
لذلك، يمكن لـ try...catch
معالجة الأخطاء التي تحدث في التعليمات البرمجية الصالحة فقط. تسمى هذه الأخطاء "أخطاء وقت التشغيل" أو في بعض الأحيان "الاستثناءات".
try...catch
يعمل بشكل متزامن
إذا حدث استثناء في التعليمات البرمجية "المجدولة"، كما هو الحال في setTimeout
، فلن try...catch
من التقاطه:
يحاول { setTimeout(وظيفة() { this.noSuchVariable; // سوف يموت البرنامج النصي هنا }, 1000); } مسك (يخطئ) { تنبيه("لن يعمل"); }
وذلك لأن الوظيفة نفسها يتم تنفيذها لاحقًا، عندما يكون المحرك قد ترك بالفعل بنية try...catch
.
للقبض على استثناء داخل دالة مجدولة، try...catch
يجب أن يكون داخل تلك الوظيفة:
setTimeout(وظيفة() { يحاول { this.noSuchVariable; // حاول...التقاط يعالج الخطأ! } يمسك { تنبيه ("تم اكتشاف خطأ هنا!" )؛ } }, 1000);
عند حدوث خطأ ما، تقوم JavaScript بإنشاء كائن يحتوي على تفاصيل عنه. يتم بعد ذلك تمرير الكائن كوسيطة catch
:
يحاول { // ... } Catch (err) { // <-- يمكن أن يستخدم "كائن الخطأ" كلمة أخرى بدلاً من err // ... }
بالنسبة لجميع الأخطاء المضمنة، يحتوي كائن الخطأ على خاصيتين رئيسيتين:
name
اسم الخطأ. على سبيل المثال، بالنسبة لمتغير غير محدد وهو "ReferenceError"
.
message
رسالة نصية حول تفاصيل الخطأ.
هناك خصائص أخرى غير قياسية متوفرة في معظم البيئات. أحد أكثرها استخدامًا ودعمًا على نطاق واسع هو:
stack
مكدس الاستدعاءات الحالي: سلسلة تحتوي على معلومات حول تسلسل الاستدعاءات المتداخلة التي أدت إلى الخطأ. تستخدم لأغراض التصحيح.
على سبيل المثال:
يحاول { لالالا. // خطأ، لم يتم تعريف المتغير! } مسك (يخطئ) { تنبيه (err.name)؛ // خطأ مرجعي تنبيه (err.message)؛ // لم يتم تعريف لالالا تنبيه (err.stack)؛ // خطأ مرجعي: لم يتم تعريف lalala عند (... مكدس الاستدعاءات) // يمكن أيضًا إظهار الخطأ ككل // يتم تحويل الخطأ إلى سلسلة كـ "الاسم: رسالة" تنبيه (خطأ)؛ // خطأ مرجعي: لم يتم تعريف لالالا }
إضافة حديثة
هذه إضافة حديثة للغة. قد تحتاج المتصفحات القديمة إلى عمليات تعبئة متعددة.
إذا لم نكن بحاجة إلى تفاصيل الخطأ، فقد يحذفها catch
:
يحاول { // ... } قبض على { // <-- بدون (يخطئ) // ... }
دعنا نستكشف حالة استخدام واقعية لـ try...catch
.
كما نعلم بالفعل، تدعم JavaScript طريقة JSON.parse(str) لقراءة القيم المشفرة بـ JSON.
عادةً ما يتم استخدامه لفك تشفير البيانات المستلمة عبر الشبكة، من الخادم أو من مصدر آخر.
نتلقاها ونتصل بـ JSON.parse
مثل هذا:
Let json = '{"name": "John"، "age": 30}'؛ // البيانات من الخادم دع المستخدم = JSON.parse(json); // تحويل تمثيل النص إلى كائن JS // الآن المستخدم هو كائن له خصائص من السلسلة تنبيه (اسم المستخدم) ؛ // جون تنبيه (user.age ); // 30
يمكنك العثور على معلومات أكثر تفصيلاً حول JSON في طرق JSON، فصل toJSON.
إذا كان تنسيق json
مشوهًا، فسيُنشئ JSON.parse
خطأ، وبالتالي "يموت" البرنامج النصي.
هل يجب أن نرضى بذلك؟ بالطبع لا!
بهذه الطريقة، إذا كان هناك خطأ ما في البيانات، فلن يعرف الزائر ذلك أبدًا (ما لم يفتح وحدة تحكم المطور). والناس لا يحبون حقًا عندما "يموت" شيء ما دون أي رسالة خطأ.
لنستخدم try...catch
لمعالجة الخطأ:
Let json = "{ bad json }"; يحاول { دع المستخدم = JSON.parse(json); // <-- عند حدوث خطأ... تنبيه (اسم المستخدم) ؛ // لا يعمل } مسك (يخطئ) { // ... يقفز التنفيذ هنا تنبيه( "نعتذر، البيانات بها أخطاء، سنحاول طلبها مرة أخرى." ); تنبيه (err.name)؛ تنبيه (err.message)؛ }
هنا نستخدم كتلة catch
فقط لإظهار الرسالة، ولكن يمكننا القيام بما هو أكثر من ذلك بكثير: إرسال طلب شبكة جديد، واقتراح بديل للزائر، وإرسال معلومات حول الخطأ إلى منشأة التسجيل، ... . كل هذا أفضل بكثير من مجرد الموت.
ماذا لو كان json
صحيحًا من الناحية النحوية، ولكن لا يحتوي على خاصية name
مطلوبة؟
مثله:
Let json = '{ "age": 30 }'; // بيانات غير كاملة يحاول { دع المستخدم = JSON.parse(json); // <-- لا توجد أخطاء تنبيه (اسم المستخدم) ؛ // بلا اسم! } مسك (يخطئ) { تنبيه ("لا يتم التنفيذ")؛ }
هنا يعمل JSON.parse
بشكل طبيعي، ولكن غياب name
هو في الواقع خطأ بالنسبة لنا.
لتوحيد معالجة الأخطاء، سنستخدم عامل throw
.
يقوم عامل throw
بإنشاء خطأ.
بناء الجملة هو:
رمي <كائن الخطأ>
من الناحية الفنية، يمكننا استخدام أي شيء ككائن خطأ. قد يكون ذلك حتى عنصرًا بدائيًا، مثل رقم أو سلسلة، ولكن من الأفضل استخدام الكائنات، ويفضل أن يكون ذلك مع خصائص name
message
(ليظل متوافقًا إلى حد ما مع الأخطاء المضمنة).
تحتوي JavaScript على العديد من المُنشئات المضمنة للأخطاء القياسية: Error
و SyntaxError
و ReferenceError
و TypeError
وغيرها. يمكننا استخدامها لإنشاء كائنات الخطأ أيضًا.
بناء الجملة الخاص بهم هو:
دع الخطأ = خطأ جديد (رسالة)؛ // أو دع الخطأ = new SyntaxError(message); دع الخطأ = new ReferenceError(message); // ...
بالنسبة للأخطاء المضمنة (ليس لأي كائنات، فقط للأخطاء)، فإن خاصية name
هي بالضبط اسم المُنشئ. message
مأخوذة من الحجة.
على سبيل المثال:
Let error = new Error("الأشياء تحدث o_O"); تنبيه (خطأ. اسم)؛ // خطأ تنبيه (خطأ. رسالة)؛ // تحدث الأشياء o_O
دعونا نرى نوع الخطأ الذي ينشئه JSON.parse
:
يحاول { JSON.parse("{ سيء json o_O }"); } مسك (يخطئ) { تنبيه (err.name)؛ // خطأ في بناء الجملة تنبيه (err.message)؛ // رمز غير متوقع ب في JSON في الموضع 2 }
كما نرى، هذا خطأ SyntaxError
.
وفي حالتنا، يعد غياب name
خطأ، حيث يجب أن يكون لدى المستخدمين name
.
لذلك دعونا رميها:
Let json = '{ "age": 30 }'; // بيانات غير كاملة يحاول { دع المستخدم = JSON.parse(json); // <-- لا توجد أخطاء إذا (! اسم المستخدم) { رمي SyntaxError الجديد ("بيانات غير كاملة: لا يوجد اسم")؛ // (*) } تنبيه (اسم المستخدم) ؛ } مسك (يخطئ) { تنبيه ("خطأ JSON:" + err.message ); // خطأ JSON: بيانات غير كاملة: لا يوجد اسم }
في السطر (*)
، يقوم عامل throw
بإنشاء خطأ SyntaxError
مع message
المحددة، بنفس الطريقة التي تنشئها JavaScript بنفسها. يتوقف تنفيذ try
على الفور وينتقل تدفق التحكم إلى catch
.
أصبح catch
الآن مكانًا واحدًا لمعالجة جميع الأخطاء: لكل من JSON.parse
والحالات الأخرى.
في المثال أعلاه نستخدم try...catch
للتعامل مع البيانات غير الصحيحة. ولكن هل من الممكن حدوث خطأ آخر غير متوقع داخل كتلة try {...}
؟ مثل خطأ في البرمجة (لم يتم تعريف المتغير) أو أي شيء آخر، وليس فقط هذا الشيء "البيانات غير الصحيحة".
على سبيل المثال:
Let json = '{ "age": 30 }'; // بيانات غير كاملة يحاول { المستخدم = JSON.parse(json); // <-- نسيت وضع "let" قبل المستخدم // ... } مسك (يخطئ) { تنبيه ("خطأ JSON:" + خطأ)؛ // خطأ JSON: خطأ مرجعي: لم يتم تعريف المستخدم // (لا يوجد خطأ JSON في الواقع) }
وبطبيعة الحال، كل شيء ممكن! المبرمجون يرتكبون الأخطاء. حتى في المرافق مفتوحة المصدر التي يستخدمها الملايين لعقود من الزمن – فجأة قد يتم اكتشاف خطأ يؤدي إلى عمليات اختراق رهيبة.
في حالتنا، يتم وضع try...catch
لاكتشاف أخطاء "البيانات غير الصحيحة". ولكن بطبيعته، يحصل catch
على جميع الأخطاء من try
. هنا يحصل على خطأ غير متوقع، لكنه لا يزال يظهر نفس رسالة "JSON Error"
. هذا خطأ ويجعل تصحيح التعليمات البرمجية أكثر صعوبة.
لتجنب مثل هذه المشاكل، يمكننا استخدام تقنية "إعادة الرمي". القاعدة بسيطة:
يجب أن يقوم Catch فقط بمعالجة الأخطاء التي يعرفها و"إعادة طرح" جميع الأخطاء الأخرى.
يمكن شرح تقنية "إعادة الرمي" بمزيد من التفصيل على النحو التالي:
Catch يحصل على كافة الأخطاء.
في كتلة catch (err) {...}
نقوم بتحليل كائن الخطأ err
.
إذا كنا لا نعرف كيفية التعامل معها، فإننا throw err
.
عادةً، يمكننا التحقق من نوع الخطأ باستخدام عامل instanceof
:
يحاول { المستخدم = { /*...*/ }; } مسك (يخطئ) { إذا (خطأ مثيل خطأ مرجعي) { تنبيه ("خطأ مرجعي")؛ // "خطأ مرجعي" للوصول إلى متغير غير محدد } }
يمكننا أيضًا الحصول على اسم فئة الخطأ من خاصية err.name
. جميع الأخطاء الأصلية لها ذلك. هناك خيار آخر وهو قراءة err.constructor.name
.
في الكود أدناه، نستخدم إعادة الرمي بحيث يتعامل catch
فقط مع SyntaxError
:
Let json = '{ "age": 30 }'; // بيانات غير كاملة يحاول { دع المستخدم = JSON.parse(json); إذا (! اسم المستخدم) { رمي SyntaxError الجديد ("بيانات غير كاملة: لا يوجد اسم")؛ } بلابلا(); // خطأ غير متوقع تنبيه (اسم المستخدم) ؛ } مسك (يخطئ) { إذا (خطأ مثيل SyntaxError) { تنبيه ("خطأ JSON:" + err.message ); } آخر { رمي خطأ. // إعادة الرمي (*) } }
الخطأ الذي يتم طرحه على السطر (*)
من داخل كتلة catch
"يسقط" من try...catch
ويمكن اكتشافه إما من خلال بنية try...catch
خارجية (إذا كانت موجودة)، أو أنه يقتل البرنامج النصي.
لذا فإن كتلة catch
تعالج في الواقع فقط الأخطاء التي تعرف كيفية التعامل معها و"تتخطى" جميع الأخطاء الأخرى.
يوضح المثال أدناه كيف يمكن اكتشاف مثل هذه الأخطاء من خلال مستوى آخر من try...catch
:
وظيفة قراءة البيانات () { Let json = '{ "age": 30 }'; يحاول { // ... بلابلا(); // خطأ! } مسك (يخطئ) { // ... إذا (! (خطأ في مثيل SyntaxError)) { رمي خطأ. // إعادة الطرح (لا أعرف كيفية التعامل معها) } } } يحاول { قراءة البيانات ()؛ } مسك (يخطئ) { تنبيه ("تم الحصول على الالتقاط الخارجي:" + err ); // أمسكت به! }
هنا readData
يعرف فقط كيفية التعامل مع SyntaxError
، في حين أن try...catch
تعرف كيفية التعامل مع كل شيء.
انتظر، هذا ليس كل شيء.
قد تحتوي بنية try...catch
على جملة كودية أخرى: finally
.
إذا كان موجودا، فإنه يعمل في جميع الحالات:
بعد try
إذا لم تكن هناك أخطاء
بعد catch
، إذا كانت هناك أخطاء.
يبدو بناء الجملة الموسع كما يلي:
يحاول { ... حاول تنفيذ الكود ... } مسك (يخطئ) { ... معالجة الأخطاء ... } أخيراً { .. نفذ دائما ... }
حاول تشغيل هذا الكود:
يحاول { تنبيه("محاولة"); if (confirm('هل ترتكب خطأ؟')) BAD_CODE(); } مسك (يخطئ) { تنبيه("التقاط"); } أخيراً { تنبيه("أخيرًا"); }
يحتوي الكود على طريقتين للتنفيذ:
إذا أجبت بـ "نعم" على "هل ارتكبت خطأ؟"، try -> catch -> finally
.
إذا قلت "لا"، try -> finally
.
غالبًا ما يتم استخدام الجملة finally
عندما نبدأ في فعل شيء ما ونريد الانتهاء منه بأي نتيجة.
على سبيل المثال، نريد قياس الوقت الذي تستغرقه دالة أرقام فيبوناتشي fib(n)
. وبطبيعة الحال، يمكننا البدء بالقياس قبل تشغيله والانتهاء منه بعد ذلك. ولكن ماذا لو حدث خطأ أثناء استدعاء الوظيفة؟ على وجه الخصوص، يؤدي تطبيق fib(n)
في الكود أدناه إلى إرجاع خطأ للأرقام السالبة أو غير الصحيحة.
يعد البند finally
مكانًا رائعًا لإنهاء القياسات مهما حدث.
وهنا يضمن finally
أنه سيتم قياس الوقت بشكل صحيح في كلتا الحالتين – في حالة التنفيذ الناجح fib
وفي حالة حدوث خطأ فيه:
Let num = +prompt("أدخل رقمًا صحيحًا موجبًا؟"، 35) اسمحوا الفرق، النتيجة؛ وظيفة فيب (ن) { إذا (ن < 0 || Math.trunc(n) != n) { رمي خطأ جديد("يجب ألا يكون سالبا، وأيضا عددا صحيحا."); } العودة ن <= 1؟ ن : فيب(ن - 1) + فيب(ن - 2); } لنبدأ = Date.now(); يحاول { النتيجة = فيب (رقم)؛ } مسك (يخطئ) { النتيجة = 0؛ } أخيراً { فرق = Date.now() - ابدأ؛ } تنبيه (نتيجة || "حدث خطأ")؛ تنبيه (`استغرق التنفيذ ${diff}ms`)؛
يمكنك التحقق من خلال تشغيل الكود بإدخال 35
في prompt
- يتم تنفيذه بشكل طبيعي، finally
بعد try
. ثم أدخل -1
- سيكون هناك خطأ فوري، وسيستغرق التنفيذ 0ms
. يتم إجراء كلا القياسات بشكل صحيح.
بمعنى آخر، قد تنتهي الوظيفة بـ return
أو throw
، هذا لا يهم. يتم تنفيذ الجملة finally
في كلتا الحالتين.
المتغيرات محلية داخل try...catch...finally
يرجى ملاحظة أنه تم الإعلان عن متغيرات result
diff
في الكود أعلاه قبل try...catch
.
بخلاف ذلك، إذا أعلنا عن let
try
، فسيكون مرئيًا بداخله فقط.
finally
return
تعمل الجملة finally
على أي خروج من try...catch
. يتضمن ذلك return
صريحة.
في المثال أدناه، هناك return
في try
. في هذه الحالة، يتم تنفيذ finally
قبل عودة عنصر التحكم إلى الكود الخارجي مباشرةً.
وظيفة الوظيفة () { يحاول { العودة 1؛ } مسك (يخطئ) { /* ... */ } أخيراً { تنبيه("أخيرًا"); } } تنبيه (وظيفة ())؛ // يعمل أولاً على التنبيه من أخيرًا، ثم هذا
try...finally
تعتبر try...finally
، بدون عبارة catch
، مفيدة أيضًا. نحن نطبقه عندما لا نريد معالجة الأخطاء هنا (نسمح لها بالوقوع)، ولكننا نريد التأكد من الانتهاء من العمليات التي بدأناها.
وظيفة الوظيفة () { // ابدأ في فعل شيء يحتاج إلى إكمال (مثل القياسات) يحاول { // ... } أخيراً { // أكمل هذا الشيء حتى لو مات الجميع } }
في الكود أعلاه، يحدث دائمًا خطأ داخل try
، لأنه لا يوجد catch
. ولكنه يعمل finally
قبل أن يترك تدفق التنفيذ الوظيفة.
خاصة بالبيئة
المعلومات الواردة في هذا القسم ليست جزءًا من JavaScript الأساسي.
لنتخيل أننا حصلنا على خطأ فادح خارج try...catch
، وتوقف النص. مثل خطأ برمجي أو أي شيء فظيع آخر.
هل هناك طريقة للرد على مثل هذه الأحداث؟ قد نرغب في تسجيل الخطأ، وإظهار شيء ما للمستخدم (عادةً لا يرى رسائل خطأ)، وما إلى ذلك.
لا يوجد أي شيء في المواصفات، لكن البيئات عادةً ما توفره، لأنه مفيد حقًا. على سبيل المثال، لدى Node.js process.on("uncaughtException")
لذلك. وفي المتصفح يمكننا تعيين وظيفة للخاصية window.onerror الخاصة، والتي سيتم تشغيلها في حالة حدوث خطأ لم يتم اكتشافه.
بناء الجملة:
window.onerror = function(message, url, line, col, error) { // ... };
message
رسالة خطأ.
url
عنوان URL للبرنامج النصي الذي حدث فيه الخطأ.
line
col
أرقام الأسطر والأعمدة التي حدث فيها الخطأ.
error
كائن خطأ.
على سبيل المثال:
<النص البرمجي> window.onerror = function(message, url, line, col, error) { تنبيه(`${message}n في ${line}:${col} من ${url}`); }; وظيفة قراءة البيانات () { badFunc(); // عفوًا، حدث خطأ ما! } قراءة البيانات ()؛ </script>
عادةً لا يتمثل دور المعالج العام window.onerror
في استعادة تنفيذ البرنامج النصي - ربما يكون هذا مستحيلًا في حالة حدوث أخطاء في البرمجة، ولكن في إرسال رسالة الخطأ إلى المطورين.
هناك أيضًا خدمات ويب توفر تسجيل الأخطاء لمثل هذه الحالات، مثل https://errorception.com أو https://www.muscula.com.
يعملون مثل هذا:
نقوم بالتسجيل في الخدمة ونحصل منها على جزء من JS (أو عنوان URL للبرنامج النصي) لإدراجه في الصفحات.
يقوم البرنامج النصي JS بتعيين وظيفة window.onerror
مخصصة.
عند حدوث خطأ، فإنه يرسل طلب شبكة حول هذا الموضوع إلى الخدمة.
يمكننا تسجيل الدخول إلى واجهة الويب الخاصة بالخدمة ورؤية الأخطاء.
تسمح بنية try...catch
بمعالجة أخطاء وقت التشغيل. فهو يسمح حرفيًا "بمحاولة" تشغيل الكود و"التقاط" الأخطاء التي قد تحدث فيه.
بناء الجملة هو:
يحاول { // قم بتشغيل هذا الرمز } مسك (يخطئ) { // إذا حدث خطأ، فانتقل هنا // الخطأ هو كائن الخطأ } أخيراً { // افعل على أية حال بعد المحاولة/الالتقاط }
قد لا يكون هناك قسم catch
أو لا يوجد finally
، لذا فإن البنيات الأقصر try...catch
try...finally
صالحة أيضًا.
كائنات الخطأ لها الخصائص التالية:
message
- رسالة الخطأ التي يمكن للإنسان قراءتها.
name
- السلسلة التي تحتوي على اسم الخطأ (اسم مُنشئ الخطأ).
stack
(غير قياسي، ولكنه مدعوم جيدًا) - المكدس في لحظة إنشاء الخطأ.
إذا لم تكن هناك حاجة إلى كائن خطأ، فيمكننا حذفه باستخدام catch {
بدلاً من catch (err) {
.
يمكننا أيضًا إنشاء الأخطاء الخاصة بنا باستخدام عامل throw
. من الناحية الفنية، يمكن أن تكون وسيطة throw
أي شيء، ولكنها عادةً ما تكون كائن خطأ يرث من فئة Error
المضمنة. المزيد عن توسيع الأخطاء في الفصل التالي.
تعد إعادة الرمي نمطًا مهمًا جدًا لمعالجة الأخطاء: عادةً ما تتوقع كتلة catch
نوع الخطأ المحدد وتعرف كيفية التعامل معه، لذا يجب عليها إعادة طرح الأخطاء التي لا تعرفها.
حتى لو لم يكن لدينا try...catch
، فإن معظم البيئات تسمح لنا بإعداد معالج أخطاء "عام" لالتقاط الأخطاء التي "تسقط". في المتصفح، هذا هو window.onerror
.
الأهمية: 5
قارن بين جزأين التعليمات البرمجية.
يستخدم الأول finally
لتنفيذ الكود بعد try...catch
:
يحاول { عمل عمل } مسك (يخطئ) { التعامل مع الأخطاء } أخيراً { تنظيف مساحة العمل }
الجزء الثاني يضع التنظيف مباشرة بعد try...catch
:
يحاول { عمل عمل } مسك (يخطئ) { التعامل مع الأخطاء } تنظيف مساحة العمل
نحن بالتأكيد بحاجة إلى التنظيف بعد العمل، لا يهم إذا كان هناك خطأ أم لا.
هل هناك ميزة هنا في استخدام finally
أم أن أجزاء التعليمات البرمجية متساوية؟ إذا كان هناك مثل هذه الميزة، فقم بإعطاء مثال عندما يكون الأمر مهما.
يصبح الفرق واضحًا عندما ننظر إلى الكود داخل الوظيفة.
يختلف السلوك إذا كان هناك "قفزة خارجة" من try...catch
.
على سبيل المثال، عندما يكون هناك return
للداخل try...catch
. تعمل عبارة finally
في حالة أي خروج من try...catch
، حتى عبر عبارة return
: مباشرة بعد الانتهاء من try...catch
، ولكن قبل أن يحصل رمز الاتصال على التحكم.
الدالة و() { يحاول { تنبيه('بدء'); إرجاع "النتيجة" ؛ } مسك (يخطئ) { /// ... } أخيراً { تنبيه ("تنظيف!")؛ } } و ()؛ // تنظيف!
…أو عندما تكون هناك throw
، كما هو الحال هنا:
الدالة و() { يحاول { تنبيه("ابدأ"); رمي خطأ جديد("خطأ"); } مسك (يخطئ) { // ... إذا ("لا يمكن التعامل مع الخطأ") { رمي خطأ. } } أخيراً { تنبيه ("تنظيف!") } } و ()؛ // تنظيف!
finally
هذا يضمن التنظيف هنا. إذا وضعنا الكود في نهاية f
، فلن يعمل في هذه المواقف.