كيف تجتاز مقابلة JavaScript: المستوى المتقدم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

كيف تجتاز مقابلة JavaScript: أهم الأسئلة المتقدمة



المقابلة لوظيفة مطور JavaScript Senior أو Middle ليست مجرد اختبار للقواعد النحوية. يرغب المحاورون في رؤية فهم عميق لآليات عمل اللغة، خاصة في بيئة Node.js. في هذه المقالة، سنستعرض الموضوعات الرئيسية التي غالبًا ما تسبب صعوبات، وسنقدم أمثلة كود تساعدك على الإجابة بثقة على أصعب الأسئلة.



1. الإغلاقات (Closures) والبيئة المعجمية (Lexical Environment)



سؤال الإغلاقات هو سؤال كلاسيكي. ولكن على المستوى المتقدم، من المهم ليس فقط تقديم تعريف، بل إظهار كيفية عمل الإغلاقات مع let و const وفي الحلقات.



السؤال: ماذا سيطبع الكود التالي ولماذا؟



for (var i = 0; i < 3; i++) {  setTimeout(() => console.log(i), 100);}// الناتج: 3, 3, 3


المشكلة هي أن var له نطاق دالة (function scope) وليس نطاق كتلة (block scope). بحلول وقت استدعاء دوال الاسترجاع (callbacks)، تكون الحلقة قد انتهت، وتكون قيمة i تساوي 3. الحل هو استخدام let (الذي ينشئ ربطًا جديدًا في كل تكرار) أو تغليف الاستدعاء في إغلاق:



for (var i = 0; i < 3; i++) {  (function(j) {    setTimeout(() => console.log(j), 100);  })(i);}// الناتج: 0, 1, 2


في Node.js، تُستخدم الإغلاقات بنشاط لإنشاء متغيرات خاصة ومصانع دوال (function factories). على سبيل المثال، عند كتابة middleware:



function createLogger(prefix) {  return function(message) {    console.log(`[${prefix}] ${message}`);  };}const infoLog = createLogger('INFO');infoLog('تم تشغيل الخادم'); // [INFO] تم تشغيل الخادم


2. Event Loop والمهام الصغيرة (Microtasks)



فهم دورة الأحداث هو أحد المعايير الرئيسية للتقييم. يحب المحاورون إعطاء مهام حول ترتيب طباعة الكود غير المتزامن.



السؤال: بأي ترتيب سيتم تنفيذ console.log؟



console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

process.nextTick(() => console.log('4'));

console.log('5');

// الناتج: 1, 5, 4, 3, 2


التحليل:

  • console.log('1') و console.log('5') — متزامنان (synchronous)، يتم تنفيذهما أولاً.
  • process.nextTick — هي مهمة صغيرة (microtask) ذات أولوية قصوى. يتم تنفيذها فور انتهاء المرحلة الحالية.
  • Promise.then — هي أيضًا مهمة صغيرة، ولكن بأولوية أقل قليلاً من nextTick.
  • setTimeout(fn, 0) — مهمة كبيرة (macrotask)، يتم تنفيذها في الدورة التالية من Event Loop.


بالنسبة لـ Node.js، من المهم تذكر process.nextTick و setImmediate. الفرق هو أن nextTick يتم تنفيذه قبل أي عمليات إدخال/إخراج (I/O)، بينما setImmediate يتم تنفيذه بعدها.



3. الوعود (Promises) والمكررات غير المتزامنة (Async/Await)



المستوى المتقدم يعني القدرة على العمل مع سلاسل الوعود (promise chains)، ومعالجة الأخطاء، والتنفيذ المتوازي.



السؤال: كيف تتعامل مع عدة طلبات غير متزامنة لتنفيذها بالتوازي، ولكن انتظار جميع النتائج؟



const fetchData = (id) => {  return new Promise((resolve) => {    setTimeout(() => resolve(`بيانات للمعرف: ${id}`), id * 100);  });};

async function getAllData() { const ids = [1, 2, 3]; const promises = ids.map(id => fetchData(id)); const results = await Promise.all(promises); console.log(results); // ['بيانات للمعرف: 1', 'بيانات للمعرف: 2', 'بيانات للمعرف: 3']}

getAllData();


نقطة مهمة: Promise.all يرفض (reject) الوعد بأكمله عند أول خطأ. إذا كنت بحاجة للحصول على جميع النتائج (حتى مع الأخطاء)، استخدم Promise.allSettled. هذا موضوع شائع في المقابلات في سياق

المدونات

توصيات الكتب