JavaScript المتقدم (ES6+)
تقنيات تصحيح الأخطاء
تقنيات تصحيح الأخطاء
تصحيح الأخطاء هو مهارة بالغة الأهمية لكل مطور. في هذا الدرس، سنستكشف تقنيات وأدوات تصحيح أخطاء قوية ستساعدك على تحديد وإصلاح الأخطاء بكفاءة في كود JavaScript الخاص بك.
طرق Console بخلاف console.log
بينما يُعد console.log مفيداً، يوفر JavaScript العديد من طرق console الأكثر قوة:
// console.error() - عرض الأخطاء باللون الأحمر
console.error('This is an error message');
// console.warn() - عرض التحذيرات باللون الأصفر
console.warn('This is a warning');
// console.info() - عرض رسائل معلوماتية
console.info('Application version: 2.1.0');
// console.table() - عرض المصفوفات/الكائنات كجداول
const users = [
{ name: 'Alice', age: 25, city: 'New York' },
{ name: 'Bob', age: 30, city: 'London' }
];
console.table(users);
// console.group() - تجميع السجلات ذات الصلة
console.group('User Details');
console.log('Name: Alice');
console.log('Age: 25');
console.groupEnd();
// console.time() - قياس وقت التنفيذ
console.time('Loop Time');
for (let i = 0; i < 1000000; i++) {
// بعض العمليات
}
console.timeEnd('Loop Time'); // Loop Time: 5.234ms
// console.trace() - عرض تتبع المكدس
function first() {
second();
}
function second() {
console.trace('Trace point');
}
first();
// console.assert() - السجل فقط إذا كان الشرط خاطئاً
const x = 5;
console.assert(x === 10, 'x should be 10');
// console.count() - عد عدد مرات الاستدعاء
for (let i = 0; i < 3; i++) {
console.count('Loop iteration');
}
// console.dir() - عرض خصائص الكائن
const element = document.querySelector('body');
console.dir(element); // يظهر خصائص DOM
نصيحة: استخدم console.table() لمصفوفات الكائنات - فهو يوفر عرضاً أوضح بكثير من console.log()، خاصة عند مقارنة كائنات متعددة.
تصحيح الأخطاء باستخدام أدوات المتصفح
توفر المتصفحات الحديثة أدوات تصحيح أخطاء قوية مدمجة مباشرة في وحدة تحكم المطور:
فتح أدوات المطور:
- Chrome/Edge: F12 أو Ctrl+Shift+I (Cmd+Option+I على Mac)
- Firefox: F12 أو Ctrl+Shift+I (Cmd+Option+I على Mac)
- Safari: Cmd+Option+I (تفعيل قائمة المطور أولاً)
لوحات أدوات المطور الرئيسية:
1. Console:
- تنفيذ JavaScript مباشرة
- عرض السجلات والأخطاء والتحذيرات
- الوصول إلى المتغيرات في النطاق الحالي
- استخدام $0 للإشارة إلى العنصر المحدد
2. Sources/Debugger:
- عرض والبحث في ملفات المصدر
- تعيين نقاط التوقف
- التنقل خلال تنفيذ الكود
- مراقبة المتغيرات
- عرض مكدس الاستدعاء
3. Network:
- مراقبة طلبات HTTP
- عرض رؤوس الطلب/الاستجابة
- فحص أوقات التحميل
- فحص استدعاءات API
4. Performance:
- تسجيل أداء وقت التشغيل
- تحديد الاختناقات
- تحليل معدل الإطارات
- تصنيف استخدام الذاكرة
5. Memory:
- أخذ لقطات للذاكرة
- تسجيل التخصيصات
- اكتشاف تسرب الذاكرة
- مقارنة اللقطات
نقاط التوقف وتصحيح الأخطاء خطوة بخطوة
تسمح لك نقاط التوقف بإيقاف تنفيذ الكود في نقاط محددة وفحص الحالة:
// تعيين نقاط التوقف في الكود باستخدام عبارة debugger
function calculateTotal(items) {
let total = 0;
debugger; // سيتوقف التنفيذ هنا عندما تكون أدوات المطور مفتوحة
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
// أنواع نقاط التوقف في أدوات المطور:
1. نقاط توقف الخط:
- انقر على رقم السطر في لوحة Sources
- يتوقف التنفيذ عند ذلك السطر
2. نقاط التوقف الشرطية:
- انقر بزر الماوس الأيمن على رقم السطر
- أضف شرطاً (مثل i === 50)
- يتوقف فقط عندما يكون الشرط صحيحاً
3. نقاط توقف DOM:
- انقر بزر الماوس الأيمن على العنصر في لوحة Elements
- التوقف عند تغييرات السمات أو إزالة العقدة أو تعديلات الشجرة الفرعية
4. نقاط توقف XHR/Fetch:
- التوقف عند طلب عنوان URL محدد
- تصحيح استدعاءات AJAX
5. نقاط توقف مستمعي الأحداث:
- التوقف عند أحداث معينة (نقر، ضغط مفتاح، إلخ)
عناصر التحكم في الخطوات:
- Continue (F8): استئناف التنفيذ إلى نقطة التوقف التالية
- Step Over (F10): تنفيذ السطر الحالي، لا تدخل الدوال
- Step Into (F11): الدخول إلى الدالة المستدعاة
- Step Out (Shift+F11): الخروج من الدالة الحالية
- Step: تنفيذ العبارة التالية
سير عمل التصحيح: تعيين نقطة توقف → تشغيل الكود → فحص المتغيرات → التنقل خلال التنفيذ → تحديد المشكلة → الإصلاح والاختبار.
تصنيف الأداء
حدد اختناقات الأداء وحسّن كودك:
// استخدام واجهة برمجة تطبيقات الأداء
const start = performance.now();
// كود للقياس
for (let i = 0; i < 1000000; i++) {
// بعض العمليات
}
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);
// استخدام performance.mark() و performance.measure()
performance.mark('start-calculation');
// حساب معقد
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i);
}
performance.mark('end-calculation');
performance.measure('calculation-time', 'start-calculation', 'end-calculation');
// الحصول على القياس
const measure = performance.getEntriesByName('calculation-time')[0];
console.log(`Calculation took: ${measure.duration}ms`);
// مقارنة التنفيذات المختلفة
function comparePerformance() {
// التنفيذ 1: حلقة for
console.time('For Loop');
let sum1 = 0;
for (let i = 0; i < 1000000; i++) {
sum1 += i;
}
console.timeEnd('For Loop');
// التنفيذ 2: reduce
console.time('Reduce');
const arr = Array.from({ length: 1000000 }, (_, i) => i);
const sum2 = arr.reduce((acc, val) => acc + val, 0);
console.timeEnd('Reduce');
}
// استخدام لوحة الأداء في Chrome DevTools:
// 1. افتح DevTools > علامة تبويب Performance
// 2. انقر على زر Record
// 3. قم بإجراء الإجراءات التي تريد تصنيفها
// 4. انقر على Stop
// 5. تحليل الرسم البياني الشعلي والجدول الزمني
اكتشاف تسرب الذاكرة
يمكن أن تؤثر تسريبات الذاكرة بشدة على أداء التطبيق. إليك كيفية اكتشافها ومنعها:
// الأسباب الشائعة لتسرب الذاكرة:
1. المؤقتات المنسية:
// سيئ: لم يتم مسح المؤقت مطلقاً
const interval = setInterval(() => {
console.log('Running...');
}, 1000);
// جيد: مسح المؤقت عند الانتهاء
const interval = setInterval(() => {
console.log('Running...');
}, 1000);
// المسح عند إلغاء تثبيت المكون أو عدم الحاجة إليه
clearInterval(interval);
2. مستمعي الأحداث غير المزالين:
// سيئ: يبقى المستمع حتى بعد إزالة العنصر
function attachListener() {
const button = document.getElementById('myButton');
button.addEventListener('click', handleClick);
// تمت إزالة الزر لكن المستمع لا يزال في الذاكرة
}
// جيد: إزالة المستمع
function attachListener() {
const button = document.getElementById('myButton');
button.addEventListener('click', handleClick);
// لاحقاً، عند الانتهاء:
button.removeEventListener('click', handleClick);
}
3. الإغلاقات التي تحتفظ بالمراجع:
// سيئ: يحتفظ الإغلاق بمرجع لبيانات كبيرة
function createProcessor() {
const largeData = new Array(1000000).fill('data');
return function process(item) {
// يستخدم فقط item، لكنه يحتفظ بمرجع إلى largeData
return item.toUpperCase();
};
}
// جيد: لا تلتقط متغيرات غير ضرورية
function createProcessor() {
return function process(item) {
return item.toUpperCase();
};
}
4. عناصر DOM المنفصلة:
// سيئ: مرجع إلى عنصر محذوف
let elements = [];
function addElement() {
const div = document.createElement('div');
document.body.appendChild(div);
elements.push(div); // يحتفظ بالمرجع حتى بعد الإزالة
}
// جيد: تنظيف المراجع
function removeElement(index) {
const element = elements[index];
element.remove();
elements.splice(index, 1); // إزالة المرجع
}
// استخدام لوحة الذاكرة في Chrome DevTools:
// 1. التقط لقطة للذاكرة
// 2. قم بإجراء قد يتسبب في تسرب
// 3. التقط لقطة أخرى
// 4. قارن اللقطات
// 5. ابحث عن الكائنات التي لا ينبغي أن تستمر
تحذير: قم دائماً بتنظيف مستمعي الأحداث والمؤقتات والاشتراكات عندما لا تكون هناك حاجة إليها. هذا مهم بشكل خاص في تطبيقات الصفحة الواحدة.
خرائط المصدر
تسمح لك خرائط المصدر بتصحيح أخطاء الكود المصغر أو المنقول عن طريق ربطه بالمصدر الأصلي:
// ما هي خرائط المصدر؟
// - ربط الكود المصغر بالمصدر الأصلي
// - تمكين تصحيح أخطاء كود الإنتاج
// - العمل مع الكود المنقول (TypeScript، Babel)
تمكين خرائط المصدر:
// في webpack.config.js
module.exports = {
devtool: 'source-map', // الإنتاج
// أو 'inline-source-map' للتطوير
};
// في تكوين Babel
{
"sourceMaps": true
}
// في TypeScript tsconfig.json
{
"compilerOptions": {
"sourceMap": true
}
}
// تعليق خريطة المصدر في الملف المولد
//# sourceMappingURL=bundle.js.map
الفوائد:
- التصحيح بأسماء المتغيرات الأصلية
- رؤية بنية الملف الأصلي
- تعيين نقاط التوقف في كود المصدر
- رسائل خطأ أفضل
- تتبعات مكدس أسهل
أفضل ممارسات التصحيح
1. إعادة إنتاج الخطأ:
- إنشاء حالة اختبار بسيطة
- توثيق خطوات الإعادة
- الاختبار في بيئات مختلفة
2. عزل المشكلة:
- استخدام البحث الثنائي (علّق على نصف الكود)
- اختبار المكونات بشكل مستقل
- فحص المدخلات والمخرجات
3. الفهم قبل الإصلاح:
- لا تخمن وتتحقق
- اقرأ رسائل الخطأ بعناية
- استخدم المصحح للتنقل خلال الكود
4. استخدام التسجيل الوصفي:
// سيئ
console.log(data);
// جيد
console.log('User data received:', {
userId: data.id,
timestamp: new Date()
});
5. كتابة كود دفاعي:
function processUser(user) {
// التحقق من المدخلات
if (!user || !user.id) {
console.error('Invalid user object:', user);
return null;
}
// إضافة التأكيدات
console.assert(typeof user.id === 'number', 'User ID must be number');
// المعالجة...
}
6. استخدام حدود الخطأ:
// التقاط الأخطاء في أجزاء محددة من التطبيق
try {
riskyOperation();
} catch (error) {
console.error('Operation failed:', error);
// التعامل بلطف
}
7. الاستفادة من ملحقات المتصفح:
- React DevTools
- Vue DevTools
- Redux DevTools
- Lighthouse للأداء
تمرين عملي:
صحح هذا الكود الذي يحتوي على مشاكل متعددة:
// كود يحتوي على أخطاء
function calculateAverage(numbers) {
let total = 0;
for (let i = 0; i <= numbers.length; i++) {
total += numbers[i];
}
return total / numbers.length;
}
const scores = [85, 90, 78, 92];
console.log(calculateAverage(scores));
المشاكل المطلوب إيجادها:
- خطأ off-by-one في شرط الحلقة
- الوصول إلى عنصر مصفوفة غير معرّف
- لا يوجد التحقق من صحة المدخلات
الكود المصحح:
function calculateAverage(numbers) {
// التحقق من صحة المدخلات
if (!Array.isArray(numbers) || numbers.length === 0) {
console.error('Invalid input:', numbers);
return 0;
}
let total = 0;
// مصحح: استخدام < بدلاً من <=
for (let i = 0; i < numbers.length; i++) {
// التحقق من صحة كل رقم
if (typeof numbers[i] !== 'number') {
console.warn(`Skipping non-number at index ${i}:`, numbers[i]);
continue;
}
total += numbers[i];
}
return total / numbers.length;
}
const scores = [85, 90, 78, 92];
console.log(calculateAverage(scores)); // 86.25
الملخص
في هذا الدرس، تعلمت:
- طرق console المتقدمة لتسجيل وتصحيح أفضل
- كيفية استخدام أدوات المطور بفعالية للتصحيح
- تعيين واستخدام أنواع مختلفة من نقاط التوقف
- تقنيات وأدوات تصنيف الأداء
- كيفية اكتشاف ومنع تسرب الذاكرة
- استخدام خرائط المصدر لتصحيح كود الإنتاج
- أفضل الممارسات للتصحيح المنهجي
التالي: في الدرس التالي، سنتقن التعبيرات النمطية لمطابقة ومعالجة نص قوية!