
عندما قرأت مقالًا آخر حول الميزات غير المعروفة في لغة جافا سكريبت وأقررت بهدوء بعض الحلول المجنونة في وحدة تحكم المتصفح ، غالبًا ما أقول في رأسي أنه ، حسنًا ، إنها بالتأكيد ليست كذلك على المنتج! بعد كل شيء ، اكتسبت اللغة منذ فترة طويلة مجتمع ضخم ولها تغطية واسعة بشكل مدهش للتنمية الصناعية. إذا كان الأمر كذلك ، فلماذا ننسى غالبًا قدرته على الفهم من قِبل الجميع ونشر حرفيًا كل هذه الإنشاءات المحددة "التي لا تنسى"؟ فقط اجعلها واضحة!
التفكير في الموضوع
يمكنك تخطي هذا graphomania.
إذا تحدثنا عن التنمية الصناعية ، في الغالبية العظمى من الحالات ، فإن الحاجة إلى دعم الكود هو أكثر أهمية من حل مهمة يطرحها النشاط التجاري. بالنسبة للكثيرين ، هذا واضح بالنسبة للبعض - جزئيًا (بالطبع ، توجد أيضًا D'Artagnans النادرة). كلما كان كودنا أكثر وضوحًا ، كلما قلت خطورة الوصول إلى الرف المترب ، ولكنا ولخلفائنا لكسب مشاكل في الجهاز العصبي.
ليس سرا أن جافا سكريبت مدهشة في مرونتها ، وهي فضيلة أعظم وعنة مزعجة. مسار مطور جافا سكريبت طويل وممتع للغاية: نحن نمتص كتابًا تلو الآخر ، ومقالًا مقالًا ، ونكتسب تجربة فريدة من نوعها ، ولكن في بعض الأحيان يكون محددًا بلغة معينة. إن التوزيع الأوسع للغة وفي نفس الوقت ، هناك عدد غني من عدم الوضوح المتراكم والمتغذى ، يسهم في تكوين جبهتين: أولئك الذين يعبدون هذه اللغة تقريباً ، وأولئك الذين ينظرون إليها على أنها بطة حقوق خرقاء ومتمايلة.
وسيكون كل شيء على ما يرام ، ولكن في كثير من الأحيان ممثلين من كلا الجبهتين تعمل على نفس المشروع. وكالعادة ، فإن كل الممارسات المقبولة هي سوء فهم (عدم الرغبة في فهم وحتى تجاهل) رمز الآخر. وفي الواقع ، "لقد حصلت على مطور جافا ، وليس هذا لك!" . يضيف أتباع جافا سكريبت أنفسهم إلى النار ، قائلين "لا أحد يعرف جافا سكريبت!" نعم "يمكنني كتابتها في سطر واحد على شبيبة!" . أعترف أنني نفسي أسيء استخدام البرمجة غير الطبيعية في وقت فراغي ...
تبدأ في الشعور بهذه المشكلة عندما تأخذ مكانًا هامشيًا وتكتسب بعض الخبرة في العمل مع الأشخاص وكودهم على جانبي المتاريس. يكون التخطيط والاجتماعات الأخرى أكثر إنتاجية عندما يفهم كل المطورين بعضهم البعض ، ليس فقط على مستوى خطوط العمل ، ولكن على الأقل قليلاً على مستوى تنفيذها. يكون لعامل الجهير السيئ السمعة تأثير أقل على المشروع ، عندما لا يتجاهل بقية الفريق تصحيح حالة سطر من ملف .js في حالة الإصابة بمرض الواجهة الأمامية الفردية. تصبح عملية تبادل المعرفة داخل الفريق وخارجه أكثر شفافية للجميع عندما يكون لدى كل شخص صورة أكثر تفصيلاً. حسنا وجميع في نفس السياق.
أنا لا أحث أي شخص على "ملء الزجاج" أو "على شكل حرف T" (كيف نقول ذلك الآن؟) ، لكن لماذا لا نرفع هذا الستار قليلاً ، ولو من مجتمع JavaScript فقط؟ للقيام بذلك ، يكفي فقط توضيح القليل من التعليمات البرمجية الخاصة بنا ، باستخدام مرونة اللغة في عدم التباهي بل للفهم.
يشبون وتحمل المسؤولية
من جانبها ، أدركت JavaScript دورها منذ فترة طويلة ، ليس كلغة لتفاعلية صفحات الإنترنت و "الإلتصاق" بمواردها ، ولكن كأداة قوية وكافية لإنشاء تطبيقات شاملة متعددة المنصات وغالباً قابلة للتطوير.
تم تصميم "لغة البرمجة الأكثر إساءة فهمًا" هذه في الأصل لمصممي الويب ، وهي تمشي على الماء لفترة طويلة ، على الرغم من تزايد شعبيتها وأهميتها المتزايدة بسرعة. من 13 إلى 14 سنة قبل إصدار ECMAScript 5.1 ، من الصعب تذكر أي تغييرات مهمة في المعيار أو فهم متجه تطورها. في ذلك الوقت ، قدم مجتمعه مساهمة كبيرة في تكوين النظام البيئي للغة: النموذج الأولي ، مسج ، MooTools ، إلخ. بعد تلقي هذه التعليقات من المطورين ، قام جافا سكريبت بعمل كبير على الخلل: الإصدار الصاخب لمدة 6 سنوات من ES6 في عام 2015 والآن الإصدارات السنوية من ECMAScript ، وذلك بفضل إعادة تصميم لجنة TC39 لإدخال ميزات جديدة للمواصفات.
حسنًا ، عندما أصبحت تطبيقاتنا كبيرة بدرجة كافية ، لم يعد نموذج OOP النموذجي لوصف أنواع المستخدمين مبررًا بسبب اتباع نهج غير معتاد. حسنا على محمل الجد ، ما هو؟
function Animal() { } function Rabbit() {} Rabbit.prototype = Object.create(Animal.prototype); Rabbit.prototype.constructor = Rabbit;
لم تظهر الفصول باللغة ، ولكن ظهر بناء الجملة. وأصبح الرمز متاحًا لأتباع النموذج التقليدي الموجه إلى الطبقات:
class Animal { constructor() { } } class Rabbit extends Animal {}
الآن في مرحلة المرشح للنشر ، توجد حقول خاصة في الفصل. من الصعب تصديق أننا عاجلاً أم آجلاً سوف نتوقف عن الضحك على بعضنا البعض باتفاق على تسمية الممتلكات الخاصة من خلال الشرطة السفلية.
في الوقت نفسه ، في اللغة التي تكون فيها الوظيفة كائنًا من الدرجة الأولى ويحدث حدث ثابت ، فمن الشائع جدًا:
let that = this; setTimeout(function() { that.n += 1; }, 1000);
ثم تبدأ الشروحات حول هذه السياقات والإغلاق في JavaScript ، الأمر الذي يخيف كل مطور خارجي ثانٍ. ولكن في كثير من الحالات ، تتجنب اللغة المفاجآت غير الضرورية عن طريق استخدام Function.prototype.bind بشكل صريح أو مع ذلك:
setTimeout(() => this.n += 1, 1000);
لدينا أيضًا وظائف سهم ، وهذه وظائف بالفعل ، وليست واجهات وظيفية (نعم ، Java؟). جنبا إلى جنب مع مجموعة موسعة من أساليب العمل مع مجموعة ، فإنها تساعد أيضا في كتابة payline التعريفي المعتاد للحسابات:
[-1, 2, -3, 4] .filter(x => x > 0) .map(x => Math.pow(2, x)) .reduce((s, x) => s + x, 0);
اللغة تعتبر بحق نفسها متعددة النماذج. ولكن هنا مثال بسيط على توقيع بعض الوظائف:
function ping(host, count) { count = count || 5; }
أولاً ، سيطرح شخص يمر سؤالاً يقول أنه من المحتمل أن تأخذ الوظيفة الحجة الأولى فقط ، ثم تقول ما هو الجحيم في هذه الحالة ، يصبح العد منطقية! في الواقع ، وظيفة لها استخدامين: مع العد وبدون. لكن هذا غير واضح تمامًا: عليك أن تنظر إلى التنفيذ وتفهمه. يمكن أن يساعد استخدام JSDoc ، لكن هذه ليست ممارسة شائعة. وهنا انتقل JavaScript إلى الأمام ، مضيفًا الدعم ليس للحمل الزائد ، ولكن على الأقل للمعلمات الافتراضية:
function ping(host, count = 5) { }
بتلخيص ، حصلت JavaScript على عدد كبير من الأشياء المألوفة: المولدات ، التكرارات ، مجموعات المجموعات وقواميس الخريطة ، المصفوفات المطبوعة ، وحتى التعبيرات العادية بدأت ترضي بدعم lookbehind ! اللغة تفعل كل شيء لتكون مناسبة لكثير من الأشياء وتصبح ودية للجميع.
طريق مواتية للواضح
من المؤكد أن اللغة نفسها جيدة ، ومن الصعب الجدال مع ذلك! ولكن ما هو الخطأ معنا؟ لماذا نذكر العالم كله باستمرار أن جافا سكريبت مختلفة بشكل أو بآخر؟ دعونا نلقي نظرة على أمثلة لبعض التقنيات المستخدمة على نطاق واسع ونسأل عن مدى ملاءمتها.
اكتب الصب
نعم ، يحتوي JavaScript على نظام كتابة ديناميكي وضعيف ويسمح لك بإجراء عمليات على أي شيء ، مع إجراء التحويلات ضمنيًا بالنسبة لنا. ولكن في كثير من الأحيان ، لا يزال الصب الواضح ضروريًا بالنسبة لنا ، ويمكن ملاحظة ما يلي:
let bool = !!(expr); let numb = +(expr); let str = ''+(expr);
هذه الحيل معروفة لدى كل مطور جافا سكريبت وتحفزها حقيقة أنها تقول إنه يمكنك "بسرعة" تحويل شيء ما إلى شيء: السرعة تعني سجلًا قصيرًا. يمكن أن يكتب أيضا كاذبة على الفور ! 1 ؟ إذا كان المطور قلقًا جدًا من الأحرف القابلة للطباعة ، فيمكنك بسهولة تهيئة قالب مباشر ضروري أو إكمال تلقائي في IDE المفضل لديه. وإذا - بالنسبة لحجم الكود المنشور ، فإننا نديره دائمًا من خلال المشوش ، الذي يعرف أفضل من معرفتنا بكيفية تجريد كل هذا من الإنسانية. لماذا لا:
let bool = Boolean(expr); let numb = Number(expr); let str = String(expr);
والنتيجة هي نفسها ، واضحة فقط للجميع.
بالنسبة لتحويلات السلسلة ، لدينا toString ، ولكن بالنسبة للتحويلات الرقمية ، هناك قيمة مثيرة للاهتمام ، والتي يمكن أيضًا تجاوزها. مثال كلاسيكي يقدم "غير مستهل" في ذهول:
let timestamp = +new Date;
لكن Date لديه طريقة getTime معروفة ، فلنستخدمها :
let timestamp = (new Date()).getTime();
أو وظيفة جاهزة:
let timestamp = Date.now();
ليست هناك حاجة مطلقًا لاستغلال تحويل النوع الضمني.
العوامل المنطقية
يتم إيلاء اهتمام خاص للمشغلين المنطقيين AND (&&) و OR (||) ، وهي ليست منطقية تمامًا في JavaScript: فهي تقبل وتعيد القيم من أي نوع. لن ندخل في تفاصيل تشغيل آلة حاسبة التعبير المنطقي ؛ سننظر في الأمثلة. الخيار المقدم مسبقًا مع الوظيفة:
function ping(host, count) { count = count || 5; }
قد يبدو مثل هذا:
function ping(host, count) {
هذا التحقق أكثر دراية ، وفي بعض الحالات يمكن أن يساعد في تجنب الأخطاء.
بدلاً من ذلك ، يبدو الأمر وحشيًا للمطور الذي اختار في البداية مسار JavaScript. ولكن بالنسبة لمعظم الآخرين ، فإن هذا الكود أصبح متوحشًا حقًا:
var root = (typeof self == 'object' && self.self === self && self) || (typeof global == 'object' && global.global === global && global);
نعم ، إنه صغير الحجم ، ونعم ، يمكن للمكتبات الشعبية تحمل كلفته. لكن من فضلك ، دعونا لا نسيء استخدامه ، لأن الكود لدينا لن يقرأه المساهمون في جافا سكريبت ، ولكن بواسطة المطورين الذين يحلون مشاكل العمل في الإطار الزمني.
قد يحدث هذا النمط على الإطلاق:
let count = typeof opts == 'object' && opts.count || 5;
هذا بالتأكيد أقصر من المشغل الثلاثي العادي ، ولكن عند قراءة هذا الرمز ، فإن أول شيء تتذكره هو أولويات العمليات المستخدمة.
إذا كتبنا دالة مسند ، والتي نمررها إلى نفس Array.prototype.filter ، فلف قيمة الإرجاع في Boolean هي نغمة جيدة. يصبح الغرض من هذه الوظيفة واضحًا على الفور ولا يوجد تنافر بين المطورين الذين تستخدم لغاتهم العوامل المنطقية "الصحيحة".
عمليات bitwise
مثال شائع للتحقق من وجود عنصر في صفيف أو سلسلة فرعية في سلسلة باستخدام bitwise NOT (NOT) ، والتي يتم تقديمها حتى من قبل بعض البرامج التعليمية:
if (~[1, 2, 3].indexOf(1)) { console.log('yes'); }
ما المشكلة هل هذا حل؟ لا يتعين علينا التحقق ! == -1 ، نظرًا لأن indexOf سيحصل على فهرس العنصر أو -1 ، وسيضيف التلدة 1 ويغير العلامة. وهكذا ، فإن التعبير يتحول إلى "خطأ" في حالة الفهرس -1.
ولكن يمكن تجنب ازدواجية التعليمات البرمجية بطريقة أخرى: وضع فحص في وظيفة منفصلة لبعض الكائنات utils ، كما يفعل الجميع ، من استخدام عمليات bitwise لأغراض أخرى. هناك وظيفة تشمل في lodash لهذا ، وأنها لا تعمل من خلال assfucked التلدة. يمكنك أن تفرح ، لأنه في ECMAScript 2016 ، تم إصلاح طريقة Array.prototype.includes ( تحتوي السطور أيضًا على واحدة).
ولكن كان هناك! يتم استخدام تيلدا آخر (مع XOR) لتقريب الأرقام ، مع تجاهل الجزء العشري:
console.log(~~3.14);
ولكن هناك parseInt أو Math.floor لهذه الأغراض. عمليات Bitwise هنا ملائمة لكتابة التعليمات البرمجية بسرعة في وحدة التحكم ، نظرًا لأن لها أيضًا أولوية منخفضة على بقية الحساب. ولكن في مراجعة الكود ، من الأفضل عدم تفويتها.
بناء الجملة واللغة يبني
بعض الممارسات الغريبة يصعب نسبتها إلى أي قسم معين. على سبيل المثال ، يقولون أن الأقواس اختيارية عند استدعاء المُنشئ ، والتعبيران التاليان متطابقان:
let rabbit = new Rabbit(); let rabbit = new Rabbit;
وهذا هو حقا! ولكن لماذا خلق سؤال من الصفر؟ ليس كل لغة يمكن أن تتباهى بمثل هذه "الميزة". وإذا كنت لا تزال ترغب في ذلك ، فليكن اتفاقًا على المشروع بأكمله. خلاف ذلك ، هناك شعور خاطئ بأن هناك بعض الاختلاف.
موقف مماثل مع التصريح عن مجموعة من المتغيرات. يسمح لك بناء جملة var والسماح بالتوجيهات بتعريف (وتحديد) العديد من المتغيرات في وقت واحد ، مفصولة بفواصل:
let count = 5, host, retry = true;
يستخدم شخص ما موجزات السطر للقراءة ، ولكن على أي حال ، فإن بناء الجملة هذا غير شائع في اللغات الشائعة. لن يقدم أحد يد المساعدة ويسأل عما إذا كنت تكتب مثل هذا:
let count = 5; let retry = true; let host;
مرة أخرى ، إذا كان هناك اتفاق على أسلوب جيد على مستوى المشروع / الشركة ، فلا توجد أسئلة. إنه ليس عليك فقط الجمع بين الكثير من بناء الجملة لمزاجك.
هناك إنشاءات محددة في اللغة ، مثل IIFE ، والتي تتيح لك استدعاء وظيفة على الفور في مكان تعريفها. الحيلة هي أن يتعرف المحلل اللغوي على تعبير وظيفي ، وليس تعريف دالة. ويمكن القيام بذلك بطرق مختلفة: الالتفاف الكلاسيكي بين قوسين ، من خلال الفراغ أو أي مشغل أحادي آخر. وليس هناك شيء رائع عن ذلك! من الضروري اختيار الخيار الوحيد وعدم تركه دون الحاجة:
(function() { }());
لا حاجة لاستخدام المشغلين لاختراق المحلل اللغوي. عندما يأتي وافد جديد إلى المشروع ، أريد أن أغمره في منطق العمل الخاص بالتطبيق ، وألا أتغذى عليه بتفسيرات من حيث تجسست جميع علامات التعجب والفراغات هذه. هناك أيضًا إدخال كلاسيكي ثانٍ بين قوسين وتعليق مثير للاهتمام من Crockford حول هذا الموضوع.
لم يكن مظهر بناء جملة الفئة في ES6 مصحوبًا بمعدلات الوصول المعتادة. وأحيانًا يرغب المطور في التبول على الطبقات ومراقبة الخصوصية. مما يؤدي إلى رمز فرانكشتاين:
class Person { constructor(name) { let _name = name; this.getName = function() { return _name; } } toString() { return `Hello, ${this.getName()}`; } }
أي ، يتم إنشاء الملحقات للمثيل في المنشئ ، ويتم تحقيق الخصوصية من خلال وصولهم إلى متغيرات الخاصية المحلية من خلال الإغلاق. هذا المثال يبدو حتى Lacconcino جيدًا ، لكن هذا نهج غير قابل للتغيير تمامًا ، إلا إذا قمت بإنشاء حل إطار عمل موثق حوله. أيها السادة ، دعونا نستخدم إما الفصول المتاحة (وانتظر توحيد الحقول الخاصة) أو وحدة النمط الشائعة. لإنشاء نوع من الحلول المختلطة المتوسطة هنا هو شيء من هذا القبيل لنفسك ، لأن الطبقات تتوقف عن أن تكون فصولاً ، والرمز واضح.
تلخيصًا ، سيقوم بمشاركة الحس السليم مع دليل الأنماط المعتمد في المشروع ، أو تهيئة linter ، أو ببساطة شظايا الرمز مع زملائه الذين يساهمون بمكونه غير JavaScript في المشروع. تقدم اللغة عدة خيارات لكل مهمة نموذجية ، لذا فإن تحسين فهم بعضهم البعض والوقوع تحت قاسم مشترك ليس بالأمر الصعب (أو تقريبًا).
سوء المغامرة
هذا الموضوع كلي بكل تأكيد وهناك المزيد من الأمثلة ، لكن الرسالة الرئيسية للمقال هي أنه يجب عليك عدم إساءة استخدام عدم الوضوح في جافا سكريبت حيث يمكن تجنب ذلك. طبيعة اللغة فريدة من نوعها: فهي تتيح لك كتابة حلول أنيقة ومعبرة (معتدلة "مدببة") ، وكذلك مفهومة ومتاحة للجميع. أنا أختلف بشكل أساسي مع الحكمة التقليدية المتمثلة في أن جافا سكريبت "عاقب نفسه" أو "دفن تحت كومة من النوايا الحسنة والأخطاء". لأن الآن معظم الغرابة لا تتضح من اللغة ، ولكن من خلال ثقافة المطورين و (لا) غير المبالين التي تشكلت حولها.