3 أسئلة جافا سكريبت يجب الانتباه إليها أثناء مقابلات الترميز

JavaScript هي اللغة الرسمية لجميع متصفحات الويب الحديثة. على هذا النحو ، تظهر أسئلة JavaScript في جميع أنواع مقابلات المطورين.

لا تتناول هذه المقالة أحدث مكتبات JavaScript أو ممارسات التطوير الشائعة أو أي من وظائف ES6 الجديدة. بدلاً من ذلك ، يتعلق الأمر بثلاثة أشياء تظهر عادةً في المقابلات عند مناقشة JavaScript. لقد تم طرح هذه الأسئلة بنفسي ، وأخبرني أصدقائي أنه تم طرحهم عليهم أيضًا.

بالطبع هذه ليست الأشياء الثلاثة الوحيدة التي يجب عليك دراستها قبل مقابلة JavaScript - هناك العديد من الطرق التي يمكنك من خلالها الاستعداد بشكل أفضل لمقابلة مقبلة - ولكن فيما يلي 3 أسئلة قد يطلبها المحاور للحكم على مدى معرفتك وفهمك لغة JavaScript و DOM.

اذا هيا بنا نبدأ! لاحظ أننا سنستخدم Vanilla JavaScript في الأمثلة أدناه ، نظرًا لأن القائم بإجراء المقابلة سيرغب عادةً في معرفة مدى فهمك لـ JavaScript و DOM دون مساعدة مكتبات مثل jQuery.

السؤال رقم 1: تفويض الحدث

عند إنشاء تطبيق ، ستحتاج أحيانًا إلى إرفاق أدوات استماع للأحداث بأزرار أو نصوص أو صور على الصفحة من أجل تنفيذ بعض الإجراءات عندما يتفاعل المستخدم مع العنصر.

إذا أخذنا قائمة مهام بسيطة كمثال ، فقد يخبرك القائم بإجراء المقابلة أنه يريد إجراء ما عندما ينقر المستخدم على أحد عناصر القائمة. ويريدون منك تنفيذ هذه الوظيفة في JavaScript بافتراض كود HTML التالي:


    
  • Walk the dog
  • Pay bills
  • Make dinner
  • Code for one hour

قد ترغب في القيام بشيء مثل ما يلي لإرفاق مستمعي الأحداث بالعناصر:

document.addEventListener('DOMContentLoaded', function() { let app = document.getElementById('todo-app'); let items = app.getElementsByClassName('item'); // attach event listener to each item for (let item of items) { item.addEventListener('click', function() { alert('you clicked on item: ' + item.innerHTML); }); } });

بينما يعمل هذا تقنيًا ، إلا أن المشكلة تكمن في أنك تقوم بإرفاق مستمع حدث بكل عنصر على حدة. هذا جيد لعناصر 4 ، ولكن ماذا لو أضاف شخص ما 10000 عنصر (قد يكون لديهم الكثير من الأشياء للقيام بها) إلى قائمة المهام الخاصة بهم؟ بعد ذلك ستنشئ وظيفتك 10000 مستمع منفصل للحدث وتربط كل منهم بـ DOM. هذا ليس فعال جدا.

في المقابلة ، من الأفضل أن تسأل المحاور أولاً عن الحد الأقصى لعدد العناصر التي يمكن للمستخدم إدخالها. إذا كان لا يمكن أن يكون أكثر من 10 ، على سبيل المثال ، فإن الكود أعلاه سيعمل بشكل جيد. ولكن إذا لم يكن هناك حد لعدد العناصر التي يمكن للمستخدم إدخالها ، فأنت تريد استخدام حل أكثر فعالية.

إذا كان من الممكن أن ينتهي الأمر بتطبيقك مع مئات من مستمعي الأحداث ، فسيكون الحل الأكثر فاعلية هو إرفاق مستمع حدث واحد بالحاوية بأكملها ، ومن ثم التمكن من الوصول إلى كل عنصر عند النقر عليه بالفعل. يسمى هذا بتفويض الحدث ، وهو أكثر فاعلية من إرفاق معالجات أحداث منفصلة.

إليك رمز تفويض الحدث:

document.addEventListener('DOMContentLoaded', function() { let app = document.getElementById('todo-app'); // attach event listener to whole container app.addEventListener('click', function(e) { if (e.target && e.target.nodeName === 'LI') { let item = e.target; alert('you clicked on item: ' + item.innerHTML); } }); });

السؤال رقم 2: استخدام الإغلاق داخل الحلقة

يتم أحيانًا ذكر الإغلاق في مقابلة حتى يتمكن القائم بإجراء المقابلة من قياس مدى معرفتك باللغة ، وما إذا كنت تعرف متى يجب تنفيذ الإغلاق.

يحدث الإغلاق بشكل أساسي عندما يكون للوظيفة الداخلية وصول إلى متغيرات خارج نطاقها. يمكن استخدام الإغلاق لأشياء مثل تنفيذ الخصوصية وإنشاء مصانع للوظائف. سؤال المقابلة الشائع فيما يتعلق باستخدام الإغلاق هو شيء من هذا القبيل:

اكتب دالة تتكرر في قائمة الأعداد الصحيحة وتطبع فهرس كل عنصر بعد تأخير لمدة 3 ثوانٍ.

يبدو التنفيذ الشائع (غير الصحيح) لهذه المشكلة كما يلي:

const arr = [10, 12, 15, 21]; for (var i = 0; i < arr.length; i++) { setTimeout(function() { console.log('The index of this number is: ' + i); }, 3000); }

إذا قمت بتشغيل هذا ، فسترى أنك تحصل بالفعل على 4 مطبوعة في كل مرة بدلاً من 0 ، 1 ، 2 ، 3 بعد تأخير لمدة 3 ثوانٍ.

لتحديد سبب حدوث ذلك بشكل صحيح ، سيكون من المفيد أن يكون لديك فهم لسبب حدوث ذلك في JavaScript ، وهو بالضبط ما يحاول القائم بإجراء المقابلة اختباره.

والسبب في ذلك هو أن setTimeoutالوظيفة تنشئ دالة (الإغلاق) لديها وصول إلى نطاقها الخارجي ، وهو الحلقة التي تحتوي على الفهرس i. بعد مرور 3 ثوانٍ ، يتم تنفيذ الوظيفة وطباعة القيمة iالتي تكون في نهاية الحلقة عند 4 لأنها تدور خلال 0 و 1 و 2 و 3 و 4 وتتوقف الحلقة أخيرًا عند 4.

هناك بالفعل عدة طرق لكتابة الوظيفة بشكل صحيح لهذه المشكلة. هنا اثنين منهم:

const arr = [10, 12, 15, 21]; for (var i = 0; i < arr.length; i++) { // pass in the variable i so that each function // has access to the correct index setTimeout(function(i_local) { return function() { console.log('The index of this number is: ' + i_local); } }(i), 3000); }
const arr = [10, 12, 15, 21]; for (let i = 0; i < arr.length; i++) { // using the ES6 let syntax, it creates a new binding // every single time the function is called // read more here: //exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads setTimeout(function() { console.log('The index of this number is: ' + i); }, 3000); }

السؤال الثالث: الرفض

هناك بعض أحداث المتصفح التي يمكن إطلاقها عدة مرات خلال فترة زمنية قصيرة بسرعة كبيرة ، مثل تغيير حجم نافذة أو التمرير لأسفل الصفحة. إذا قمت بإرفاق مستمع حدث بحدث تمرير النافذة على سبيل المثال ، وقام المستخدم بالتمرير المستمر لأسفل الصفحة بسرعة كبيرة ، فقد يتم إطلاق الحدث آلاف المرات في غضون 3 ثوانٍ. هذا يمكن أن يسبب بعض مشاكل الأداء الخطيرة.

إذا كنت تناقش إنشاء تطبيق في مقابلة ، وأحداث مثل التمرير ، أو تغيير حجم النافذة ، أو الضغط على المفتاح ، فتأكد من ذكر التنبيه و / أو الاختناق كطريقة لتحسين سرعة الصفحة والأداء. مثال حقيقي مأخوذ من منشور الضيف هذا حول حيل css:

في عام 2011 ، ظهرت مشكلة على موقع Twitter على الويب: عندما كنت تقوم بالتمرير لأسفل على موجز Twitter الخاص بك ، أصبح بطيئًا وغير مستجيب. نشر John Resig منشور مدونة حول المشكلة حيث تم شرح مدى سوء الفكرة في إرفاق وظائف باهظة الثمن مباشرة scrollبالحدث.

يعد التخفيض أحد الطرق لحل هذه المشكلة عن طريق تحديد الوقت الذي يجب أن يمر به حتى يتم استدعاء الوظيفة مرة أخرى. ومن شأن التنفيذ الصحيح للdebouncing بالتالي مجموعة عدة مكالمات وظيفة في واحدة وتنفذ مرة واحدة فقط بعد مرور بعض الوقت. إليك تطبيق في JavaScript عادي يستخدم موضوعات مثل النطاق والإغلاق وهذا وأحداث التوقيت:

// debounce function that will wrap our event function debounce(fn, delay) { // maintain a timer let timer = null; // closure function that has access to timer return function() { // get the scope and parameters of the function // via 'this' and 'arguments' let context = this; let args = arguments; // if event is called, clear the timer and start over clearTimeout(timer); timer = setTimeout(function() { fn.apply(context, args); }, delay); } }

هذه الوظيفة - عند الالتفاف حول حدث - لن يتم تنفيذها إلا بعد انقضاء فترة زمنية معينة.

يمكنك استخدام هذه الوظيفة على النحو التالي:

// function to be called when user scrolls function foo() { console.log('You are scrolling!'); } // wrap our function in a debounce to fire once 2 seconds have gone by let elem = document.getElementById('container'); elem.addEventListener('scroll', debounce(foo, 2000));

Throttling هو أسلوب آخر مشابه للخفض ، باستثناء أنه بدلاً من الانتظار لبعض الوقت لتمريره قبل استدعاء وظيفة ، فإن الاختناق ينشر فقط استدعاءات الوظيفة عبر فترة زمنية أطول. لذلك ، إذا حدث حدث 10 مرات في غضون 100 مللي ثانية ، فقد يؤدي الاختناق إلى انتشار كل استدعاء من استدعاءات الوظائف ليتم تنفيذها مرة كل ثانيتين بدلاً من إطلاق كل 100 مللي ثانية.

لمزيد من المعلومات حول التنبيه والاختناق ، قد تكون المقالات والبرامج التعليمية التالية مفيدة:

  • Throttling and Debouncing في JavaScript
  • الفرق بين الخنق والخفض
  • أمثلة على الخنق والخفض
  • مشاركة مدونة Remy Sharp على استدعاءات وظائف Throttling

إذا كنت قد استمتعت بقراءة هذه المقالة ، فقد ترغب في قراءة دروس JavaScript وحل بعض تحديات ترميز JavaScript التي أستضيفها على Coderbyte. أحب أن أسمع رأيك!