يحتوي هذا الملف التمهيدي (README) على معلومات تعلمتها على مر السنين حول التعامل مع أخطاء JavaScript، والإبلاغ عنها إلى الخادم، والتنقل عبر الكثير من الأخطاء التي يمكن أن تجعل هذا الأمر صعبًا للغاية. لقد تحسنت المتصفحات في هذا المجال، ولكن لا يزال هناك مجال للتحسين للتأكد من أن جميع التطبيقات يمكنها التعامل بشكل سليم وسليم مع أي خطأ يحدث.
يمكن العثور على حالات الاختبار للمحتوى الموجود في هذا الدليل على https://mknichel.github.io/javascript-errors/.
جدول المحتويات
مقدمة
تشريح خطأ جافا سكريبت
إنتاج خطأ جافا سكريبت
رسائل الخطأ
تنسيق تتبع المكدس
اكتشاف أخطاء جافا سكريبت
window.onerror
حاول/قبض
نقاط الدخول المحمية
وعود
عمال الويب
ملحقات كروم
يعد اكتشاف الأخطاء والإبلاغ عنها وإصلاحها جزءًا مهمًا من أي تطبيق لضمان صحة التطبيق واستقراره. نظرًا لأن كود JavaScript يتم تنفيذه أيضًا على العميل وفي العديد من بيئات المتصفح المختلفة، فقد يكون من الصعب أيضًا البقاء على اطلاع بأخطاء JS من تطبيقك. لا توجد مواصفات ويب رسمية لكيفية الإبلاغ عن أخطاء JS التي تسبب اختلافات في تنفيذ كل متصفح. بالإضافة إلى ذلك، كانت هناك العديد من الأخطاء في تنفيذ المتصفحات لأخطاء JavaScript مما جعل الأمر أكثر صعوبة. تتنقل هذه الصفحة عبر هذه الجوانب من أخطاء JS حتى يتمكن المطورون المستقبليون من التعامل مع الأخطاء بشكل أفضل، ونأمل أن تتقارب المتصفحات مع الحلول القياسية.
يتكون خطأ JavaScript من جزأين أساسيين: رسالة الخطأ وتتبع المكدس . رسالة الخطأ عبارة عن سلسلة تصف الخطأ الذي حدث، ويصف تتبع المكدس مكان حدوث الخطأ في التعليمات البرمجية. يمكن أن تنتج أخطاء JS إما عن طريق المتصفح نفسه أو عن طريق رمز التطبيق.
يمكن للمتصفح أن يلقي خطأ JS عندما لا يتم تنفيذ جزء من التعليمات البرمجية بشكل صحيح، أو يمكن أن يتم طرحه مباشرة عن طريق التعليمات البرمجية.
على سبيل المثال:
فار أ = 3;أ();
في هذا المثال، لا يمكن استدعاء المتغير الذي يكون في الواقع رقمًا كدالة. سوف يلقي المتصفح خطأ مثل TypeError: a is not a function
ذات تتبع مكدس يشير إلى هذا السطر من التعليمات البرمجية.
قد يرغب المطور أيضًا في حدوث خطأ في جزء من التعليمات البرمجية إذا لم يتم استيفاء شرط مسبق معين. على سبيل المثال
إذا (! تحقق الشرط المسبق ()) { رمي خطأ جديد ("لا يستوفي الشرط المسبق!")؛}
في هذه الحالة، سيكون الخطأ هو Error: Doesn't meet precondition!
. سيحتوي هذا الخطأ أيضًا على تتبع مكدس يشير إلى السطر المناسب. يمكن معالجة الأخطاء التي يلقيها المتصفح ورمز التطبيق بنفس الطريقة.
هناك عدة طرق يمكن للمطورين من خلالها إلقاء خطأ في JavaScript:
throw new Error('Problem description.')
throw Error('Problem description.')
<-- يعادل الأول
throw 'Problem description.'
<-- سيء
throw null
<-- والأسوأ من ذلك
لا يُنصح بإلقاء سلسلة أو قيمة خالية نظرًا لأن المتصفح لن يقوم بإرفاق تتبع مكدس لهذا الخطأ، مما يؤدي إلى فقدان سياق مكان حدوث هذا الخطأ في الكود. من الأفضل رمي كائن خطأ فعلي، والذي سيحتوي على رسالة الخطأ بالإضافة إلى تتبع المكدس الذي يشير إلى الأسطر الصحيحة من التعليمات البرمجية حيث حدث الخطأ.
يحتوي كل متصفح على مجموعته الخاصة من الرسائل التي يستخدمها للاستثناءات المضمنة، مثل المثال أعلاه لمحاولة استدعاء وظيفة غير وظيفية. ستحاول المتصفحات استخدام نفس الرسائل، ولكن نظرًا لعدم وجود مواصفات، فهذا غير مضمون. على سبيل المثال، {0} is not a function
للمثال أعلاه بينما سيبلغ IE11 عن Function expected
(وخاصة أيضًا بدون الإبلاغ عن المتغير الذي تمت محاولة استدعائه).
ومع ذلك، تميل المتصفحات إلى الاختلاف في كثير من الأحيان أيضًا. عندما تكون هناك عدة عبارات افتراضية في عبارة switch
، سيطرح Chrome "More than one default clause in switch statement"
بينما سيُبلغ Firefox عن "more than one switch default"
. ومع إضافة ميزات جديدة إلى الويب، يجب تحديث رسائل الخطأ هذه. يمكن تفعيل هذه الاختلافات لاحقًا عندما تحاول معالجة الأخطاء المبلغ عنها من التعليمات البرمجية المبهمة.
يمكنك العثور على النماذج التي تستخدمها المتصفحات لرسائل الخطأ على:
فايرفوكس - http://mxr.mozilla.org/mozilla1.9.1/source/js/src/js.msg
كروم - https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/messages.js
إنترنت إكسبلورر - https://github.com/Microsoft/ChakraCore/blob/4e4d4f00f11b2ded23d1885e85fc26fcc96555da/lib/Parser/rterrors.h
ستنتج المتصفحات رسائل خطأ مختلفة لبعض الاستثناءات.
تتبع المكدس هو وصف لمكان حدوث الخطأ في الكود. وهو يتألف من سلسلة من الإطارات، حيث يصف كل إطار سطرًا معينًا في الكود. الإطار العلوي هو الموقع الذي تم فيه إلقاء الخطأ، في حين أن الإطارات اللاحقة هي مكدس استدعاءات الوظيفة - أو كيفية تنفيذ التعليمات البرمجية للوصول إلى تلك النقطة حيث تم إلقاء الخطأ. نظرًا لأن JavaScript عادة ما تكون متسلسلة ومصغرة، فمن المهم أيضًا أن يكون لديك أرقام أعمدة حتى يمكن تحديد موقع العبارة الدقيقة عندما يحتوي سطر معين على العديد من البيانات.
يبدو تتبع المكدس الأساسي في Chrome كما يلي:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
يتكون كل إطار مكدس من اسم الوظيفة (إذا كان ذلك ممكنًا ولم يتم تنفيذ التعليمات البرمجية في النطاق العام)، والبرنامج النصي الذي جاء منه، ورقم السطر والعمود من التعليمات البرمجية.
لسوء الحظ، لا يوجد معيار لتنسيق تتبع المكدس، لذلك يختلف هذا باختلاف المتصفح.
يبدو تتبع المكدس الخاص بـ Microsoft Edge وIE 11 مشابهًا لمتصفح Chrome باستثناء أنه يسرد التعليمات البرمجية العامة بشكل صريح:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:3) at Global code (http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3)
يبدو تتبع مكدس Firefox كما يلي:
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9 @http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
يشبه تنسيق Safari تنسيق Firefox ولكنه مختلف قليلاً أيضًا:
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:18 global code@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:13
نفس المعلومات الأساسية موجودة، لكن الشكل مختلف.
لاحظ أيضًا أنه في مثال Safari، وبصرف النظر عن اختلاف التنسيق عن Chrome، فإن أرقام الأعمدة تختلف عن كل من Chrome وFirefox. يمكن أيضًا أن تنحرف أرقام الأعمدة أكثر في حالات الخطأ المختلفة - على سبيل المثال في الكود (function namedFunction() { throwError(); })();
، سيقوم Chrome بالإبلاغ عن العمود لاستدعاء الدالة throwError()
بينما يقوم IE11 بالإبلاغ عن رقم العمود كبداية للسلسلة. ستعود هذه الاختلافات إلى العمل لاحقًا عندما يحتاج الخادم إلى تحليل تتبع المكدس بحثًا عن الأخطاء التي تم الإبلاغ عنها وإزالة تشويش تتبعات المكدس المبهمة.
راجع https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack لمزيد من المعلومات حول خاصية مكدس الأخطاء. عند الوصول إلى خاصية Error.stack، يقوم Chrome بتضمين رسالة الخطأ كجزء من المكدس ولكن Safari 10+ لا يفعل ذلك.
يختلف تنسيق تتبعات المكدس باختلاف المتصفح من حيث أرقام النماذج والأعمدة المستخدمة.
وبالتعمق أكثر، هناك الكثير من الفروق الدقيقة لتكديس تنسيقات التتبع التي تمت مناقشتها في الأقسام أدناه.
بشكل افتراضي، الوظائف المجهولة ليس لها اسم وتظهر إما كسلسلة فارغة أو "وظيفة مجهولة" في أسماء الوظائف في تتبع المكدس (اعتمادًا على المستعرض). لتحسين تصحيح الأخطاء، يجب عليك إضافة اسم لجميع الوظائف للتأكد من ظهوره في إطار المكدس. أسهل طريقة للقيام بذلك هي التأكد من تحديد الوظائف المجهولة باسم، حتى لو لم يتم استخدام هذا الاسم في أي مكان آخر. على سبيل المثال:
setTimeout(function nameOfTheAnonymousFunction() { ... }, 0);
سيؤدي هذا إلى انتقال تتبع المكدس من:
at http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17
ل
at nameOfTheAnonymousFunction (http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)
في Safari، سينتقل هذا من:
https://mknichel.github.io/javascript-errors/javascript-errors.js:175:27
ل
nameOfTheAnonymousFunction@https://mknichel.github.io/javascript-errors/javascript-errors.js:171:41
تضمن هذه الطريقة ظهور nameOfTheAnonymousFunction
في الإطار لأي كود من داخل تلك الوظيفة، مما يجعل تصحيح الأخطاء أسهل بكثير. راجع http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/#toc-debugging-tips لمزيد من المعلومات.
ستستخدم المتصفحات أيضًا اسم المتغير أو الخاصية التي تم تعيين الوظيفة لها إذا لم يكن للوظيفة نفسها اسم. على سبيل المثال، في
var fnVariableName = function() { ... };
ستستخدم المستعرضات fnVariableName
كاسم للوظيفة في تتبعات المكدس.
at throwError (http://mknichel.github.io/javascript-errors/javascript-errors.js:27:9) at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
والأكثر دقة من ذلك، إذا تم تعريف هذا المتغير ضمن وظيفة أخرى، فإن جميع المتصفحات ستستخدم فقط اسم المتغير كاسم للوظيفة في تتبع المكدس باستثناء Firefox، الذي سيستخدم نموذجًا مختلفًا يسلسل اسم الدالة الخارجية مع اسم المتغير الداخلي. مثال:
الدالة throwErrorFromInnerFunctionAssignedToVariable() { var fnVariableName = function() { throw new Error("foo"); }; fnVariableName();}
سوف تنتج في فايرفوكس:
throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37
في المتصفحات الأخرى، سيبدو هذا كما يلي:
at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
يستخدم Firefox نص إطار مكدس مختلف للوظائف المحددة ضمن وظيفة أخرى.
يمكن أيضًا تعيين اسم العرض للوظيفة من خلال خاصية displayName
في جميع المتصفحات الرئيسية باستثناء IE11. في هذه المتصفحات، سيظهر اسم العرض في مصحح أخطاء أدوات التطوير، ولكن في جميع المتصفحات باستثناء Safari، لن يتم استخدامه في تتبعات مكدس الأخطاء (يختلف Safari عن الباقي باستخدام اسم العرض أيضًا في تتبع المكدس المرتبط بالخطأ).
var someFunction = function() {};someFunction.displayName = " # وصف أطول للوظيفة.";
لا توجد مواصفات رسمية لخاصية DisplayName، ولكنها مدعومة من قبل جميع المتصفحات الرئيسية. راجع https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName وhttp://www.alertdebugging.com/2009/04/29/building-a-better -javascript-profiler-with-webkit/ لمزيد من المعلومات حول اسم العرض.
لا يدعم IE11 خاصية اسم العرض.
يستخدم Safari خاصية اسم العرض كاسم الرمز في تتبعات مكدس الأخطاء.
إذا تم الإبلاغ عن خطأ دون تتبع المكدس (راجع المزيد من التفاصيل عندما يحدث ذلك أدناه)، فمن الممكن التقاط تتبع المكدس برمجيًا.
في Chrome، من السهل جدًا القيام بذلك عن طريق استخدام Error.captureStackTrace
API. راجع https://github.com/v8/v8/wiki/Stack%20Trace%20API لمزيد من المعلومات حول استخدام واجهة برمجة التطبيقات هذه.
على سبيل المثال:
وظيفة تجاهلThisFunctionInStackTrace() { var err = new Error(); Error.captureStackTrace(err,ignoreThisFunctionInStackTrace); إرجاع خطأ.المكدس؛}
في المتصفحات الأخرى، يمكن أيضًا جمع تتبع المكدس عن طريق إنشاء خطأ جديد والوصول إلى خاصية المكدس لذلك الكائن:
var err = new Error('');return err.stack;
ومع ذلك، يقوم IE10 بملء تتبع المكدس فقط عندما يتم طرح الخطأ فعليًا:
يحاول { رمي خطأ جديد ('')؛} قبض (ه) { إرجاع e.stack;}
إذا لم تنجح أي من هذه الأساليب، فمن الممكن إنشاء تتبع مكدس تقريبي بدون أرقام الأسطر أو الأعمدة عن طريق التكرار على arguments.callee.caller
callee.caller - لن يعمل هذا في الوضع الصارم ES5 على الرغم من أنه ليس أسلوبًا موصى به.
من الشائع جدًا إدراج النقاط غير المتزامنة في تعليمات JavaScript البرمجية، كما هو الحال عندما تستخدم التعليمات البرمجية setTimeout
أو من خلال استخدام Promises. يمكن أن تتسبب نقاط الإدخال غير المتزامنة هذه في حدوث مشكلات لتتبع المكدس، نظرًا لأنها تتسبب في تكوين سياق تنفيذ جديد ويبدأ تتبع المكدس من البداية مرة أخرى.
يدعم Chrome DevTools تتبعات المكدس غير المتزامن، أو بمعنى آخر التأكد من أن تتبع المكدس للخطأ يعرض أيضًا الإطارات التي حدثت قبل تقديم نقطة المزامنة. باستخدام setTimeout، سيؤدي هذا إلى تحديد من قام باستدعاء وظيفة setTimeout التي أنتجت خطأً في النهاية. راجع http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/ لمزيد من المعلومات.
سيبدو تتبع المكدس غير المتزامن كما يلي:
throwError @ throw-error.js:2 setTimeout (async) throwErrorAsync @ throw-error.js:10 (anonymous function) @ throw-error-basic.html:14
لا يتم دعم عمليات تتبع المكدس غير المتزامن إلا في Chrome DevTools في الوقت الحالي، فقط للاستثناءات التي يتم طرحها عندما تكون DevTools مفتوحة. لن تحتوي تتبعات المكدس التي يتم الوصول إليها من كائنات الخطأ في التعليمات البرمجية على تتبع المكدس غير المتزامن كجزء منها.
من الممكن تعبئة آثار المكدس غير المتزامنة في بعض الحالات، ولكن هذا قد يتسبب في تحقيق أداء كبير لتطبيقك نظرًا لأن التقاط تتبع المكدس ليس رخيصًا.
فقط Chrome DevTools يدعم أصلاً تتبعات المكدس غير المتزامنة.
ستستخدم تتبعات المكدس للتعليمات البرمجية التي تم تقييمها أو تضمينها في صفحة HTML عنوان URL الخاص بالصفحة وأرقام الأسطر/الأعمدة للتعليمات البرمجية التي تم تنفيذها.
على سبيل المثال:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
إذا كانت هذه البرامج النصية تأتي بالفعل من برنامج نصي تم تضمينه لأسباب تتعلق بالتحسين، فسيكون عنوان URL والأسطر وأرقام الأعمدة خاطئة. للتغلب على هذه المشكلة، يدعم Chrome وFirefox التعليق التوضيحي //# sourceURL=
(لا يدعم ذلك Safari وEdge وIE). سيتم استخدام عنوان URL المحدد في هذا التعليق التوضيحي كعنوان URL لجميع تتبعات المكدس، وسيتم حساب رقم السطر والعمود بالنسبة إلى بداية علامة بدلاً من مستند HTML. بالنسبة لنفس الخطأ المذكور أعلاه، سيؤدي استخدام التعليق التوضيحي sourceURL بقيمة "inline.js" إلى إنتاج تتبع مكدس يبدو كما يلي:
at throwError (http://mknichel.github.io/javascript-errors/inline.js:8:9) at http://mknichel.github.io/javascript-errors/inline.js:12:3
هذه تقنية مفيدة حقًا للتأكد من أن تتبعات المكدس لا تزال صحيحة حتى عند استخدام البرامج النصية المضمنة والتقييم.
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl يصف التعليق التوضيحي لـ sourceURL بمزيد من التفاصيل.
لا يدعم Safari وEdge وIE التعليق التوضيحي sourceURL لتسمية البرامج النصية والتقييمات المضمنة. إذا كنت تستخدم البرامج النصية المضمنة في IE أو Safari وقمت بإخفاء التعليمات البرمجية الخاصة بك، فلن تتمكن من إزالة تشويش الأخطاء التي تأتي من تلك البرامج النصية.
حتى Chrome 42، لم يحسب Chrome أرقام الأسطر بشكل صحيح للنصوص البرمجية المضمنة التي تستخدم التعليق التوضيحي sourceURL. راجع https://bugs.chromium.org/p/v8/issues/detail?id=3920 لمزيد من المعلومات.
أرقام الأسطر لإطارات المكدس من البرامج النصية المضمنة غير صحيحة عند استخدام التعليق التوضيحي لعنوان URL المصدر نظرًا لأنها مرتبطة ببداية مستند HTML بدلاً من بداية علامة البرنامج النصي المضمنة (مما يجعل إزالة التشويش الصحيح غير ممكن). https://code.google.com/p/chromium/issues/detail?id=578269
بالنسبة للتعليمات البرمجية التي تستخدم التقييم، هناك اختلافات أخرى في تتبع المكدس إلى جانب ما إذا كان يستخدم التعليق التوضيحي sourceURL أم لا. في Chrome، يمكن أن يبدو تتبع المكدس من العبارة المستخدمة في eval كما يلي:
Error: Error from eval at evaledFunction (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3),:1:36) at eval (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3), :1:68) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
في MS Edge وIE11، سيبدو هذا كما يلي:
Error from eval at evaledFunction (eval code:1:30) at eval code (eval code:1:2) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
في سفاري:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
وفي فايرفوكس:
Error from eval evaledFunction@http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:36 @http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:11 evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3
قد تجعل هذه الاختلافات من الصعب تحليل كود التقييم نفسه عبر جميع المتصفحات.
يستخدم كل متصفح تنسيقًا مختلفًا لتتبع المكدس للأخطاء التي تحدث داخل التقييم.
يمكن أيضًا استدعاء كود JavaScript الخاص بك مباشرة من الكود الأصلي. يعد Array.prototype.forEach
مثالًا جيدًا - حيث تقوم بتمرير دالة إلى forEach
وسيقوم محرك JS باستدعاء هذه الوظيفة نيابةً عنك.
الدالة throwErrorWithNativeFrame() { فار آر = [0, 1, 2, 3]; arr.forEach(functionnamedFn(value) {throwError(); });}
يؤدي هذا إلى إنتاج آثار مكدس مختلفة في متصفحات مختلفة. يُلحق Chrome وSafari اسم الوظيفة الأصلية في تتبع المكدس نفسه كإطار منفصل، مثل:
(Chrome) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.forEach (native) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7) (Safari) namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:15 forEach@[native code] throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:14 (Edge) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.prototype.forEach (native code) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7)
ومع ذلك، لا يُظهر Firefox وIE11 أنه تم استدعاء forEach
كجزء من المكدس:
namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5 throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:3
تتضمن بعض المتصفحات إطارات التعليمات البرمجية الأصلية في تتبعات المكدس، بينما لا تفعل المتصفحات الأخرى ذلك.
لاكتشاف وجود خطأ في تطبيقك، يجب أن تكون بعض التعليمات البرمجية قادرة على اكتشاف هذا الخطأ والإبلاغ عنه. هناك تقنيات متعددة لاكتشاف الأخطاء، ولكل منها إيجابيات وسلبيات.
يعد window.onerror
أحد أسهل الطرق وأفضلها للبدء في اكتشاف الأخطاء. من خلال تعيين window.onerror
لوظيفة ما، سيتم الإبلاغ عن أي خطأ لم يتم اكتشافه بواسطة جزء آخر من التطبيق إلى هذه الوظيفة، بالإضافة إلى بعض المعلومات حول الخطأ. على سبيل المثال:
window.onerror = function(msg, url, line, col, err) { console.log("واجه التطبيق خطأ:" + msg); console.log("تتبع المكدس:" + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror يصف هذا بمزيد من التفصيل.
تاريخياً، كانت هناك بعض المشاكل في هذا النهج:
لم يتم توفير كائن خطأ
من المفترض أن تكون الوسيطة الخامسة للدالة window.onerror
كائن خطأ. تمت إضافة هذا إلى مواصفات WHATWG في عام 2013: https://html.spec.whatwg.org/multipage/webappapis.html#errorevent. يوفر Chrome وFirefox وIE11 الآن كائن خطأ بشكل صحيح (جنبًا إلى جنب مع خاصية المكدس الحرج)، لكن Safari وMS Edge وIE10 لا يقدمون ذلك. يعمل هذا في Firefox منذ الإصدار 14 من Firefox (https://bugzilla.mozilla.org/show_bug.cgi?id=355430) وفي Chrome منذ أواخر عام 2013 (https://mikewest.org/2013/08/debugging-runtime-errors -with-window-onerror، https://code.google.com/p/chromium/issues/detail?id=147127). أطلق Safari 10 دعمًا لكائن الخطأ في window.onerror.
لا يدعم Safari (الإصدارات الأقل من 10) وMS Edge وIE10 كائن الخطأ مع تتبع المكدس في window.onerror.
التطهير عبر المجال
في Chrome، سيتم تصحيح الأخطاء التي تأتي من مجال آخر في معالج window.onerror إلى "خطأ في البرنامج النصي."، ""، 0. وهذا أمر مقبول بشكل عام إذا كنت لا تريد حقًا معالجة الخطأ إذا كان يأتي من البرنامج النصي الذي لا يهمك، حتى يتمكن التطبيق من تصفية الأخطاء التي تبدو بهذا الشكل. ومع ذلك، لا يحدث هذا في Firefox أو Safari أو IE11، ولا يفعل Chrome ذلك مع كتل المحاولة/الالتقاط التي تغلف التعليمات البرمجية المخالفة.
إذا كنت ترغب في تلقي الأخطاء في window.onerror
في Chrome بدقة كاملة من البرامج النصية عبر النطاقات، فيجب أن توفر هذه الموارد الرؤوس المشتركة المناسبة. راجع https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror لمزيد من المعلومات.
Chrome هو المتصفح الوحيد الذي سيصحح الأخطاء التي تأتي من مصدر آخر. احرص على تصفية هذه العناصر، أو تعيين الرؤوس المناسبة.
ملحقات كروم
في الإصدارات القديمة من Chrome، قد تؤدي ملحقات Chrome المثبتة على جهاز المستخدم أيضًا إلى ظهور أخطاء يتم الإبلاغ عنها إلى window.onerror. وقد تم إصلاح هذه المشكلة في الإصدارات الأحدث من Chrome. راجع قسم ملحقات Chrome المخصص أدناه.
تعمل واجهة برمجة window.addEventListener("error")
بنفس طريقة واجهة برمجة تطبيقات window.onerror. راجع http://www.w3.org/html/wg/drafts/html/master/webappapis.html#runtime-script-errors لمزيد من المعلومات حول هذا الأسلوب.
لا يؤدي اكتشاف الأخطاء عبر window.onerror إلى منع ظهور هذا الخطأ أيضًا في وحدة تحكم DevTools. هذا هو على الأرجح السلوك الصحيح للتطوير حيث يمكن للمطور رؤية الخطأ بسهولة. إذا كنت لا تريد أن تظهر هذه الأخطاء في الإنتاج للمستخدمين النهائيين، فيمكن استدعاء e.preventDefault()
في حالة استخدام الأسلوب window.addEventListener.
window.onerror هي أفضل أداة لاكتشاف أخطاء JS والإبلاغ عنها. من المستحسن أن يتم الإبلاغ عن أخطاء JS التي تحتوي على كائنات خطأ صالحة وتتبعات المكدس فقط إلى الخادم، وإلا فقد يكون من الصعب التحقيق في الأخطاء أو قد تحصل على الكثير من البريد العشوائي من ملحقات Chrome أو البرامج النصية عبر النطاقات.
بالنظر إلى القسم أعلاه، لسوء الحظ، لا يمكن الاعتماد على window.onerror
في جميع المتصفحات لالتقاط جميع معلومات الخطأ. للحصول على الاستثناءات محليًا، فإن كتلة المحاولة/الالتقاط هي الخيار الواضح. من الممكن أيضًا التفاف ملفات JavaScript بأكملها في كتلة حاول/التقاط لالتقاط معلومات الخطأ التي لا يمكن اكتشافها باستخدام window.onerror. يعمل هذا على تحسين أوضاع المتصفحات التي لا تدعم window.onerror، ولكن له أيضًا بعض الجوانب السلبية.
لن تلتقط كتلة المحاولة/الالتقاط جميع الأخطاء في البرنامج، مثل الأخطاء التي يتم طرحها من كتلة التعليمات البرمجية غير المتزامنة من خلال window.setTimeout
. يمكن استخدام Try/catch مع نقاط الدخول المحمية للمساعدة في سد الثغرات.
كتل المحاولة/الالتقاط التي تغطي التطبيق بأكمله ليست كافية لالتقاط جميع الأخطاء.
لن يتم تحسين الإصدارات القديمة من V8 (وربما محركات JS الأخرى)، والوظائف التي تحتوي على كتلة محاولة/التقاط بواسطة المترجم (http://www.html5rocks.com/en/tutorials/speed/v8/). أصلح Chrome هذا الأمر في TurboFan (https://codereview.chromium.org/1996373002).
"نقطة الدخول" إلى JavaScript هي أي واجهة برمجة تطبيقات للمتصفح يمكنها بدء تنفيذ التعليمات البرمجية الخاصة بك. تتضمن الأمثلة setTimeout
أو setInterval
أو مستمعي الأحداث أو XHR أو مقابس الويب أو الوعود. سيتم اكتشاف الأخطاء التي يتم طرحها من نقاط الإدخال هذه بواسطة window.onerror، ولكن في المتصفحات التي لا تدعم كائن الخطأ الكامل في window.onerror، هناك حاجة إلى آلية بديلة لالتقاط هذه الأخطاء نظرًا لطريقة المحاولة/الالتقاط المذكورة أعلاه لن يمسك بهم أيضًا.
لحسن الحظ، تسمح JavaScript بتغليف نقاط الإدخال هذه بحيث يمكن إدراج كتلة محاولة/التقاط قبل استدعاء الوظيفة لالتقاط أي أخطاء يرميها الكود.
ستحتاج كل نقطة إدخال إلى رمز مختلف قليلاً لحماية نقطة الإدخال، ولكن جوهر المنهجية هو:
وظيفة حمايةEntryPoint(fn) { وظيفة الإرجاع protectedFn() {try { return fn();} Catch (e) { // التعامل مع الخطأ.} }}_oldSetTimeout = window.setTimeout;window.setTimeout = function protectedSetTimeout(fn, time) { return _oldSetTimeout.call(window, protectedEntryPoint(fn), time);};
للأسف، من السهل أن تمر الأخطاء التي تحدث في Promises دون أن تتم ملاحظتها أو الإبلاغ عنها. الأخطاء التي تحدث في Promise ولكن لا يتم التعامل معها عن طريق إرفاق معالج الرفض لا يتم الإبلاغ عنها في أي مكان آخر - ولا يتم الإبلاغ عنها إلى window.onerror
. حتى لو أرفق الوعد معالج رفض، فيجب على هذا الكود نفسه الإبلاغ يدويًا عن تلك الأخطاء حتى يتم تسجيله. راجع http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling لمزيد من المعلومات. على سبيل المثال:
window.onerror = الوظيفة(...) { // لن يتم استدعاء هذا أبدًا بواسطة كود الوعد.};var p = new Promise(...);p.then(function() { throw new Error("لن تتم معالجة هذا الخطأ في أي مكان.");});var p2 = new Promise(...);p2.then(function() { رمي خطأ جديد ("ستتم معالجة هذا الخطأ في السلسلة.")؛}).catch(function(error) { // عرض رسالة الخطأ للمستخدم // يجب أن يقوم هذا الرمز بالإبلاغ عن الخطأ يدويًا حتى يتم تسجيله على الخادم، إن أمكن.});
تتمثل إحدى الطرق للحصول على مزيد من المعلومات في استخدام نقاط الإدخال المحمية لتغليف استدعاءات أساليب Promise مع محاولة/التقاط للإبلاغ عن الأخطاء. قد يبدو هذا كالتالي:
var _oldPromiseThen = Promise.prototype.then; Promise.prototype.then = function protectedThen(callback, errorHandler) {return _oldPromiseThen.call(this, protectedEntryPoint(callback), protectedEntryPoint(errorHandler)); };
للأسف، لن تتم معالجة الأخطاء الواردة من Promises افتراضيًا.
تعالج تطبيقات الوعود، مثل Q وBluebird وClosure الأخطاء بطرق مختلفة أفضل من معالجة الأخطاء في تطبيق المتصفح لـ Promises.
في Q، يمكنك "إنهاء" سلسلة Promise عن طريق استدعاء .done()
الذي سيتأكد من أنه إذا لم تتم معالجة الخطأ في السلسلة، فسيتم إعادة طرحه والإبلاغ عنه. راجع https://github.com/kriskowal/q#handling-errors
في بلوبيرد، يتم تسجيل حالات الرفض غير المعالجة والإبلاغ عنها على الفور. راجع http://bluebirdjs.com/docs/features.html#surfacing-unhandled-errors
في تنفيذ goog.Promise الخاص بـ Closure، يتم تسجيل حالات الرفض غير المعالجة والإبلاغ عنها إذا لم تعالج أي سلسلة في Promise الرفض خلال فترة زمنية قابلة للتكوين (للسماح للتعليمة البرمجية لاحقًا في البرنامج بإضافة معالج رفض).
يناقش قسم تتبع المكدس غير المتزامن أعلاه أن المتصفحات لا تلتقط معلومات المكدس عندما يكون هناك ربط غير متزامن، مثل استدعاء Promise.prototype.then
. تتميز عمليات polyfills الوعدية بطريقة لالتقاط نقاط تتبع المكدس غير المتزامن والتي يمكن أن تجعل تشخيص الأخطاء أسهل بكثير. يعد هذا الأسلوب مكلفًا، ولكنه قد يكون مفيدًا حقًا في التقاط المزيد من معلومات تصحيح الأخطاء.
في Q، اتصل Q.longStackSupport = true;
. راجع https://github.com/kriskowal/q#long-stack-traces
في Bluebird، اتصل بـ Promise.longStackTraces()
في مكان ما في التطبيق. راجع http://bluebirdjs.com/docs/features.html#long-stack-traces.
في الإغلاق، قم بتعيين goog.Promise.LONG_STACK_TRACES
إلى true.
أضاف Chrome 49 دعمًا للأحداث التي يتم إرسالها عند رفض الوعد. يتيح ذلك للتطبيقات الارتباط بأخطاء Promise لضمان الإبلاغ عنها مركزيًا بالإضافة إلى بقية الأخطاء.
window.addEventListener('unhandledrejection', events => { // Event.reason يحتوي على سبب الرفض. عندما يتم طرح خطأ، فهذا هو كائن الخطأ.});
راجع https://googlechrome.github.io/samples/promise-rejection-events/ وhttps://www.chromestatus.com/feature/4805872211460096 لمزيد من المعلومات.
وهذا غير مدعوم في أي متصفح آخر.
أصبح عمال الويب، بما في ذلك العمال المخصصون والعاملون المشتركون وعمال الخدمة، أكثر شيوعًا في التطبيقات اليوم. نظرًا لأن كل هؤلاء العمال عبارة عن نصوص برمجية منفصلة عن الصفحة الرئيسية، فإن كل منهم يحتاج إلى كود معالجة الأخطاء الخاص به. من المستحسن أن يقوم كل برنامج نصي عامل بتثبيت رمز معالجة الأخطاء والإبلاغ الخاص به لتحقيق أقصى قدر من الفعالية في التعامل مع أخطاء العمال.
يقوم عمال الويب المخصصون بالتنفيذ في سياق تنفيذ مختلف عن الصفحة الرئيسية، لذلك لا يتم اكتشاف أخطاء العمال بواسطة الآليات المذكورة أعلاه. يجب اتخاذ خطوات إضافية لالتقاط الأخطاء من العاملين على الصفحة.
عند إنشاء عامل، يمكن تعيين خاصية onerror على العامل الجديد:
var عامل = new Worker('worker.js');worker.onerror = function(errorEvent) { ... };
تم تعريف ذلك في https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror. تحتوي وظيفة onerror
الموجودة على العامل على توقيع مختلف عن window.onerror
الذي تمت مناقشته أعلاه. بدلاً من قبول 5 وسيطات، يأخذ worker.onerror
وسيطة واحدة: كائن ErrorEvent
. يمكن العثور على واجهة برمجة التطبيقات لهذا الكائن على https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent. فهو يحتوي على الرسالة واسم الملف والسطر والعمود، ولكن لا يوجد متصفح مستقر اليوم يحتوي على كائن "خطأ" الذي يحتوي على تتبع المكدس (خطأ errorEvent.error فارغ). نظرًا لأنه يتم تنفيذ واجهة برمجة التطبيقات هذه في نطاق الصفحة الرئيسية، فسيكون من المفيد استخدام نفس آلية إعداد التقارير مثل الصفحة الرئيسية؛ لسوء الحظ، نظرًا لعدم وجود تتبع المكدس، فإن واجهة برمجة التطبيقات (API) هذه ذات استخدام محدود.
داخل JS الذي يديره العامل، يمكنك أيضًا تحديد واجهة برمجة تطبيقات onerror التي تتبع واجهة برمجة تطبيقات window.onerror المعتادة: https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler. في كود العامل:
self.onerror = function(message, filename, line, col, error) { ... };
تتبع مناقشة واجهة برمجة التطبيقات (API) هذه في الغالب المناقشة أعلاه بخصوص window.onerror. ومع ذلك، هناك أمران جديران بالملاحظة يجب الإشارة إليهما:
لا يُبلغ Firefox وSafari عن كائن "الخطأ" باعتباره الوسيط الخامس للوظيفة، لذلك لا تحصل هذه المتصفحات على تتبع المكدس من العامل (يحصل Chrome وMS Edge وIE11 على تتبع المكدس). يمكن استخدام نقاط الإدخال المحمية لوظيفة onmessage
داخل العامل لالتقاط معلومات تتبع المكدس لهذه المتصفحات.
نظرًا لأن هذا الكود يتم تنفيذه داخل العامل، فيجب أن يختار الكود كيفية الإبلاغ عن الخطأ مرة أخرى إلى الخادم: يجب عليه إما استخدام postMessage
لتوصيل الخطأ مرة أخرى إلى الصفحة الرئيسية، أو تثبيت آلية الإبلاغ عن أخطاء XHR (تتم مناقشة المزيد أدناه) في العامل نفسه .
في Firefox وSafari وIE11 (ولكن ليس في Chrome)، سيتم أيضًا استدعاء وظيفة window.onerror
الخاصة بالصفحة الرئيسية بعد استدعاء خطأ العامل الخاص ومستمع حدث onerror الذي تم تعيينه بواسطة الصفحة. ومع ذلك، لن يحتوي window.onerror هذا أيضًا على كائن خطأ وبالتالي لن يحتوي على تتبع مكدس أيضًا. ويجب أن تحرص هذه المتصفحات أيضًا على عدم الإبلاغ عن الأخطاء من العاملين عدة مرات.
يدعم Chrome وFirefox واجهة SharedWorker API لمشاركة عامل بين صفحات متعددة. نظرًا لأن العامل مشترك، فهو غير مرتبط بصفحة رئيسية واحدة بشكل حصري؛ يؤدي هذا إلى بعض الاختلافات في كيفية معالجة الأخطاء، على الرغم من أن SharedWorker يتبع في الغالب نفس المعلومات التي يتبعها عامل الويب المخصص.
في Chrome، عندما يكون هناك خطأ في SharedWorker، سيتم استدعاء معالجة الأخطاء الخاصة بالعامل فقط داخل رمز العامل نفسه (مثلما إذا قاموا بتعيين self.onerror
). لن يتم استدعاء window.onerror
للصفحة الأصلية، ولا يدعم Chrome خطأ AbstractWorker.onerror
الموروث الذي يمكن استدعاؤه في الصفحة الأصلية كما هو محدد في المواصفات.
في Firefox، هذا السلوك مختلف. سيؤدي حدوث خطأ في العامل المشترك إلى استدعاء window.onerror للصفحة الرئيسية، لكن كائن الخطأ سيكون فارغًا. بالإضافة إلى ذلك، يدعم Firefox خاصية AbstractWorker.onerror
، بحيث يمكن للصفحة الأصلية إرفاق معالج أخطاء خاص بها بالعامل. ومع ذلك، عند استدعاء معالج الأخطاء هذا، سيكون كائن الخطأ خاليًا، لذا لن يكون هناك تتبع مكدس، لذا فهو ذو استخدام محدود.
تختلف معالجة الأخطاء للعاملين المشتركين حسب المتصفح.
عمال الخدمة عبارة عن مواصفات جديدة تمامًا ولا تتوفر حاليًا إلا في إصدارات Chrome وFirefox الحديثة. يتبع هؤلاء العمال نفس المناقشة التي يتبعها عمال الويب المخصصون.
يتم تثبيت عمال الخدمة عن طريق استدعاء وظيفة navigator.serviceWorker.register
. تُرجع هذه الوظيفة وعدًا سيتم رفضه إذا حدث خطأ في تثبيت عامل الخدمة، مثل ظهور خطأ أثناء التهيئة. سيحتوي هذا الخطأ على رسالة سلسلة فقط ولا شيء غير ذلك. بالإضافة إلى ذلك، نظرًا لأن Promises لا تُبلغ عن الأخطاء إلى معالجات window.onerror
، فسيتعين على التطبيق نفسه إضافة كتلة التقاط إلى Promise لالتقاط الخطأ.
navigator.serviceWorker.register('service-worker-installation-error.js').catch(function(error) { // نوع الخطأ في السلسلة});
تمامًا مثل العمال الآخرين، يمكن لعمال الخدمة تعيين وظيفة self.onerror
داخل عمال الخدمة لاكتشاف الأخطاء. سيتم الإبلاغ عن أخطاء التثبيت في عامل الخدمة إلى وظيفة onerror، ولكن لسوء الحظ لن تحتوي على كائن خطأ أو تتبع مكدس.
تحتوي واجهة برمجة تطبيقات عامل الخدمة على خاصية onerror موروثة من واجهة AbstractWorker، لكن Chrome لا يفعل أي شيء