آموزش جاوا اسکریپت مبتدی : اشکالزدایی و مدیریت ارور در جاوا اسکریپت
در این درس، به موضوع اشکالزدایی جاوا اسکریپت برمیگردیم. در اینجا عمیقتر به تکنیکهای ردیابی خطاها میپردازیم و توضیح میدهیم که چگونه به صورت تدافعی کدنویسی کنید و خطاها را در کد خود مدیریت کنید و از همان ابتدا از بروز مشکلات جلوگیری کنید.
خلاصهای از انواع خطاهای جاوا اسکریپت
پیش از این در این ماژول، در بخش
«چه مشکلی پیش آمد؟»، به طور گسترده به انواع خطاهایی که میتوانند در برنامههای جاوااسکریپت رخ دهند، پرداختیم و گفتیم که میتوان آنها را تقریباً به دو نوع تقسیم کرد - خطاهای نحوی و خطاهای منطقی. همچنین به شما کمک کردیم تا با برخی از انواع رایج پیامهای خطای جاوااسکریپت آشنا شوید و به شما نشان دادیم که چگونه با استفاده از دستورات اشکالزدایی سادهای انجام دهید.
در این مقاله، نگاهی عمیقتر به ابزارهای موجود برای ردیابی خطاها خواهیم داشت و همچنین به روشهایی برای جلوگیری از خطاها در وهله اول خواهیم پرداخت.
قبل از تلاش برای ردیابی خطاهای خاص، ابتدا باید کد خود را اعتبارسنجی کنید. از سرویس اعتبارسنجی Markup W3C، سرویس اعتبارسنجی CSS و یک خطکش جاوا اسکریپت مانند ESLint برای اطمینان از اعتبار کد خود استفاده کنید. این کار احتمالاً برخی از خطاها را نشان میدهد که میتوانید آنها را برطرف کنید و به شما امکان میدهد روی خطاهای باقی مانده تمرکز کنید.
خیلی راحت نیست که مجبور باشید کد خود را کپی و در یک صفحه وب قرار دهید تا اعتبار آن را بارها و بارها بررسی کنید. توصیه میکنیم یک افزونه linter را در ویرایشگر کد خود نصب کنید تا هنگام نوشتن کد، خطاها را گزارش دهد. سعی کنید ESLint را در فهرست افزونهها یا افزونههای ویرایشگر کد خود جستجو کرده و آن را نصب کنید.
چندین مشکل رایج جاوا اسکریپت وجود دارد که باید به آنها توجه داشته باشید، مانند:
مشکلات اساسی نحوی و منطقی (به عیبیابی جاوا اسکریپت مراجعه کنید).
اطمینان از اینکه متغیرها و غیره در دامنه صحیح تعریف شدهاند و شما با تداخل بین مواردی که در مکانهای مختلف اعلام شدهاند، مواجه نمیشوید (به دامنه و تداخلهای تابع مراجعه کنید).
سردرگمی در مورد این موضوع، از نظر اینکه به چه دامنهای اعمال میشود، و بنابراین آیا مقدار آن همان چیزی است که شما در نظر داشتید. میتوانید برای یک مقدمه سبک، «this» چیست؟ را بخوانید. همچنین باید مثالهای دیگر ماژول راه مطالعه کنید تا مطمئن شوید که عملکرد را در دامنه this صحیح اعمال میکنید.
استفاده نادرست از توابع درون حلقههایی که با یک متغیر سراسری تکرار میشوند (به طور کلی، «اشتباه گرفتن دامنه»).
قبل از تلاش برای استفاده از مقادیری که برمیگردانند، مطمئن شوید که عملیات ناهمزمان تکمیل شدهاند. این معمولاً به معنای درک نحوه استفاده از promiseها است: استفاده مناسب از await یا اجرای کد برای مدیریت نتیجه یک فراخوانی ناهمزمان در کنترلکننده promise. برای آشنایی با این موضوع، به نحوه استفاده از promiseها مراجعه کنید.
کنسول جاوا اسکریپت مرورگر
ابزارهای توسعهدهنده مرورگر ویژگیهای مفید زیادی برای اشکالزدایی جاوا اسکریپت دارند. برای شروع، کنسول جاوا اسکریپت خطاهای موجود در کد شما را گزارش میدهد.
به مثال زیر توجه کنید. در اجرای چنین کدی اگر به کنسول نگاه کنید، یک پیام خطا خواهید دید. متن دقیق آن به مرورگر بستگی دارد، اما چیزی شبیه به این خواهد بود: . اگر به کد منبع نگاه کنیم، بخش کد مربوطه این است:
بنابراین به محض اینکه سعی میکنیم از jsonObj استفاده کنیم (که همانطور که انتظار میرود قرار است یک شیء JSON باشد)، کد از کار میافتد. قرار است این شیء با استفاده از فراخوانی زیر از یک فایل خارجی دریافت شود:
با این حال، این شکست میخورد.
ممکن است از قبل بدانید که مشکل این کد چیست، اما بیایید ببینیم چگونه میتوانید آن را بررسی کنید. ما با Console API شروع میکنیم که به کد جاوا اسکریپت اجازه میدهد تا با کنسول جاوا اسکریپت مرورگر تعامل داشته باشد. این API چندین ویژگی دارد؛ شما قبلاً با آشنا شدهاید که یک پیام سفارشی را در کنسول چاپ میکند.
اگر سعی کنید یک فراخوانی برای ثبت مقدار بازگشتی اضافه کنید، مانند این:
صفحه را در مرورگر رفرش کنید. این بار، قبل از پیام خطا، پیام جدیدی را مشاهده خواهید کرد که در کنسول ثبت شده است:
خروجی نشان میدهد که مقدار برگشتی داده JSON نیست، بلکه یک Promise است. تابع ناهمزمان است: Promiseای را برمیگرداند که فقط زمانی اجرا میشود که پاسخ واقعی از شبکه دریافت شده باشد. قبل از اینکه بتوانیم از پاسخ استفاده کنیم، باید منتظر بمانیم تا Promise اجرا شود.
console.error و call stack
به عنوان یک نکتهی کوتاه، بیایید سعی کنیم از یک متد کنسول متفاوت برای گزارش خطا استفاده کنیم - . در کد خود،اگر اکنون عبارت زیر را جایگزین کنید:
حال اگر کد خود را ذخیره کنید و مرورگر را رفرش کنید؛ اکنون پیامی را که به عنوان خطا گزارش شده است، با همان رنگ و آیکون خطای uncaught در زیر آن مشاهده خواهید کرد. علاوه بر این، یک فلش باز/بسته در کنار پیام مشاهده خواهید کرد. اگر این را فشار دهید، یک خط واحد مشاهده خواهید کرد که نشان دهنده خطی در فایل جاوا اسکریپت است که خطا از آن سرچشمه گرفته است. در واقع، خط خطای uncaught نیز این را دارد، اما دو خط دارد:
این بدان معناست که همانطور که قبلاً اشاره کردیم، خطا توسط تابع ایجاد شده است. اگر به کد خود نگاه کنید، خواهید دید که فراخوانی ناشناس، را فراخوانی میکند. این خطوط به عنوان call stack شناخته میشوند و میتوانند هنگام تلاش برای ردیابی منبع خطایی که شامل چندین مکان مختلف در کد شما میشود، واقعاً مفید باشند.
فراخوانی در این مورد به طور خاص مفید نیست، اما اگر call stack از قبل موجود نباشد، میتواند برای تولید call stack مفید باشد.
به هر حال، بیایید به تلاش برای رفع خطای خود برگردیم. میتوانیم با زنجیر کردن متد به انتهای فراخوانی به پاسخ حاصل از Promise برآورده شده دسترسی پیدا کنیم. سپس میتوانیم مقدار پاسخ حاصل را به توابعی که آن را میپذیرند، مانند این ارسال کنیم:
اگر ذخیره و رفرش کنید و ببینید کد شما کار میکند یا نه میبینید که تغییر فوق مشکل را برطرف نکرده است. متأسفانه، ما هنوز همان خطا را داریم!
به طور خلاصه، هر زمان که چیزی کار نمیکند و مقداری در نقطهای از کد شما آن چیزی که قرار است باشد به نظر نمیرسد، میتوانید از یا تابع مشابه دیگری برای چاپ مقدار و مشاهدهی مشکل استفاده کنید.
استفاده از اشکالزدای جاوا اسکریپت
بیایید این مشکل را با استفاده از یک ویژگی پیچیدهتر از ابزارهای توسعهدهنده مرورگر، یعنی اشکالزدای جاوا اسکریپت، همانطور که در فایرفاکس نامیده میشود، بیشتر بررسی کنیم.

- در سمت چپ، میتوانید اسکریپتی را که میخواهید اشکالزدایی کنید انتخاب کنید (در این مورد، فقط یکی داریم).
- پنل مرکزی، کد موجود در اسکریپت انتخاب شده را نشان میدهد.
- پنل سمت راست، جزئیات مفیدی در مورد محیط فعلی - نقاط شکست، پشته فراخوانی و محدودههای فعال فعلی - را نشان میدهد.
ویژگی اصلی چنین ابزارهایی، امکان اضافه کردن نقاط شکست به کد است - اینها نقاطی هستند که اجرای کد متوقف میشود و در آن نقطه میتوانید محیط را در وضعیت فعلیاش بررسی کنید و ببینید چه اتفاقی میافتد.
بیایید استفاده از نقاط شکست را بررسی کنیم:
خطا در همان خط قبلی رخ میدهد - برای (const hero of heroes) { - خط ۲۶ در تصویر زیر. روی شماره خط در پنل مرکزی کلیک کنید تا یک نقطه شکست به آن اضافه کنید (یک فلش آبی در بالای آن ظاهر میشود).
حالا صفحه را رفرش کنید (Cmd/Ctrl + R) - مرورگر اجرای کد در آن خط را متوقف میکند. در این مرحله، سمت راست بهروزرسانی میشود تا موارد زیر را نشان دهد:

در قسمت Breakpoints، جزئیات نقطه توقفی که تنظیم کردهاید را خواهید دید.
در قسمت Call Stack، چند ورودی خواهید دید - این اساساً همان callstack است که قبلاً در بخش بررسی کردیم. Call Stack لیستی از توابعی را نشان میدهد که برای فراخوانی تابع فعلی فراخوانی شدهاند. در بالا، را داریم، تابعی که در حال حاضر در آن هستیم، و در قسمت دوم onload را داریم که تابع مدیریت رویداد حاوی فراخوانی را ذخیره میکند.
در قسمت Scopes، محدوده فعال فعلی برای تابعی که در حال بررسی آن هستیم را مشاهده خواهید کرد. ما فقط سه مورد داریم - showHeroes، block و Window (محدوده سراسری). هر محدوده را میتوان گسترش داد تا مقادیر متغیرهای داخل محدوده را هنگام توقف اجرا نشان دهد.
میتوانیم اطلاعات بسیار مفیدی را در اینجا پیدا کنیم:
دامنه showHeroes را گسترش دهید - از این میتوانید ببینید که متغیر heroes تعریف نشده است، که نشان میدهد دسترسی به ویژگی members از jsonObj (خط اول تابع) کار نکرده است.
همچنین میتوانید ببینید که متغیر jsonObj یک شیء Response را ذخیره میکند، نه یک شیء JSON.
ورودی مقداری است که وعده با آن انجام شده است. این وعده در قالب JSON نیست: یک شیء Response است. برای بازیابی محتوای پاسخ به عنوان یک شیء JSON، یک مرحله اضافی لازم است.
مدیریت ارورهای جاوا اسکریپت در کد شما
HTML و CSS مجاز هستند - خطاها و ویژگیهای ناشناخته اغلب به دلیل ماهیت زبانها قابل مدیریت هستند. به عنوان مثال، CSS ویژگیهای ناشناخته را نادیده میگیرد و بقیه کد اغلب به درستی کار میکند. با این حال، جاوا اسکریپت به اندازه HTML و CSS مجاز نیست. اگر موتور جاوا اسکریپت با اشتباهات یا نحو ناشناخته مواجه شود، اغلب خطا میدهد.
بیایید یک استراتژی رایج برای مدیریت خطاهای جاوا اسکریپت در کد شما را بررسی کنیم. بخشهای زیر به گونهای طراحی شدهاند که پس از آن یک کپی از فایل قالب نشان داده شده در زیر به عنوان در دستگاه محلی خود تهیه کنید، قطعه کدها را بین تگهای باز و بسته و اضافه کنید، سپس فایل را در یک مرورگر باز کنید و خروجی را در کنسول جاوا اسکریپت devtools مشاهده کنید.
یکی از کاربردهای رایج شرطهای جاوا اسکریپت، مدیریت خطاها است. شرطها به شما این امکان را میدهند که بسته به مقدار یک متغیر، کدهای متفاوتی را اجرا کنید. اغلب، شما میخواهید از این روش به صورت تدافعی استفاده کنید تا از بروز خطا در صورت عدم وجود مقدار یا نوع نادرست آن جلوگیری کنید، یا اگر مقدار باعث بازگشت نتیجه نادرست شود، که میتواند بعداً مشکلاتی ایجاد کند، خطایی را ثبت کنید.
بیایید به یک مثال نگاه کنیم. فرض کنید تابعی داریم که قد کاربر را بر حسب اینچ به عنوان ورودی میگیرد و قد او را بر حسب متر، تا 2 رقم اعشار، برمیگرداند. این میتواند به این شکل باشد:
در عنصر فایل مثال خود، یک const به نام height تعریف کنید و مقدار 70 را به آن اختصاص دهید:
تابع بالا را زیر خط قبلی کپی کنید.
تابع را فراخوانی کنید، ثابت ارتفاع را به عنوان ورودی به آن ارسال کنید و مقدار بازگشتی را در کنسول ثبت کنید:
مثال را در مرورگر بارگذاری کنید و به کنسول جاوا اسکریپت devtools نگاه کنید. باید مقدار ۱.۷۸ را که در آن ثبت شده است، ببینید.
بنابراین این به صورت جداگانه خوب کار میکند. اما اگر دادههای ارائه شده وجود نداشته باشند یا نادرست باشند چه اتفاقی میافتد؟ این سناریوها را امتحان کنید:
اگر مقدار ارتفاع را به "۷۰" تغییر دهید (یعنی ۷۰ که به صورت رشته بیان میشود)، مثال باید ... همچنان خوب کار کند. دلیل این امر این است که محاسبه در خط اول رشته، مقدار را به نوع داده عددی تبدیل میکند. این در یک مورد ساده مانند این اشکالی ندارد، اما در کد پیچیدهتر، دادههای اشتباه میتوانند منجر به انواع اشکالات شوند، که برخی از آنها ظریف و تشخیص آنها دشوار است!
اگر ارتفاع را به مقداری تغییر دهید که نمیتوان آن را به عدد تبدیل کرد، مانند "۷۰ اینچ" یا ["باب"، ۷۰] یا NaN، مثال باید نتیجه را به صورت NaN برگرداند. این میتواند انواع مشکلات را ایجاد کند، به عنوان مثال، اگر بخواهید قد کاربر را در جایی از رابط کاربری وبسایت وارد کنید.
اگر مقدار ارتفاع را به طور کامل حذف کنید (با اضافه کردن // در ابتدای خط، آن را کامنت کنید)، کنسول خطایی شبیه به نشان میدهد، که میتواند باعث توقف اجرای برنامه شما شود.
بدیهی است که هیچ یک از این نتایج خوب نیستند. چگونه در برابر دادههای بد دفاع کنیم؟
بیایید یک شرط درون تابع خود اضافه کنیم تا قبل از انجام محاسبه، بررسی کنیم که آیا دادهها خوب هستند یا خیر. سعی کنید تابع فعلی خود را با کد زیر جایگزین کنید:
حال، اگر دو سناریوی اول را دوباره امتحان کنید، پیام کمی مفیدتر ما را مشاهده خواهید کرد که به شما ایدهای از آنچه برای رفع مشکل باید انجام شود، میدهد. میتوانید هر چیزی را که دوست دارید در آنجا قرار دهید، از جمله تلاش برای اجرای کد برای اصلاح مقدار num، اما این توصیه نمیشود - این تابع یک هدف ساده دارد و شما باید اصلاح مقدار را در جای دیگری از سیستم انجام دهید.
نکته: در عبارت if()، ابتدا با استفاده از عملگر typeof بررسی میکنیم که آیا نوع داده num است یا خیر، سپس بررسی میکنیم که آیا مقدار false را برمیگرداند یا خیر. ما باید این کار را برای دفاع در برابر حالت خاص تنظیم num روی NaN انجام دهیم، زیرا همچنان را برمیگرداند!
با این حال، اگر سناریوی سوم را دوباره امتحان کنید، همچنان خطای را دریافت خواهید کرد. شما نمیتوانید این واقعیت را که مقداری از داخل تابعی که سعی در استفاده از آن مقدار دارد، در دسترس نیست، برطرف کنید.
چگونه این را مدیریت کنیم؟ بهتر است تابع ما طوری تنظیم شود که وقتی دادههای صحیح را دریافت نمیکند، یک خطای سفارشی برگرداند. ابتدا نحوه انجام این کار را بررسی خواهیم کرد، سپس همه خطاها را با هم مدیریت خواهیم کرد.
شما میتوانید با استفاده از دستور throw، همراه با سازندهی ، در هر نقطه از کد خود یک خطای سفارشی ایجاد کنید. بیایید این را در عمل ببینیم.
در تابع خود، خط را در داخل بلوک else تابع خود با خط زیر جایگزین کنید:
مثال خود را دوباره اجرا کنید، اما مطمئن شوید که num روی یک مقدار بد (یعنی غیر عددی) تنظیم شده است. این بار، باید خطای سفارشی خود را به همراه یک پشته فراخوانی مفید برای کمک به شما در یافتن منبع خطا مشاهده کنید (اگرچه توجه داشته باشید که پیام همچنان به ما میگوید که خطا "uncaught" یا "unhandled" است). بسیار خب، خطاها آزاردهنده هستند، اما این بسیار مفیدتر از اجرای موفقیتآمیز تابع و بازگرداندن یک مقدار غیر عددی است که میتواند بعداً مشکلاتی ایجاد کند.
بنابراین، چگونه میتوانیم همه این خطاها را مدیریت کنیم؟
دستور به طور خاص برای مدیریت خطاها طراحی شده است و ساختار زیر را دارد:
درون بلوک try، سعی میکنید کدی را اجرا کنید. اگر این کد بدون بروز خطا اجرا شود، همه چیز خوب است و بلوک catch نادیده گرفته میشود. با این حال، اگر خطایی رخ دهد، بلوک catch اجرا میشود که دسترسی به شیء Error که نشاندهنده خطا است را فراهم میکند و به شما امکان میدهد کدی را برای مدیریت آن اجرا کنید.
بیایید از در کد خود استفاده کنیم.
خط که تابع را در انتهای اسکریپت شما فراخوانی میکند، با بلوک زیر جایگزین کنید. اکنون خط خود را درون یک بلوک try اجرا میکنیم و هر خطایی را که برمیگرداند، درون یک بلوک catch مربوطه مدیریت میکنیم.
ذخیره و رفرش کنید، حالا باید دو چیز را ببینید:
پیام خطا و پشته فراخوانی مانند قبل هستند، اما این بار، بدون برچسب "uncaught" یا "unhandled".
پیام ثبت شده "کدی را برای مدیریت خطا وارد کنید".
حالا سعی کنید num را به یک مقدار مناسب (عدد) بهروزرسانی کنید، و نتیجه محاسبه را بدون پیام خطا ثبت شده خواهید دید.
این نکته قابل توجه است - اکنون هرگونه خطای ارسالی مدیریت میشود، بنابراین باعث خرابی برنامه نمیشود. میتوانید هر کدی را که دوست دارید برای مدیریت خطا اجرا کنید. در بالا، ما یک پیام ساده را ثبت میکنیم، اما برای مثال، میتوانید تابعی را که از کاربر میخواهد دوباره قد خود را وارد کند، فراخوانی کنید، اما این بار از او بخواهید خطای ورودی را اصلاح کند. حتی میتوانید از یک عبارت برای اجرای کد مدیریت خطای مختلف بسته به نوع خطایی که برگردانده میشود، استفاده کنید.
تشخیص ویژگی زمانی مفید است که قصد دارید از ویژگیهای جدید جاوا اسکریپت استفاده کنید که ممکن است در همه مرورگرها پشتیبانی نشوند. میتوانید ویژگی را آزمایش کنید، سپس کد را به صورت مشروط اجرا کنید تا تجربه قابل قبولی را در مرورگرهایی که از این ویژگی پشتیبانی میکنند و آنهایی که پشتیبانی نمیکنند، ارائه دهید. به عنوان یک مثال سریع، API موقعیت مکانی جغرافیایی (که دادههای موقعیت مکانی موجود برای دستگاهی که مرورگر وب روی آن اجرا میشود را نمایش میدهد) یک نقطه ورودی اصلی برای استفاده خود دارد - یک ویژگی موقعیت مکانی جغرافیایی که در شیء سراسری Navigator موجود است. بنابراین، میتوانید با استفاده از یک ساختار مشابه آنچه قبلاً دیدیم، تشخیص دهید که آیا مرورگر از موقعیت مکانی جغرافیایی پشتیبانی میکند یا خیر:
مشکلات بسیار دیگری نیز وجود دارد که با جاوا اسکریپت (و HTML و CSS!) با آنها مواجه خواهید شد، که دانش چگونگی یافتن پاسخها را به صورت آنلاین بسیار ارزشمند میکند.
از جمله بهترین منابع اطلاعات پشتیبانی میتوان به stackoverflow.com، MDN و caniuse.com اشاره کرد.
برای استفاده از MDN، اکثر افراد در موتور جستجو، فناوری مورد نظر خود را جستجو میکنند، به علاوه عبارت "mdn"، به عنوان مثال "mdn HTML video".
caniuse.com اطلاعات پشتیبانی را به همراه چند لینک مفید منابع خارجی ارائه میدهد. به عنوان مثال، به https://caniuse.com/#search=video مراجعه کنید (باید ویژگی مورد نظر خود را در کادر متن وارد کنید).
stackoverflow.com (SO) یک سایت انجمن است که در آن میتوانید سؤال بپرسید و از توسعهدهندگان دیگر بخواهید راهحلهای خود را به اشتراک بگذارند، پستهای قبلی را جستجو کنید و به سایر توسعهدهندگان کمک کنید. قبل از ارسال سؤال جدید، پاسخی برای سؤال خود جستجو کنید تا ببینید آیا از قبل وجود دارد یا خیر. برای مثال، ما در SO عبارت «غیرفعال کردن فوکوس خودکار در گفتگوی HTML» را جستجو کردیم و خیلی سریع به نتیجهی Disable showModal auto-focusing using HTML attributes رسیدیم.
گذشته از آن، سعی کنید موتور جستجوی مورد علاقهتان را برای یافتن پاسخ مشکلتان جستجو کنید. اغلب مفید است که در صورت وجود پیامهای خطای خاص، آنها را جستجو کنید - احتمالاً توسعهدهندگان دیگر نیز مشکلات مشابهی با شما داشتهاند.