كيف تجتاز مقابلة JavaScript: أهم أسئلة المستوى المتقدم
\n\nمقابلة العمل لوظيفة مطور JavaScript كبير (Senior) أو مهندس Node.js ليست مجرد اختبار لقواعد اللغة. يريد المحاورون رؤية فهم عميق للغة: كيف يعمل Event Loop، ما هو السياق this في الحقيقة، وكيف تتجنب "الجحيم" من التداخل عند العمل مع العمليات غير المتزامنة. في هذه المقالة، سنحلل الأسئلة الأكثر شيوعًا وخبثًا التي تُطرح على المستوى المتقدم. استعد ليس فقط لقراءة الإجابات، بل لفهم الآليات.
1. Event Loop، مكدس الاستدعاءات (Call Stack) والمهام الصغيرة (Microtasks vs Macrotasks)
\n\nهذه هي على الأرجح المجموعة الأكثر شيوعًا في مقابلات Node.js. السؤال هو: "ماذا سيطبع هذا الكود وبأي ترتيب؟".
\n\nconsole.log('1');\n\nsetTimeout(() => console.log('2'), 0);\n\nPromise.resolve().then(() => console.log('3'));\n\nprocess.nextTick(() => console.log('4'));\n\nconsole.log('5');\n\nالإجابة الصحيحة: 1, 5, 4, 3, 2.
\n\nالتحليل:
\n- \n
- الكود المتزامن (Synchronous code) (1, 5) يُنفذ أولاً. \n
- process.nextTick() هي مهمة صغيرة (microtask) ذات أولوية قصوى. تُنفذ فورًا بعد العملية الحالية، ولكن قبل أي مهام صغيرة أخرى (Promise) ومهام كبيرة (macrotasks) مثل setTimeout. \n
- Promise.then() هي أيضًا مهمة صغيرة، لكن أولويتها أقل من
nextTick. \n - setTimeout(cb, 0) هي مهمة كبيرة (macrotask). تُنفذ فقط بعد أن يتم تفريغ مكدس الاستدعاءات وجميع المهام الصغيرة (جميع
nextTickوPromise) بالكامل. \n
كيف تجيب بشكل صحيح: اشرح أن Node.js له مراحل (Phases) في Event Loop. المهام الصغيرة (process.nextTick و Promise) تُعالج بين المراحل. المهام الكبيرة (setTimeout, setInterval, I/O) تُعالج داخل المراحل. nextTick هو قائمة انتظار غير قياسية ولكنها مهمة جدًا، ويمكنها حظر Event Loop إذا استُخدمت بشكل متكرر (Recursively).
2. السياق this و"فقدانه"
\n\nالسؤال: "كيف يعمل this في JavaScript؟ أعط مثالاً حيث يُفقد this، وكيف تصلح ذلك".
المستوى المتقدم يعني أنك تعرف القواعد الأربع:
\n- \n
- الربط الجديد (New binding) — عند استدعاء دالة باستخدام
new، يشيرthisإلى الكائن الجديد الذي تم إنشاؤه. \n - الربط الصريح (Explicit binding) —
call,apply,bind. \n - الربط الضمني (Implicit binding) — عندما تُستدعى الدالة كطريقة لكائن (obj.method()). \n
- الافتراضي (Default) — الكائن العام (
globalفي Node.js،windowفي المتصفح) أوundefinedفي الوضع الصارم (Strict mode). \n
مثال على فقدان السياق:
\nconst user = {\n name: 'Alice',\n greet() {\n console.log(`Hello, ${this.name}`);\n }\n};\n\nconst greetFn = user.greet;\ngreetFn(); // Hello, undefined\n\nلماذا؟ قمنا بتعيين الطريقة إلى متغير. الآن greetFn() تُستدعى بدون سياق الكائن (user). this يرتبط بالكائن العام (أو undefined في الوضع الصارم).
كيف تصلح:
\n- \n
- استخدم
bind:const greetFn = user.greet.bind(user);\n - استخدم دالة السهم (Arrow function) (ليس لها
thisخاص بها، بل تأخذه من السياق المعجمي الخارجي). \n - استدعِ عبر
call/apply:greetFn.call(user). \n
سؤال إضافي: "وماذا سيحدث إذا مررنا user.greet إلى setTimeout؟" — نفس تأثير فقدان السياق. يُصلح باستخدام bind أو دالة السهم في الكولباك (Callback).