المستوى:
خبير
عدد الأسئلة:
١٢ سؤالاً
الزمن:
٦ دقائق

أسئلة انترفيو Senior Front End مع الإجابة

قرارات ومواقف صعبة يُسأل عنها الخبراء في مقابلات وظيفة مطور فرونت اند: Git وأدوات المطوّر، وHTML وإتاحة المواقع، وReact وإدارة الحالة، وأداء الواجهات، وأساسيات JavaScript، وتنسيق الصفحات بـ CSS. في آخرها تقرير بنقاط ضعفك.

المهارات: HTML وإتاحة المواقع، تنسيق الصفحات بـ CSS، أساسيات JavaScript، React وإدارة الحالة، أداء الواجهات، Git وأدوات المطوّر

تعليمات: لكل سؤال إجابة صحيحة واحدة. إن لم تعرف الإجابة فاختر «لا أعرف»، وسيدلّك التقرير على ما تراجعه.

السؤال الأول: عند فتح نافذة حوار مخصصة، تريد أن يبقى محتوى الصفحة خلفها ظاهراً، لكن دون أن يصل إليه المستخدم بالفأرة أو بمفتاح Tab، ودون أن تقرأه قارئات الشاشة. ما الحل الأدق؟

اختر إجابتك لتبدأ الاختبار

كل الأسئلة مع الإجابات (١٢ سؤالاً)

جرّب المقابلة أولاً، ثم افتح الإجابات لتراجعها مع الشرح.
  1. ١. عند فتح نافذة حوار مخصصة، تريد أن يبقى محتوى الصفحة خلفها ظاهراً، لكن دون أن يصل إليه المستخدم بالفأرة أو بمفتاح Tab، ودون أن تقرأه قارئات الشاشة. ما الحل الأدق؟

    • إضافة tabindex="-1" إلى العنصر الذي يضم محتوى الصفحة الخلفي
    • إضافة aria-hidden="true" إلى العنصر الذي يضم محتوى الصفحة الخلفي
    • إضافة pointer-events: none في CSS إلى العنصر الذي يضم محتوى الصفحة الخلفي
    • إضافة السمة inert إلى العنصر الذي يضم محتوى الصفحة الخلفي

    الشرح: السمة inert تمنع التركيز والنقر على العنصر وكل ما بداخله، وتزيله من شجرة الإتاحة دفعة واحدة. أما aria-hidden فيخفي المحتوى عن قارئات الشاشة فقط ويبقي روابطه قابلة للوصول بمفتاح Tab، وtabindex="-1" لا يؤثر إلا في العنصر نفسه دون أبنائه، وpointer-events لا يمنع لوحة المفاتيح ولا قارئات الشاشة.

  2. ٢. في نظام التصميم الذي تقوده مكوّن Card عنوانه مكتوب دائماً بالوسم <h3>، لكنه يُستخدم في صفحات يأتي فيها مباشرة بعد <h1>، وفي صفحات أخرى داخل قسم عنوانه <h3>. ما أفضل قرار من حيث إتاحة الموقع؟

    • تمرير مستوى العنوان عبر خاصية (prop) ليطابق موضع البطاقة في بنية الصفحة
    • استخدام <h1> دائماً داخل <section>، لأن المتصفحات تعيد ترقيم العناوين تلقائياً بحسب التداخل
    • استبدال العنوان بـ <div> عريض الخط، لأن قارئات الشاشة لا تعتمد على تسلسل العناوين
    • إبقاء <h3> في كل الصفحات مع تغيير حجم خطه عبر CSS بحسب موضعه

    الشرح: يتنقل مستخدمو قارئات الشاشة في الصفحة عبر العناوين، فيجب أن يعكس مستواها بنية المحتوى الفعلية، ولذلك يُترك تحديده للصفحة التي تستخدم المكوّن. ويخطئ كثيرون بالاعتماد على خوارزمية الهيكل (document outline) في HTML5، وهي لم تُطبَّق في المتصفحات وحُذفت من المواصفة.

  3. ٣. القائمة المنسدلة .dropdown موجودة داخل البطاقة .card، والبطاقة والرأس .site-header في المستوى نفسه من الصفحة. عند التمرير تختفي القائمة تحت الرأس رغم أن قيمة z-index لها 9999. ما السبب؟ .site-header { position: sticky; top: 0; z-index: 10; } .card { position: relative; z-index: 1; } .card .dropdown { position: absolute; z-index: 9999; }

    • الخاصية z-index لا تعمل على العناصر التي لها position: absolute
    • العنصر الذي له position: sticky يُرسم دائماً فوق جميع عناصر الصفحة مهما كانت قيمها
    • .card تنشئ سياق تكديس (stacking context)، فتُقارَن z-index للقائمة داخلها فقط
    • القيمة 9999 تتجاوز الحد الأعلى الذي تقبله المتصفحات للخاصية z-index، فتُهمَل

    الشرح: قيمة z-index على عنصر موضوع (positioned) تنشئ سياق تكديس، فتُرسم البطاقة بكل محتواها في المستوى 1 مقارنةً بالرأس ذي المستوى 10، ولا أثر لقيمة 9999 خارج البطاقة. يظن كثيرون أن z-index ترتيب عام على مستوى الصفحة، والحل إزالة z-index عن البطاقة أو عرض القائمة خارجها عبر portal.

  4. ٤. مكوّن بطاقة المنتج يظهر في الصفحة نفسها داخل شريط جانبي ضيق وداخل منطقة المحتوى العريضة، وتريد أن يتغير تخطيطه بحسب المساحة المتاحة له لا بحسب عرض الشاشة. ما الأسلوب الأنسب في CSS؟

    • وحدات vw لكل أحجام البطاقة الداخلية حتى تتناسب مع العرض تلقائياً
    • استعلامات الوسائط @media (max-width: ...) بنقاط توقف مدروسة تغطي كل أحجام الشاشات الشائعة
    • استعلامات الحاوية @container مع container-type: inline-size على العنصر الأب
    • القاعدة @supports للتحقق من عرض البطاقة قبل تطبيق التخطيط المناسب

    الشرح: استعلامات الحاوية تقيس عرض العنصر الحاوي نفسه، فتتصرف البطاقة تصرفاً صحيحاً أينما وُضعت. أما @media وvw فيعتمدان على عرض نافذة العرض، وهو واحد للموضعين في الصفحة نفسها، و@supports يفحص دعم المتصفح للخصائص لا أبعاد العناصر.

  5. ٥. تكتب مكتبة تحتاج إلى ربط بيانات داخلية بعناصر DOM يمررها المستخدم، دون تعديل العناصر نفسها، ودون أن تمنع جمع المهملات (garbage collection) عند حذف العناصر من الصفحة. ما البنية الأنسب لتخزين هذه البيانات؟

    • Map تكون العناصر مفاتيحه والبيانات قيمه، لأنه أسرع في البحث
    • كائن عادي {} تكون العناصر مفاتيحه والبيانات قيمه
    • WeakMap تكون العناصر مفاتيحه والبيانات قيمه
    • WeakSet يضم العناصر، وتُضاف البيانات إليه بجانب كل عنصر

    الشرح: يحتفظ WeakMap بمفاتيحه احتفاظاً ضعيفاً، فإذا حُذف العنصر ولم يبقَ مرجع آخر إليه جُمع هو وقيمته تلقائياً، بينما يمنع Map جمعه فيسبب تسرباً في الذاكرة. أما WeakSet فلا يخزّن قيماً مرتبطة بالعناصر، والكائن العادي يحوّل كل مفتاح إلى النص "[object Object]" فتتطابق المفاتيح كلها.

  6. ٦. حقل بحث يرسل طلب fetch بعد أن يتوقف المستخدم عن الكتابة قليلاً، لكن نتائج كلمة سابقة تظهر أحياناً بدلاً من نتائج الكلمة الأخيرة، لأن ردود الخادم لا تصل بترتيب إرسالها. ما الإصلاح الأدق؟

    • إلغاء الطلب السابق عبر AbortController قبل إرسال الطلب الجديد
    • زيادة مدة debounce حتى يقل احتمال تداخل الطلبات المتتالية
    • استبدال .then() بـ async/await لأنها تضمن وصول الردود بترتيب إرسالها
    • إضافة الترويسة Cache-Control: no-store إلى الطلبات لمنع عرض نتائج قديمة مخزّنة

    الشرح: إلغاء الطلب السابق يضمن ألا يصل إلى الواجهة إلا رد آخر طلب، مهما تأخر الخادم في الرد على ما قبله. أما زيادة debounce فتقلل التداخل ولا تمنعه، وasync/await صيغة أخرى للوعود لا تتحكم في ترتيب ردود الشبكة، والمشكلة ليست في التخزين المؤقت أصلاً.

  7. ٧. في تطبيق كبير يوفّر سياق واحد AppContext القيمة { user, theme, cart }، ولاحظت أن المكوّنات التي تقرأ theme فقط يُعاد تصييرها مع كل تغيير في السلة. ما الإصلاح الأنسب؟

    • تغليف كل مكوّن يقرأ theme بـ React.memo لمنع إعادة تصييره
    • تخزين cart في useRef داخل المزوّد (Provider) بدلاً من useState لتجنب التصيير
    • تغليف الدوال الموجودة في قيمة السياق بـ useCallback
    • تقسيم AppContext إلى سياقات منفصلة مثل ThemeContext وCartContext

    الشرح: يُعاد تصيير كل مستهلك للسياق عند تغيّر قيمته، ولا يمنع React.memo ذلك لأنه يقارن الخصائص (props) فقط. وفصل البيانات التي تتغير بمعدلات مختلفة في سياقات مستقلة يجعل التحديث يصل إلى من يحتاجه وحده، أما useRef فيوقف التصيير لكنه يمنع واجهة السلة من التحديث.

  8. ٨. يقيس هذا المكوّن ارتفاع التلميح (tooltip) بعد عرضه ليضعه فوق الزر، لكن المستخدمين يرونه يومض لحظةً في موضع خاطئ قبل أن ينتقل إلى موضعه الصحيح. ما الإصلاح المناسب؟ useEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTop(buttonTop - height); }, [buttonTop]);

    • نقل القياس إلى useMemo ليُحسب أثناء التصيير قبل الرسم
    • تغليف setTop داخل setTimeout بمهلة 0 حتى يكتمل الرسم
    • تغليف مكوّن التلميح بـ React.memo لتقليل مرات تصييره
    • استبدال useEffect بـ useLayoutEffect في هذا المكوّن

    الشرح: يعمل useEffect عادةً بعد أن يرسم المتصفح الشاشة فيظهر الموضع الأول قبل تصحيحه، أما useLayoutEffect فيعمل بعد تحديث DOM وقبل الرسم، فيظهر التلميح في موضعه الصحيح من البداية. ويخطئ بعضهم بنقل القياس إلى useMemo، مع أنه يعمل أثناء التصيير قبل أن يصبح العنصر موجوداً في DOM.

  9. ٩. يعطي تقرير Lighthouse قيمة سيئة لمؤشر LCP، وعنصر LCP هو صورة كبيرة في أعلى الصفحة الرئيسية، وقد أضاف الفريق loading="lazy" إلى كل صور الموقع ضمن حملة لتحسين الأداء. ما الإجراء الأنسب؟

    • إضافة decoding="async" إلى الصورة، مع إبقاء التحميل الكسول عليها دون أي تغيير
    • تحميل الصورة عبر JavaScript بعد حدث load حتى لا تؤخر بقية موارد الصفحة
    • تحويل الصورة إلى خلفية عبر background-image في ملف CSS الرئيسي للصفحة
    • إزالة loading="lazy" عن هذه الصورة وإضافة fetchpriority="high" إليها

    الشرح: التحميل الكسول يؤخر جلب الصورة حتى يتحقق المتصفح من ظهورها في نافذة العرض، فيتأخر أكبر عنصر مرئي، ولذلك تُستثنى منه الصور الظاهرة عند فتح الصفحة، وfetchpriority="high" يقدّم جلبها على غيرها. أما background-image فيتأخر اكتشافها حتى يُحلَّل ملف CSS، فتزيد المشكلة سوءاً.

  10. ١٠. يستغرق هذا المقطع وقتاً طويلاً عندما تحتوي الصفحة على آلاف العناصر، ويُظهر تبويب Performance تحذيرات Forced reflow. ما السبب الرئيسي للبطء؟ document.querySelectorAll('.item').forEach((el) => { el.style.width = container.offsetWidth / 2 + 'px'; });

    • الدالة forEach أبطأ بكثير من حلقة for التقليدية عند التعامل مع آلاف العناصر
    • قراءة offsetWidth بعد كل كتابة لنمط تفرض إعادة حساب التخطيط (layout) فوراً
    • دمج الرقم مع 'px' ينشئ سلسلة نصية جديدة مكلفة في كل تكرار
    • تعديل style.width يجعل المتصفح يعيد تحليل ملفات CSS كلها في كل تكرار

    الشرح: تعديل النمط يُبطل التخطيط، ثم تجبر قراءة offsetWidth في التكرار التالي المتصفح على إعادة حسابه فوراً، فيتكرر ذلك آلاف المرات (layout thrashing). والحل قراءة القيمة مرة واحدة قبل الحلقة ثم تنفيذ الكتابات كلها، أما الفرق بين forEach وfor فضئيل لا يفسر هذا البطء.

  11. ١١. أعدت ترتيب التزامات فرعك feature/checkout ودمجت بعضها عبر git rebase -i بعد أن دفعته إلى المستودع البعيد، وربما دفع زميلك إلى الفرع نفسه التزاماً جديداً في الأثناء. كيف تدفع فرعك بأمان؟

    • git push --force، فهو يستبدل الفرع البعيد بنسختك المحلية مباشرة
    • git push --force-with-lease، فيُرفض الدفع إن تغيّر الفرع البعيد
    • git pull ثم git push، لدمج التزام زميلك مع التاريخ الجديد قبل الدفع
    • git push --no-verify، لتجاوز فحوصات الخادم التي ترفض التاريخ المعدّل

    الشرح: يرفض --force-with-lease الكتابة فوق الفرع البعيد إذا تغيّر عما رأيته آخر مرة، فلا يُمحى التزام زميلك دون علمك، بينما يستبدله --force دون أي تحقق. أما git pull فيدمج التاريخ القديم مع المعدّل فتتكرر الالتزامات، و--no-verify يتجاوز خطافات Git المحلية فقط ولا علاقة له بإعادة كتابة التاريخ.

  12. ١٢. في تطبيق صفحة واحدة (SPA) يزداد استهلاك الذاكرة كلما تنقّل المستخدم بين الصفحات ثم عاد، حتى يثقل التبويب بعد ساعة من الاستخدام. ما أنسب طريقة في Chrome DevTools لتحديد الكائنات المسرّبة؟

    • تشغيل تقرير Lighthouse وتطبيق توصيات قسم الأداء الواردة فيه
    • استخدام تبويب Coverage لمعرفة الشيفرة غير المستخدمة التي تُحمَّل في كل صفحة
    • مراقبة أحجام الطلبات في تبويب Network أثناء التنقل المتكرر بين الصفحات
    • مقارنة لقطات Heap snapshot في تبويب Memory قبل التنقل وبعده

    الشرح: مقارنة لقطات الذاكرة تُظهر الكائنات التي بقيت بعد مغادرة الصفحة، مثل عناصر DOM المنفصلة (Detached) ومستمعي الأحداث غير المزالين، ومنها تُعرف سلسلة المراجع التي تمنع جمعها. أما Lighthouse وCoverage وNetwork فتقيس التحميل وحجم الشيفرة، لا ما يبقى في الذاكرة أثناء الاستخدام.

المزيد