try...catch في JavaScript

من موسوعة حسوب
مراجعة 15:36، 28 يناير 2018 بواسطة عبد اللطيف ايمش (نقاش | مساهمات) (استبدال النص - '\[\[تصنيف:(.*)\]\]' ب'{{SUBPAGENAME}}')
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

التعبير البرمجي try...catch يُستخدَم لتجربة قسم من التعابير البرمجية، ويُحدِّد طريقةً لمعالجة الاستثناءات إن رُمِيَت.

البنية العامة

try {
   try_statements
}
[catch (exception_var_1) {
   catch_statements_1
}]
...
[catch (exception_var_2) {
   catch_statements_2
}]
[finally {
   finally_statements
}]

try_statements

التعابير البرمجية التي ستُنفَّذ والتي قد ترمي استثناءً.

catch_statements_1 و catch_statements_2

التعابير البرمجية التي ستُنفَّذ إذا رُمي استثناءٌ في قسم try.

exception_var_1 و exception_var_2

مُعرِّف يُستخدَم لتخزين قيمة كائن الاستثناء (exception object) المرتبط مع قسم catch.

finally_statements

التعابير البرمجية التي ستُنفَّذ بعد إكمال تنفيذ try، وهذه التعابير ستُنفَّذ سواءً رُمي استثناءٌ أم لم يرمَ.

الوصف

التعبير try يتألف من قسم يحتوي على تعبير برمجي واحد أو أكثر (ويجب استخدام القوسين {} حتى لو كان القسم يتألف من تعبيرٍ برمجيٍ واحد)، ويجب أن يضم قسم catch أو قسم finally واحد على الأقل؛ وهذا يعني أنَّ هنالك ثلاثة أشكال من تعابير try:

  • try...catch
  • try...finally
  • try...catch...finally

يحتوي قسم catch على التعابير البرمجي التي تُحدِّد ما الذي يجب فعله عند رمي استثناء في قسم try؛ أي أنَّنا نريد أن ينجح تنفيذ التعابير البرمجي في قسم try، وإن لم ينجح تنفيذها فنريد أن ننقل التحكم إلى قسم catch؛ فلو رمى أيّ تعبير (أو دالة مستدعاة) في قسم try استثناءً فسينتقل التحكم مباشرةً إلى قسم catch، وإن لم يحدث أيّ استثناء في قسم try فسيتم تخطي قسم catch كليًّا.

قسم finally سيُنفَّذ بعد تنفيذ قسم try و catch لكن قبل التعابير البرمجة التي تلي try؛ وسيُنفَّذ دومًا سواءً رُميَ استثناءٌ أم لم يرمَ.

يمكنك تشعّب تعابير try داخل بعضها بعضًا، وإذا لم يملك تعبير try الداخلي قسم catch مناسب، فسيُستعمَل قسم catch لتعبير try الأب.

لاحظ أنَّ بالإمكان استخدام try لمعالجة الاستثناءات التي ترميها لغة JavaScript إضافةً إلى الاستثناءات الخاصة التي يرميها المستخدم.

أقسام catch غير الشرطية

عند استخدام قسم catch وحيد دون شرط معه، فسينتقل تنفيذ البرنامج إلى ذاك القسم في كل مرة يُرمى فيها استثناء؛ فمثلًا عند وقوع استثناء في الشيفرة الآتية فسينتقل التحكم مباشرةً إلى قسم catch:

try {
   throw 'myException'; // توليد استثناء
}
catch (e) {
   // التعابير البرمجية اللازمة لمعالجة الاستثناء
   logMyErrors(e); // تسجيل معلومات كائن الاستثناء
}

يُحدِّد قسم catch مُعرِّفًا (اسمه e في المثال السابق) الذي يحتوي على القيمة المرمية عبر تعبير throw، لاحظ أنَّ JavaScript تُنشِئ مُعرِّفًا عند دخول قسم catch وتتيحه للمجال المحلي (local scope) وسيبقى هذا المُعرِّف متاحًا حتى نهاية قسم catch ولن يكون متوافرًا بعده.

أقسام catch الشرطية

يمكن استخدام التعابير الشرطية if...else داخل قسم catch لاختبار تحقق شرط معيّن لكائن الاستثناءات؛ فسنختبر في المثال الآتي إذا كان الاستثناء المرمي من نوع TypeError أو RangeError أو EvalError:

try {
    myroutine(); // قد تؤدي هذه الدالة إلى رمي استثناء من أي نوع
} catch (e) {
    if (e instanceof TypeError) {
        // ...
    } else if (e instanceof RangeError) {
        // ...
    } else if (e instanceof EvalError) {
        // ...
    } else {
       // ...
       logMyErrors(e);
    }
}

مُعرِّف الاستثناء

عندما يرمى استثناءٌ في قسم try فإنَّ exception_var (مثلًا e في catch (e)‎) سيحتفظ بالقيمة المُحدَّدة عبر التعبير throw، ويمكنك استخدام هذا المُعرِّف للحصول على معلومات حول الاستثناء الذي قد رُمِي.

لاحظ أنَّ هذا المُعرِّف محليٌ، أي أنَّه قد أنُشِئ عند دخول القسم catch، ولن يكون متاحًا بعد انتهاء تنفيذ القسم catch.

قسم finally

قسم finally يحتوي على التعابير البرمجية التي ستُنفَّذ بعد قسم try و catch، لكن قبل التعابير التي تليها؛ لاحظ أنَّ قسم finally سيُنفَّذ سواءً رُمي الاستثناء أم لم يرمَ، وإذا رمَي استثناءٌ فستُنفَّذ التعابير البرمجية في قسم finally سواءً عالج قسم catch الاستثناء أم لم يعالجه؛ ويمكنك استخدام قسم finally لإنهاء تنفيذ السكربت بشكل جيد عند وقوع استثناء، مثل إغلاق الموارد التي استخدمها السكربت...

قد تجد من المستغرب وجود قسم يُنفِّذ التعابير البرمجية سواءً كان هنالك استثناءٌ أم لا؛ لكن هذا القسم له غرضٌ مفيدٌ ومهم؛ إذ إنَّ الشيفرات البرمجية التي تلي تعبير try قد لا تُنفَّذ إن لم يُعالَج الاستثناء؛ وبالتالي لو وضعنا شيفرة إغلاق الموارد فيها فقد لا تُنفَّذ دومًا، أما لو وضعناها في قسم finally فستُنفَّذ دومًا.

المثال الآتي سيفتح ملفًا على القرص الصلب ثم سنستخدم دالةً ضمن قسم try للكتابة عليه (تذكَّر أنَّ JavaScript قد تُستخدَم في جهة الخادم، مما يعني إمكانية الوصول إلى الملفات المحلية)؛ وإذا حدث استثناءٌ وكان الملف مفتوحًا، فستُغلِق التعابير البرمجية الموجودة في قسم finally الملف قبل انتهاء تنفيذ السكربت:

openMyFile();
try {
   // حجز المورد
   writeMyFile(theData);
}
finally {
   closeMyFile(); // إغلاق المورد على كل حال
}

أمثلة

تعابير try المتشعبة

هذا مثال عن تعبير try له القسم finally فقط، موجودٌ ضمن تعبير try له القسم catch فقط:

try {
  try {
    throw new Error('oops');
  }
  finally {
    console.log('finally');
  }
}
catch (ex) {
  console.error('outer', ex.message);
}

// الناتج
// "finally"
// "outer" "oops"

أما لو أضفنا القسم catch إلى تعبير try الداخلي لمعالجة الاستثناء:

try {
  try {
    throw new Error('oops');
  }
  catch (ex) {
    console.error('inner', ex.message);
  }
  finally {
    console.log('finally');
  }
}
catch (ex) {
  console.error('outer', ex.message);
}

// الناتج
// "inner" "oops"
// "finally"

لاحظ أنَّ أيّ استثناء سيُعالَج مرةً واحدةً عبر أقرب قسم catch إليه إلا إذا أُعيد رميه (بالتعبير throw)، أي لو ظهرت أيّة استثناءات خارج تعبير try الداخلي فستُعالَج عبر قسم catch الخارجي.

إعادة القيم من قسم finally

إذا أُعيدت قيمة ضمن قسم finally ستصبح القيمة النهائية المُعادة من قسم try...catch...finally كله، بغض النظر عن أيّة تعابير return موجودة في أقسام try و catch:

(function() {
  try {
    try {
      throw new Error('oops');
    }
    catch (ex) {
      console.error('inner', ex.message);
      throw ex;
    }
    finally {
      console.log('finally');
      return;
    }
  }
  catch (ex) {
    console.error('outer', ex.message);
  }
})();

// الناتج
// "inner" "oops"
// "finally"

دعم المتصفحات

الميزة Chrome Firefox Internet Explorer Opera Safari
الدعم الأساسي نعم نعم نعم نعم نعم

مصادر ومواصفات