DanLevy.net

اختبار: إتقان أخطاء جافا سكريبت المتقدمة

هل استثناءاتك استثنائية حقًا؟

هل تعتقد أنك تعرف أخطاء JavaScript من الداخل والخارج؟

ماذا يُرجع JSON.stringify(error)؟

const error = new Error('Oops');
console.log(JSON.stringify(error));

فكر في الخصائص القابلة للتعداد في كائنات الخطأ.

كائنات الخطأ تحتوي على خصائص غير قابلة للتعداد (message, name, stack)، لذا فإن JSON.stringify() يُرجع {}. هذا خطأ شائع عند إرسال الأخطاء في استجابات API. استخدم JSON.stringify(error, Object.getOwnPropertyNames(error)) أو أنشئ كائنًا عاديًا بدلاً من ذلك.

ما الفرق بين هذين؟

const err = new Error('Test');
console.log(err);
console.log(JSON.stringify(err));

فكر في كيفية تعامل console.log مع الكائنات مقابل تسلسل JSON.

يظهر console.log(err) الخطأ مع رسالته وتتبع المكدس لأن وحدة التحكم لديها معالجة خاصة لكائنات الخطأ. بينما يُرجع JSON.stringify(err) '{}' لأن خصائص الخطأ غير قابلة للتعداد. هذا الاختلاف يوقع العديد من المطورين في حيرة أثناء تصحيح واجهات برمجة التطبيقات.

ما هي نتائج هذه الفحوصات؟

class CustomError extends Error {}
const err = new CustomError('test');
console.log(err instanceof CustomError);
console.log(err instanceof Error);
console.log(err instanceof Object);

تذكر سلسلة النموذج الأولي في وراثة JavaScript.

جميعها ترجع true. CustomError يمتد من Error، والذي يمتد من Object. عامل instanceof يتحقق من سلسلة النموذج الأولي بأكملها، لذا فإن مثيل CustomError هو أيضًا مثيل لكل من Error و Object.

ماذا يحدث مع instanceof Error عبر iframes؟

// In iframe:
const iframeError = new Error('test');
// In parent window:
console.log(iframeError instanceof Error);

السياقات المختلفة لها منشئات Error مختلفة.

يمكن أن يُرجع instanceof القيمة false عبر سياقات التنفيذ المختلفة (iframes, workers) لأن كل سياق له منشئ Error الخاص به. استخدم Object.prototype.toString.call(obj) === '[object Error]' للكشف الموثوق عن الأخطاء عبر السياقات.

ماذا يحدث عندما ترمي سلسلة نصية؟

try {
throw "Oops!";
} catch (e) {
console.log(e instanceof Error);
console.log(typeof e);
}

تسمح JavaScript برمي أي قيمة، وليس فقط كائنات الخطأ.

تسمح JavaScript برمي أي قيمة. هنا، e instanceof Error هو false و typeof e هو "string". هذا يمكن أن يكسر كود معالجة الأخطاء الذي يفترض أن جميع الاستثناءات الملتقطة هي كائنات خطأ. قم دائمًا برمي مثيلات Error لتحسين التصحيح.

ما هي قيمة err.name؟

class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
const err = new CustomError('test');
console.log(err.name);

انظر إلى ما ينتجه this.constructor.name.

err.name هي "CustomError" لأن this.constructor.name يعيد اسم الفئة. تعيين this.name = this.constructor.name هو نمط شائع لضمان عرض اسم الفئة المخصصة بشكل صحيح في تتبع الأخطاء ورسائل الأخطاء.

ما الناتج دون تعيين name؟

class MyError extends Error {
// No constructor or name setting
}
const err = new MyError('test');
console.log(err.name);

ماذا تحتوي خاصية name الافتراضية في Error؟

بدون تعيين this.name بشكل صريح، يرث الخطأ خاصية name الافتراضية من الكلاس Error، وهي "Error". لهذا السبب يجب على كلاسات الأخطاء المخصصة دائمًا تعيين this.name = this.constructor.name في المُنشئ الخاص بها.

ماذا تُرجع wrapper.cause.message؟

const original = new Error('Original error');
const wrapper = new Error('Wrapper',
{ cause: original }
);
console.log(wrapper.cause.message);

Error.cause هي ميزة حديثة في JavaScript لسلسلة الأخطاء.

Error.cause (ES2022) تسمح بسلسلة الأخطاء للحفاظ على سياق الخطأ الأصلي. wrapper.cause يشير إلى الخطأ الأصلي، لذا wrapper.cause.message تُرجع "الخطأ الأصلي". هذا مفيد لتغليف الأخطاء ذات المستوى المنخفض بسياق ذي مستوى أعلى.

ماذا تفعل Error.captureStackTrace؟

function createError(msg) {
const err = new Error(msg);
Error.captureStackTrace(err, createError);
return err;
}
const error = createError('test');

هذه ميزة خاصة بـ V8 لتتبعات مكدس أنظف.

Error.captureStackTrace (V8/Node.js) تزيل الدالة المحددة (createError) من تتبع المكدس، مما يجعل دوال إنشاء الأخطاء غير مرئية للمستخدمين النهائيين. هذا يخلق تتبعات مكدس أنظف تشير إلى مكان استدعاء الدالة المصنعة، وليس الدالة نفسها.

ما هي رسالة الخطأ؟

function validate(value) {
if (!value) {
throw new Error(
`Value ${value} is invalid`
);
}
}
try {
validate(undefined);
} catch (e) {
console.log(e.message);
}

كيف يتعامل القالب النصي مع القيمة undefined أثناء الاستيفاء؟

تقوم القوالب النصية بتحويل undefined إلى السلسلة "undefined" أثناء الاستيفاء. تصبح رسالة الخطأ "Value undefined is invalid". للحصول على رسائل أنظف، فكر في استخدام value ?? 'null' أو فحوصات مماثلة قبل الاستيفاء.

ما الذي يتم إرساله إلى العميل؟

// Express.js route
app.get('/api/data', (req, res) => {
const error = new Error('Database failed');
res.json({ error });
});

تذكر كيف يتم تسلسل كائنات الخطأ بواسطة JSON.stringify.

يستخدم res.json() داخليًا JSON.stringify()، لذلك يصبح كائن الخطأ {}. يتلقى العميل {"error":{}}. لإصلاح ذلك، استخدم res.json({ error: error.message }) أو res.json({ error: { message: error.message, name: error.name } }).

ماذا يمكن أن يقبل Promise.reject()؟

Promise.reject('string').catch(e =>
console.log(typeof e)
);
Promise.reject({code: 404}).catch(e =>
console.log(e.code)
);
Promise.reject(42).catch(e =>
console.log(e)
);

تعمل رفضات Promise بشكل مشابه لعبارات throw.

مثل throw، يقبل Promise.reject() أي قيمة - سلاسل نصية، كائنات، أرقام، إلخ. هذا يطبع "string" و 404 و 42. تحقق دائمًا من نوع القيم الملتقطة في سلاسل الـ Promise، خاصة عند التعامل مع كود طرف ثالث قد يرفض بقيم غير Error.

ما مدى موثوقية error.code و error.errno؟

const fs = require('fs');
fs.readFile('missing.txt', (err, data) => {
if (err) {
console.log(err.code); // 'ENOENT'
console.log(err.errno); // -2
}
});

ضع في اعتبارك بيئات JavaScript المختلفة وأنواع الأخطاء.

الخصائص مثل code و errno خاصة بالبيئة (Node.js في هذه الحالة) وليست جزءًا من كائن Error القياسي. أخطاء المتصفح لن تحتوي على هذه الخصائص. تحقق دائمًا من وجودها: if (err.code === 'ENOENT') بدلاً من افتراض وجودها.

ماذا تُرجع هذه الفحوصات؟

const fakeError = {
name: 'Error',
message: 'Fake error',
stack: 'fake stack'
};
console.log(fakeError instanceof Error);
console.log(Object.prototype.toString.call(
fakeError
) === '[object Error]');

أحد الفحوصات ينظر إلى سلسلة النموذج الأولي، والآخر إلى الفتحات الداخلية.

instanceof Error تُرجع false لأن الكائن لم يُنشأ بواسطة مُنشئ Error. Object.prototype.toString.call() تُرجع أيضًا false (تُرجع '[object Object]') لأنها تفحص الفتحة الداخلية [[Class]]. كلا الطريقتين تحددان بشكل صحيح أن هذا كائن خطأ مزيف.

أتقن فن معالجة الأخطاء

من مزالق التسلسل إلى فشل instanceof عبر السياقات، هذه المفاهيم المتقدمة تفصل المطورين المبتدئين عن المحترفين المحنكين المتضررين.

هل أنت مستعد لمزيد من التحديات؟ اطلع على مجموعة الاختبارات الكاملة لمزيد من الألغاز الذهنية حول JavaScript والخوارزميات والمزيد!