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

لقطة شاشة لنسخة سيئة من traverseUpUntil
تقبل الدالة المذكورة أعلاه عنصرًا ووظيفة شرطية وترجع أقرب عقدة رئيسية تفي بالوظيفة الشرطية.
const traverseUpUntil = (el, f) => {
استنادًا إلى حقيقة أنه يجب قراءة الرمز مثل النص العادي ، فإن السطر الأول يحتوي على ثلاث عيوب إجمالية.
- لا يتم قراءة معلمات الدالة مثل الكلمات .
- لنفترض أنه يمكن فهم
el
، لأن مثل هذا الاسم يستخدم عادةً للإشارة إلى عنصر ، ولكن اسم المعلمة f
لا يفسر أي شيء على الإطلاق. - يمكن قراءة اسم الوظيفة على النحو التالي: "switch حتى تمرير el f" ، والذي من الأفضل قراءته كـ "switch حتى تمرير f لـ el". بالطبع ، أفضل طريقة للقيام بذلك هي السماح للوظيفة أن تسمى
el.traverseUpUntil(f)
، ولكن هذه مشكلة أخرى.
let p = el.parentNode
هذا هو السطر الثاني. مرة أخرى المشكلة مع الأسماء ، هذه المرة مع المتغير. إذا نظر شخص ما إلى الرمز ، فمن المرجح أن يفهم ما p
. هذا هو parentNode
للمعلمة el
. ومع ذلك ، ما يحدث عندما ننظر إلى p
المستخدمة في مكان آخر ، لم يعد لدينا سياق يشرح ما هو عليه .
while (p.parentNode && !f(p)) {
في السطر التالي ، فإن المشكلة الرئيسية التي نواجهها هي عدم فهم ما يعنيه أو يفعله !f(p)
، لأن "f" يمكن أن تعني أي شيء . من المفترض أن الشخص الذي يقرأ الشفرة يجب أن يفهم أن !f(p)
هو فحص العقدة الحالية لاستيفاء شرط معين. إذا مرت ، يتم قطع الدورة.
p = p.parentNode
كل شيء واضح هنا.
return p
ليس من الواضح تمامًا ما يتم إرجاعه بسبب اسم متغير غير صالح.
الجزء 2: دعونا ريفاكتور

لقطة شاشة لنسخة جيدة من traverseUpUntil
أولاً ، نغير أسماء المعلمات وترتيبها: (el, f) =>
إلى (condition, node) =>
.
قد تتساءل لماذا بدلاً من "عنصر (عنصر روسي) استخدمت" عقدة "( عقدة روسية). استخدمته للأسباب التالية:
- نكتب رمزًا من حيث العقد ، على سبيل المثال
.parentNode
، فلماذا لا تجعلها متسقة. - "العقدة" أقصر من "العنصر" ، ولا يضيع المعنى .
ثم ننتقل إلى أسماء المتغيرات:
let parent = node
من المهم جدًا الإفصاح تمامًا عن قيمة المتغير الخاص بك في اسمه ، لذا فإن "p" أصبحت الآن " أصل" (أصل روسي). ربما لاحظت أيضًا أننا لا نبدأ بالحصول على node.parentNode
. node.parentNode
، بدلاً من ذلك نحصل على العقدة فقط.
نذهب أبعد من ذلك:
do { parent = parent.parentNode } while (parent.parentNode && !condition(parent))
بدلًا من المعتاد while
اخترت do ... while
. هذا يعني أننا بحاجة إلى الحصول على العقدة الأصلية في كل مرة قبل التحقق من الشروط ، وليس العكس. يساعد استخدام do ... while
أيضًا على قراءة التعليمات البرمجية مثل النص العادي.
لنحاول أن نقرأ: "قم بتعيين العقدة الأصل للأصل إلى الأصل ، طالما أن الأصل لديه عقدة أصل ، ووظيفة الشرط لا تعود صحيحة . " بالفعل أكثر وضوحا.
return parent
في كثير من الأحيان ، يفضل المطورون استخدام نوع ما من المتغيرات الشائعة (أو قيمة returnValue
) ، ولكن هذه ممارسة سيئة جدًا . إذا قمت بتسمية متغيرات الإرجاع الخاصة بك بشكل صحيح ، يصبح من الواضح ما يتم إرجاعه. ومع ذلك ، في بعض الأحيان يمكن أن تكون الوظائف طويلة ومعقدة ، مما يؤدي إلى الكثير من الارتباك. في هذه الحالة ، أقترح تقسيم وظيفتك إلى عدة وظائف ، وإذا كانت لا تزال معقدة للغاية ، فربما تساعد إضافة التعليقات.
الجزء 3: تبسيط الكود
الآن بعد أن جعلت الشفرة قابلة للقراءة ، حان الوقت لإزالة الشفرة غير الضرورية . أنا متأكد من أن بعضكم قد لاحظ بالفعل أننا لسنا بحاجة إلى المتغير parent
على الإطلاق.
const traverseUpUntil = (condition, node) => { do { node = node.parentNode } while (node.parentNode && !condition(node)) return node }
قمت ببساطة بإزالة السطر الأول واستبدلت "أصل" بـ "عقدة". لذا تخطيت الخطوة غير الضرورية لإنشاء "أب" وذهبت مباشرة إلى الحلقة.
ولكن ماذا عن اسم المتغير؟
على الرغم من أن "العقدة" ليست أفضل وصف لهذا المتغير ، إلا أنها مرضية. ولكن دعونا لا نتوقف عند هذا الحد ، فلنعيد تسميته. ماذا عن "currentNode"؟
const traverseUpUntil = (condition, currentNode) => { do { currentNode = currentNode.parentNode } while (currentNode.parentNode && !condition(currentNode)) return currentNode }
هذا أفضل! الآن ، عندما نقرأ الطريقة ، نعلم أن currentNode
تمثل دائمًا العقدة التي نحن فيها الآن ، بدلاً من كونها عقدة "نوعًا ما".