חידון: שליטה מתקדמת בשגיאות JS
האם החריגות שלך באמת חריגות?
חושבים שאתם מכירים שגיאות JavaScript מבפנים ומבחוץ?
- בחנו את המומחיות שלכם בטיפול בשגיאות! 💥
- אין צורך בהתחברות או הרשמה. ✨
- רב-ברירה. 🤖 … אלו לא שאלות try-catch טיפוסיות!
מה מחזירה JSON.stringify(error)?
const error = new Error('Oops');console.log(JSON.stringify(error));חשוב על מאפיינים ניתנים למנייה באובייקטי 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) מציג את השגיאה עם ההודעה ומחסנית הקריאות כי לקונסול יש טיפול מיוחד באובייקטי Error. JSON.stringify(err) מחזיר '{}' כי מאפייני Error אינם ניתנים למנייה. ההבדל הזה מבלבל מפתחים רבים בעת ניפוי באגים ב-API.
מה התוצאות של בדיקות אלו?
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 היא תבנית נפוצה כדי להבטיח שמחלקות שגיאה מותאמות אישית יציגו את השם הנכון ב-stack traces ובהודעות שגיאה.
מה הפלט מבלי להגדיר 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 מחזירה "Original error". זה שימושי לעטיפת שגיאות ברמה נמוכה עם הקשר ברמה גבוהה יותר.
מה עושה 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 routeapp.get('/api/data', (req, res) => { const error = new Error('Database failed'); res.json({ error });});זכור כיצד אובייקטי 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 במקרה זה) ואינם חלק מאובייקט השגיאה הסטנדרטי. שגיאות דפדפן לא יכילו מאפיינים אלה. תמיד בדוק את קיומם: 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, אלגוריתמים ועוד!