اذهب إلى المحتوى

كتابة شيفرات جافاسكربت مقروءة: قصة نوعين من الخبراء


Ali Alrohia

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

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

هناك عدة عواملٍ يجب النظر إليها من قِبل الخبراء عند كتابة شيفرةٍ مقروءة، وتحديد الخيارات الواضحة الصحيحة منها والخاطئة.

الخيار الواضح

لقد أضاف TC39 الكثير من الميزات الجديدة إلى ECMAScript خلال السنوات الماضية بهدف تحسين تجربة المطوّر، حيث تتضمن هذه الميزات أنماطًا مُحسنةً ومستوحاةً من لغاتٍ أخرى، وأحد هذه الإضافات في ES2019 هي Array.prototype.flat()‎، حيث يُطلب مُدخلٌ واحدٌ وهو العمق أو Infinity لنشر مصفوفة، ويكون العمق الافتراضي عند عدم استخدام أي مدخلٍ هو 1، وبدون هذه الإضافة كان علينا استخدام الصيغة التالية لنشر المصفوفة في مستوٍ واحدٍ.

let arr = [1, 2, [3, 4]];
[].concat.apply([], arr); 
// [1, 2, 3, 4]

أما عند إضافة flat()‎ أصبح من المُمكن التعبير عن نفس الوظيفة باستخدام تعليمةٍ واحدةٍ وهي الدالّة التالية.

arr.flat();
// [1, 2, 3, 4]

يُعَد السطر الثاني من الشيفرة أكثر قابليةً للقراءة، وسوف يتفق على هذه النقطة كِلا النوعين من الخبراء.

لا يعلم جميع المطورين بوجود الدالّة flat()‎ وهم ليسوا بحاجةٍ لذلك، وعند ورودها فإن flat سيكون فعلًا يصف نفسه ويتضمن محتوى ما يفعله وهي أسهل للفهم من concat.apply()‎.

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

الفحص الداخلي

تتميز لغة جافاسكربت بأنها متعددة الاستخدام وواسعة الانتشار، فهي مُستخدمة في جميع أجزاء شبكة الويب، ويمكن كتابة الشيفرة نفسها بعدة طرقٍ ولكن لن تتمكن من تحديد الخيار الصحيح ما لم تفهم جيدًا الخيارات المتوفرة والقيود التي تُرافق كل منها.

ستُستخدم البرمجة الوظيفية مع map()‎ مثالًا لذلك وستُشرح جميع التكرارات التي تعطي نفس النتيجة، ويُعَد المثال التالي النسخة الأقصر من الأمثلة التي تستخدم map()‎ حيث يتسع في سطرٍ واحدٍ.

const arr = [1, 2, 3];
 let multipliedByTwo = arr.map(el => el * 2);
 // multipliedByTwo is [2, 4, 6]

سيُضاف مِحرفان فقط إلى المثال التالي وهما قوسان "()"، حيث توجد دائمًا خسارةٌ وكسب بعد أي تغيير، كما يوجد اختلافُ عند استخدام دالةٍ متعددة المتغيرات الأقواس دائمًا، ولا يوجد ضررٌ في إضافة هذين القوسين هنا فهما يحسّنان التناسق عند كتابة دالةٍ متعددة المتغيرات، وحقيقةً يجبرنا Prettier على استخدام الأقواس، فهو لا يسمح بإنشاء دالةٍ سهميّةٍ دون استخدام الأقواس.

let multipliedByTwo = arr.map((el) => el * 2);

ستُرفع سوية المثال وتُضاف أقواس مجموعة وتعليمة return لتبدو هذه الدالّة أشبه بالدوال التقليدية. هذه الإضافات مطلوبة عندما تكون الدالّة أكثر من سطرٍ واحد، وبالتأكيد ستُكتب العديد من الدوال المتضمنة أكثر من سطر واحد ضمن الشيفرة البرمجية.

let multipliedByTwo = arr.map((el) => {
 return el * 2;
 });

ستُزال الآن الدالّة السهمية وتُستخدم نفس صيغة المثال السابق لكن مع استخدام الكلمة المفتاحية function، وهذا مثير لأن هذه الصيغة سوف تعمل في جميع الحالات، ولن ينتج أي خطأ مهما كان عدد المتغيرات أو السطور، وذلك نظرًا لكونها مقروءةً ومألوفة أكثر من الصيغة المستخدمة في المثال الأول، ولكن هل هذا التغيير سيء؟ وكيف سيكون وقعه على مُبرمجٍ جديد أو شخصٍ خبير جدًا بلغةٍ مختلفةٍ عن جافاسكريبت؟ هل سوف تُشكل هذه الصيغة عبئًا على شخص يعرف جافاسكربت جيدًا بالموازنة مع المثال الأول؟

let multipliedByTwo = arr.map(function(el) {
 return el * 2;
 });

وصلنا الآن إلى الخيار الأخير وهو تمرير الدالّة فقط، حيث يمكن كتابة timesTwo بأي طريقةٍ تريدها ولا توجد مشكلة يُسببها تمرير اسم الدالّة في أية حالة، لكن لنتوقف للحظة ونفكر فيما إذا كانت هذه الصيغة تُسبب الحيرة أم لا، فإذا كنت جديدًا على طريقة كتابة هذه الشيفرة، فلن يكون واضحًا لك أن timesTwo هي دالّة وليست كائنًا، ولكن map()‎ موجودة لتُعطيك تلميحًا، لذلك هناك العديد من الأمور الهامة الواجب الانتباه إليها وهي امكانية إغفال هذه الدالة، وسهولة العثور عليها، وتأثيرها على النتيجة.

const timesTwo = (el) => el * 2;
let multipliedByTwo = arr.map(timesTwo);

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

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

ليس بالضرورة أن يكون الأحدث هو الأفضل

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

يسمح لك التعيين بالتفكيك Destructing assignment؛ إمكانية تفريغ القيم من الكائنات (أو المصفوفات) وهو مماثل للشكل الآتي.

const {node} = exampleObject;

وهو يُهيئ متغيرًا ويُسند له قيمةً في سطرٍ واحد ولكن هذا غير ضروري.

let node
;({node} = exampleObject)

يُسنِد السطر الأخير من الشيفرة البرمجية قيمةً للمتغير باستخدام التفكيك، لكن التصريح عن المتغير يشغل سطرًا قبله، وهذا أمرٌ يرغب الكثير باستخدامه، إلا أن الغالبية لا تُدرك إمكانية ذلك.

نلاحظ عند النظر إلى الشيفرة بتمعُّن؛ وجود الفاصلة المنقوطة غير المُستخدمة لإنهاء السطر كما جرت العادة، إضافةً إلى استخدام الأقواس للإحاطة بالتعليمة واستخدام أقواس مجموعة داخلها أيضًا، وبالتأكيد فإن وظيفة هذه التعليمة غير واضحة، فمن الصعب قراءتها، و يجب ألا تكون موجودة في أي شيفرةٍ مكتوبة من قِبل خبير.

let node
node = exampleObject.node

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

الشيفرة ليست كل شيء

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

تختلف صيغة لغة البرمجة التي تستخدمها من أجل فريقٍ من مطوّري جافاسكربت عن تلك التي تستخدمها من أجل فريقٍ متعدد اللغات لم يتعمق أفراده في تفاصيل اللغة، ونذكر على سبيل المثال الصيغة spread مقابل concat()‎، حيث أُضيفت spread إلى ECMAScript منذ عدة سنوات واعتُمدت على نحوٍ واسعٍ، فهي تُعَد ميزةً للصيغة تسمح لك بإنجاز العديد من الأمور وأحدها هو تعاقب تسلسل مجموعةٍ من المصفوفات.

const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = [...arr1, ...arr2];

بالرغم من فعالية وقوة spread إلا أنها ليست رمزًا بديهيًا تستطيع التنبؤ بعمله، وبالتالي إن كنت لا تعلم وظيفته فلن يكون مفيدًا.

بينما يعتقد العديد من الخبراء أن هذه الصيغة مألوفةً بالنسبة لفريقٍ من مُختصي جافاسكربت، يتساءل خبراء النوع الثاني عما إذا كان هذا الكلام صحيحًا بالنسبة لفريقٍ من المبرمجين متعددي اللغات، لذلك فمن المُحتمل أن يختار هؤلاء الخبراء استخدام concat()‎ بدلًا من spread لأن اسمها يُعبّر عن عملها.

ينتج عن الشيفرة البرمجية التالية نفس النتيجة التي تُقدمها spread في المثال السابق.

const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = arr1.concat(arr2);

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

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

الخلاصة

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

ترجمة -وبتصرّف- للمقال Human-Readable JavaScript: A Tale of Two Experts لصاحبه Laurie Barth.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...