لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 11/23/20 في كل الموقع
-
"أريد تعلم البرمجة لكنني لا أعرف من أين أبدأ!" هذه هي أكثر عبارة تتردد على سمعي من حديثي العهد بالبرمجة، إذ يأتيني هذا السؤال مرارًا وتكرارًا؛ وفي كل مرة أحاول أن أجيب عنه في سياقه، أجد أنني أضيف معلومات جديدة على إجاباتي السابقة، لذا قررت كتابة هذا المقال بعنوان "تعلم البرمجة" لعله يفيد الراغبين في تعلم تطوير التطبيقات في بدء رحلتهم مع تعلم البرمجة من الصفر. جدول المحتويات حرصًا على تنظيم المقالة ولتسهيل الوصول إلى القسم الذي تريده بسهولة، سنذكر هنا جدول المحتويات باختصار: ما هي البرمجة؟ لماذا تتعلم البرمجة؟ ما عليك معرفته لتصبح مبرمجًا الأدوات اللازمة للبدء في تعلم البرمجة لماذا هناك العديد من لغات البرمجة؟ مفاهيم البرمجة مصادر تعلم البرمجة تطوير واجهات المستخدم تطوير الواجهات الخلفية تعلم تطوير تطبيقات الجوال تطوير الألعاب تطوير الأنظمة المدمجة تطوير تطبيقات سطح المكتب كيفية اختيار لغة البرمجة التي تناسبك نصائح لتعلم البرمجة ما هي البرمجة؟ البرمجة هي عملية تقسيم مهمة معينة يراد تنفيذها عبر الحاسوب إلى أجزاء صغيرة ومترابطة وقابلة للتنفيذ بأوامر بسيطة. بعد ذلك، تجري كتابة هذه الأوامر والتعليمات بإحدى لغات البرمجة، والتي هي وسيلة للتخاطب مع الحاسوب. إليك المثال العملي التالي الذي يشرح ماهية البرمجة: إن كنت تتوقع زيارة صديق لك اليوم، واتصل بك ليقول لك: "أنا واقف بجانب الحديقة ولا أعرف كيف أصل إلى منزلك". أنت عادةً تمر كل يوم من جانب الحديقة وتعرف الطريق بينها وبين منزلك شبرًا بشبر. برأيك هل ينفع إن قلت له: "منزلي معروف وقريب من الحديقة وأنا كل يوم أمر من جانبها"؟ لا، بالتأكيد. تحتاج إلى أن تقسِّم المشكلة إلى أجزاء تمثل خطوات بسيطة يستطيع صديقك فهمها وتنفيذها. مثلًا، أخبره أن ينفذ الأوامر التالية: "سر إلى الأمام عشرة أمتار" ثم "اتجه إلى اليمين" ثم "سر إلى نهاية الشارع" ثم "اتجه إلى اليسار". أخبره بعد ذلك: "عُدَّ الأبنية الموجودة على اليسار حتى تصل إلى البناء الرابع" ثم "اصعد إلى الطابق الثاني" ثم "اطرق على الباب الذي سيظهر أمامك". مبارك! بهذه الطريقة، تستطيع أن تدل صديقك على منزلك بدقة. البرمجة هي الشيء نفسه تمامًا. فهل ترى التعابير المكتوبة بين علامتي الاقتباس؟ إنها التعابير التي تكتب بإحدى لغات البرمجة والتي تخاطب الحاسوب بدلًا من صديقك السابق. لغات البرمجة هي مجموعة من المفردات والقواعد اللغوية التي تشكل لغةً وسيطةً للتخاطب مع الحاسوب وأمره بتنفيذ تعليمات وأشياء محدَّدة. فلا الحاسوب يفهم لغة البشر ولا البشر يفهمون لغة الحاسوب، لذا كان هنالك حاجة ملحة لوجود لغة وسيطة يفهمها كلاهما؛ نتيجةً لذلك، انبثق مفهوم لغة البرمجة. بعبارة أخرى، لو أردنا أن نقول للحاسوب "افعل كذا"، فسنحتاج إلى لغةٍ مشتركةٍ بيننا وبينه ليفهم ما نبتغيه، وهنا يأتي دور لغات البرمجة، إذ يمكنك أن تعدّ لغات البرمجة على أنها وسيط بين المبرمج والحاسوب. يهتم المبرمج بالتفكير في تسلسل الخطوات التي على الحاسوب القيام بها لإتمام العمل المطلوب منه (مثل حساب العمر اعتمادًا على تاريخ الولادة)، ثم كتابة هذه الخطوات بترتيب منطقي بإحدى لغات البرمجة. ربما لاحظتَ في الجملة السابقة أن جزءًا من مهمة المبرمج هو التفكير المنطقي، وهذا يجعلنا ننتقل إلى السؤال الشائع "هل أستطيع تعلم البرمجة وأصبح مبرمجًا؟" أو "هل أنا مؤهل لأصبح مبرمجًا؟". لماذا تتعلم البرمجة؟ يبدو أن تعلم البرمجة من الصفر ليس بالصعوبة التي توقعتها، لكنك تريد حافزًا يجعلك تتعلم البرمجة. تسمع كثيرًا أن البرمجة هي مجال المستقبل، وأن وظائف المبرمجين ستكتسح مجال التوظيف في السنوات القادمة؟ أستطيع أن أؤكد لك ذلك، كما أنَّ وظائف البرمجة هي من أعلى الوظائف دخلًا. فلو كنت تريد بدء مشوارك الاحترافي وتريد عملًا مستقرًا وذا دخلٍ ممتاز، فإن تعلم البرمجة والعمل بها هو أفضل خيارٍ أمامك. وظائف البرمجة مريحة عمومًا، فالعمل كله مكتبي أمام حاسوب في بيئة مريحة ومناسبة، وأغلبية الشركات تتبع نظام العمل 40 ساعة في الأسبوع (أي 5 أيام لمدة 8 ساعات يوميًا)، ولا تغفل عن قدرتك على العمل عن بعد من خلال الانترنت أو كمستقل في أوقات فراغك. تعلم البرمجة سيوسع أفق تفكيرك كثيرًا، خصوصًا أن تعاملك مع الحاسوب يتبع إلى التفكير المنطقي، وستجد أن البرمجة ستسهل لك القيام بأمور أخرى في الحاسوب. ما عليك معرفته لتصبح مبرمجًا يتردد الكثيرون في تعلم البرمجة متذرعين بأن مستواهم في الرياضيات ليس ممتازًا، وهذا ليس صحيحًا، فصحيحٌ أنَّ هنالك أمور تعترضك أثناء أداء عملك كمبرمج تتطلب خبرة في الرياضيات، إلا أنَّه قد تمر عليك فترات طويلة لا تحتاج فيها إلى مسائل رياضية. كل ما يلزمك للبدء في تعلم البرمجة هو الأساسيات التي يعرفها الجميع. إلى حين اعتراضك أية مسألة أو مشكلة تتطلب مهارة في الرياضيات، هنالك الكثير من المصادر والمراجع التي تستطيع الرجوع إليها آنذاك. بعبارة أخرى، أجِّل هذا الأمر قليلًا ولا تخف. الأهم من ذلك هو أن تكون قادرًا على التفكير بشكل منطقي. التفكير المنطقي التفكير المنطقي هو المهارة التي تجمع كافة المبرمجين تحت مظلة واحدة، وهي أساس كتابة الخوارزميات، إذ يجب أن تكون قادرًا على اكتساب هذه المهارة وتطويرها. الخوارزميات كلمة "الخوارزميات" هي الكلمة المرعبة التي ينفر منها البعض، فكل ما يتخيلونه عند ذكرها هو الرياضيات المعقدة والمعادلات الطويلة والرموز العجيبة، لكن الأمر بسيط جدًا؛ فالخوازرميات هي تطبيقٌ للتفكير المنطقي في خطوات متسلسلة واضحة تمامًا لحل مشكلة ما. لكي أوضِّح لك أن الخوارزميات ليست أمرًا معقدًا، سأخبرك بكيفية كتابة برنامج يسأل المستخدم عن سنة ميلاده، ثم يعيد عمره الحالي بالسنوات. الخطوة الأولى: إظهار رسالة نصية نطلب فيها من المستخدم إدخال تاريخ ميلاده. الخطوة الثانية: تخزين سنة الميلاد التي أدخلها المستخدم. الخطوة الثالثة: الحصول على السنة الحالية. الخطوة الرابعة: طرح مدخلات المستخدم من السنة الحالية. الخطوة الخامسة والأخيرة: إظهار الناتج. ما سبق هو خوارزمية بسيطة تتألف من خطوات متسلسلة، لكن إذا أمعنّا النظر فيها سنجد خللًا في حال أدخل المستخدم تاريخًا أكبر من التاريخ الحالي، أي لو أدخل 2050 مثلًا بدلًا من 1995. عندها سيصبح العمر المعاد من الخوارزمية سالبًا، ويمكننا أن نحل هذه الإشكالية منطقيًا بوضع شرط يمنع المستخدم من إدخال تاريخ أكبر من التاريخ الحالي. إطارات العمل كلمة أخرى شائعة جدًا في عالم البرمجة هي "إطارات العمل" frameworks، إطارات العمل هي مجموعة من الشيفرات البرمجية التي تسهل على المبرمج إنشاء التطبيقات، بتوفير وحدات جاهزة تقدم خدمات متنوعة مثل تسجيل المستخدمين، وإرسال البريد الإلكتروني، والتعامل مع قواعد البيانات. أي يمكنك أن تعدّها أدوات برمجية تساعدك في برمجة تطبيقك وتسهِّل لك فعل ذلك. الأدوات اللازمة للبدء في تعلم البرمجة تحتاج إلى حاسوبٍ بمواصفات جيدة (ليس من الضروري أن يكون من أفضل الحواسيب، وإنما أن يمتلك مقدارًا جيدًا من الذاكرة العشوائية). لا ننصح بمواصفات معينة أو نظام تشغيل معين، استعمل ما يحلو لك وما ترى نفسك معتادًا عليه (سواءً كان ويندوز أو لينكس أو ماك). ستحتاج أيضًا إلى اتصالٍ جيد بالإنترنت للوصول إلى المواد التعليمية، ولتنزيل البرمجيات والمكتبات اللازمة للتطوير. أما بخصوص أدوات التطوير، فستحتاج إلى برمجية لكتابة الشيفرات، وهنالك نوعان رئيسيان لها: المحررات النصية: مثل Visual Studio Code أو Atom أو Sublime Text أو Bracktes أو Notepad++. وهذه المحررات النصية تكون بسيطة في أغلبها، وتوفر ميزات أساسية مثل تلوين الشيفرات، وبعض ميزات الإكمال التلقائي، وتدعم أغلبيتها إضافات لزيادة وظائفها. وظيفة هذه المحررات النصية عمومًا هي تعديل الشيفرات بسهولة وسرعة. ننصحك بتجربة Visual Studio Code لشهرته حاليًا وكثرة إضافاته ودعمه الممتاز من شركة Microsoft. بيئات التطوير المدمجة: مثل Visual Studio و Eclipse و Android Studio و NetBeans و Apple Xcode وغيرها. وهذه البيئات توفر ميزات أكثر بكثير من المحررات النصية، مثل تشغيل الشيفرات وتنقيحها (debugging) وميزات التحكم بالإصدارات (version control) والاتصال بقواعد البيانات وخلاف ذلك. لماذا هناك العديد من لغات البرمجة؟ قد تتساءل، لماذا هناك العديد من لغات البرمجة؟ أليست هذه اللغات كلها تنفذ الهدف ذاته؟ لماذا لا يكون هنالك لغة موحدة بين المبرمجين والحاسوب؟ الحقيقة أنّه توجد لغة برمجة واحدة ولكن ليست إحدى اللغات التي تراها أمامك في الصورة. اللغة التي نشير إليها هي لغة الآلة التي يستطيع معالج الحاسوب قراءتها وفهمها. أتتساءل ما هي لغة الآلة وكيف تبدو؟ إليك مقطعًا منها: معلومٌ أنّ معالج الحاسوب لا يفهم شيئًا سوى الأصفار والواحدات، وهذه اللغة -أي لغة الآلة- هي تمثيل للأصفار والواحدات بطريقة تخبر الحاسوب بما يجب عليه فعله. الجدير بالذكر أن هذه اللغة عصية الفهم على البشر، وحتى إن استطعت كتابة شيفرة مثل الشيفرة الموضحة بالصورة (كما في السنوات الأولى من بداية اختراع الحاسوب)، لن يفهمها الآخرون ولن يستطيع أحد التعديل على الشيفرة وتطويرها لاحقًا باستثنائك. سعيًا لإيجاد لغة قريبة من لغة البشر، انقسمت لغات البرمجية إلى قسمين: لغات البرمجة منخفضة المستوى، ولغات البرمجة عالية المستوى وذلك تبعًا لمدى قربها من لغة الآلة أو لغة البشر على التوالي. أي أنّ لغات البرمجة منخفضة المستوى هي اللغات الأقرب للغة الآلة آنفة الذكر مثل لغة التجميع Assembly language، ولغات البرمجة عالية المستوى هي اللغات الأقرب للغة البشر مثل لغة بايثون وجافا. تنفيذ البرامج المكتوبة بلغات برمجة عالية المستوى يقودنا الحديث عن اللغات عالية المستوى واللغات منخفضة المستوى إلى الحديث عن كيفية تنفيذ المعالج للشيفرة المكتوبة بلغة عالية المستوى لا يفهمها المعالج (أليس هذا ما تفكر به الآن؟). عرفنا أن المعالج يفهم الأوامر والتعليمات المكتوبة بلغة منخفضة المستوى (لغة الآلة)، فمَثَلُ هذه العملية كمَثَلِ شخصٍ أجنبي تعلم اللغة العربية وبدأ التحدث مع ناطقٍ باللغة العربية، إذ يمكن لهما التواصل مباشرةً - ليخبر كل منها ما يريد من الآخر فعله - دون وسيط. أمَّا مَثَلُ كتابة برنامج بلغة عالية المستوى أقرب إلى لغة البشر والطلب من الحاسوب تنفيذه كمثل ناطق باللغة الهندية يريد التخاطب مع ناطق باللغة العربية دون أن يفقه أحدهما لغة الآخر. في هذه الحالة، لن يستطيع أحدهما فهم ما يتكلم به الآخر وستفشل عملية التواصل. قد تقول: لماذا لا يحضران مترجمًا يترجم ما يقوله كل منها للآخر؟ حسنًا، هذا ما يحصل تمامًا عندما يراد تنفيذ برنامج بلغة لا يفهمها معالج الحاسوب. في اللغات البشرية، هنالك نوع واحد من المترجمين يعرفه الجميع للترجمة من لغة إلى آخرى؛ أما في لغات البرمجة، هنالك نوعان من المترجمين بين اللغات هما: المفسر interpreter، والمترجم compiler. بناءً على ذلك، تنقسم لغات البرمجة إلى لغات مفسرة ولغات مترجمة. (من الآن وصاعدًا، كلما ذكرنا لغات البرمجة، فنحن نشير إلى لغات البرمجة عالية المستوى.) -المفسر (interpreter): وهو برنامج خاصٌ يفسِّر الشيفرة المصدرية لبرنامج مكتوب بلغة عالية المستوى سطرًا بسطر ويحولها إلى لغة منخفضة المستوى لينفذها الحاسوب مباشرةً. -المترجم (compiler): وهو برنامج خاصٌ يحوِّل الملفات المصدرية لبرنامج مكتوب بلغة عالية المستوى إلى ملف تنفيذي مكتوب بلغة الآلة دفعةً واحدةً، ثم يمكن تشغيل الملف التنفيذي على الحاسوب للقيام بالمهمة المطلوبة. لماذا يوجد الكثير من لغات البرمجة عالية المستوى؟ الآن وبعد أن عرفت الفرق بين لغة الآلة ولغة البشر، لربّما ما زلت تتساءل عن كثرة اللغات البرمجية عالية المستوى المتوافرة وعدم وجود لغة واحدة. نستطيع القول أنك خطوت خطوةً جيدةً للأمام إذ أصبحت الآن أكثر دقة. جواب سؤلك هو أنّ كل لغات البرمجة تُستخدم لتحويل فكرة منطقية إلى سلسلة أوامر يمكن للحاسوب أن ينفذها. فعلى سبيل المثال لا الحصر يمكنك استخدام أي من Ruby أو Java أو Python أو C# أو Go أو JavaScript لبناء موقع ويب. لكن يمكنك أن تعدّ لغات البرمجة على أنها أدوات، وكل أداة تسهّل مهمة دونًا عن أخرى. فعلى سبيل المثال، السيارة والحافلة والدراجة والمحراث الزراعي كلها وسائط نقل، لكنها مختلفة الاستخدام؛ فلا يمكنك أن تذهب وعائلتك لقضاء إجازة صيفية مستخدمين المحراث الزراعي، كما لا يمكنك استخدام سيارة سباق في مدينة مكتظة ذات شوارع ضيقة للذهاب بها إلى العمل. مع أن آلية عمل هذه المركبات متشابهة. والأمر سيانٌ بالنسبة إلى البرمجة. خلاصة القول أنّ هنالك لغات برمجة متخصصة بإنشاء تطبيقات سطح المكتب، وأخرى متخصصة بإنشاء تطبيقات الجوال، وأخرى تستعمل خصيصًا لمواقع الويب، وأخرى لبرمجة العتاد، وهذا ما يحيلنا إلى الحديث عن مجالات البرمجة واللغات الأنسب لكلٍ منها. مفاهيم البرمجة "حسنًا، اقتنعتُ أن البرمجة مناسبة لي وليست صعبة كما كنتُ أتخيل، من أين أبدأ طريقي في تعلم البرمجة إذًا؟" قبل الإجابة عن السؤال السابق، سآخذ وقتي لأشرح لك بعض المفاهيم الخاصة بالبرمجة، ثم سنتحدث عن مجالات العمل فيها وما المسار الأفضل لتعلمك كلًا منها. أنت تعلم أن البرنامج هو سلسلة أوامر ينفذها الحاسوب لحل مشكلة ما، والبرنامج نفسه مكتوب بلغة يفهمها الحاسوب تسمى لغة الآلة. من الأمور الملحوظة التركيز كثيرًا على لغة البرمجة ذاتها أثناء بداية تعلم البرمجة. سأخبرك حقيقةً صادمةً: "لغة البرمجة التي تستعملها ليست بتلك الأهمية التي تتوقعها"، أنا لا أقول لك أن جميع لغات البرمجة متماثلة أو تُستعمل لنفس الاستعمالات، لكن لا تركِّز كثيرًا على تعلم كيفية الكتابة في لغة برمجة ما وتهمل المفاهيم البرمجية التي تقف وراءها. المتغيرات والثوابت عليك أن تتعرف على مفهوم المتغيرات variables المستعمل في جميع لغات البرمجة، والذي يعني إسناد قيمة ما إلى رمز أو كلمة وتخزين هذه القيمة في الذاكرة. فلو أردنا أن نخزن العبارة "Hello World" في متغير ما فنكتب شيئًا شبيهًا بما يلي: var variable_name = "Hello World"; أي أننا نسند الجزء الموجود على يمين إشارة المساواة إلى المتغير المذكور على يسار إشارة المساواة. يمكننا أن نستنتج من اسم "المتغيرات" أن قيمتها قابلة للتغيير خلال تنفيذ البرنامج، فيمكننا في مكانٍ ما من الملف المصدري أن نعيد تعريف المتغير السابق بكتابة: var variable_name = "New value"; أما الثوابت فهي تتشابه مع المتغيرات في كثيرٍ من النواحي، إلا أنك لا تستطيع إعادة تعريف قيمتها بعد تعريفها أول مرة. قد تستفيد من الثوابت عندما تكون متأكدًا تمامًا من عدم تغيير القيمة خلال فترة تنفيذ البرنامج. فلو أردنا تعريف ثابت اسمه pi يحتوي على القيمة 3.14 (والتي سنعرف أنها لن تتغير مطلقًا)، فيمكننا أن نكتب: const pi = 3.14; وإذا حاولتَ تغيير قيمة الثابت بعد تعريفه فستحصل على رسالة خطأ. الشروط تدعم جميع لغات البرمجة تعريف شروط تُنفَّذ في حالات معينة. ففي الخوازرمية السابقة التي شرحنا فيها حساب العمر، يمكننا أن نكتب الشرط بالعربية كما يلي: إذا كان (تاريخ الميلاد أكبر من التاريخ الحالي): نقول للمستخدم أن هنالك خطأ وإلا: سنحسب العمر بطرح تاريخ الميلاد من التاريخ الحالي وإذا أردنا كتابتها بإحدى لغات البرمجة فستبدو شبيهةً بما يلي: if ( user_birth > current_year ) { // ERROR! } else { age = current_year - user_birth; } لا تلقِ للأقواس بالًا، فهي جزء من لغة البرمجة، وقد تختلف من لغة لأخرى، وليست موضع اهتمامنا حاليًا. حلقات التكرار ماذا لو كانت لدينا قاعدة بيانات فيها أكثر من مستخدم ولكل مستخدم تاريخ ميلاد. لا تقل لي سنأخذ التواريخ يدويًا وندخلها إلى البرنامج! هذا مضيعةٌ للوقت، والصواب هو إنشاء حلقة تكرار تأخذ قيمة تاريخ الميلاد الخاص بكل مستخدم ثم تحسب عمره كما أسلفنا في القسم السابق. دعنا نعدل الخوارزمية البسيطة لنضيف تكرارًا فيها: ما أجمل البرمجة! تخيل لو كان عندك ألف مستخدم، وكان عليك حساب أعمارهم، يمكنك بضغطة زر أن تحسبها كلها. الدوال الدالة function هي مجموعة من التعليمات البرمجية التي تقبل مدخلات وتعيد القيمة المطلوبة. تكون الدوال عادةً قصيرةً وتقوم بمهمة وحيدة فقط. فمثلًا لو أردنا تعريف دالة باسم divide تقبل عددين، وتعيد ناتج قسمة العدد الكبير على الصغير، فيمكننا أن نكتب الخورزمية الآتية: مصادر تعلم البرمجة للمبتدئين أول ما سيتبادر إلى ذهنك بعد قرارك تعلم البرمجة هو من أين سأتعلم؟ هنا يأتي دور القسم التعليمي المتكامل في حسوب ليقدم للمبتدئ (والمحترف على حدٍ سواء) محتوى علمي مميز ومبسط. تزخر أكاديمية حسوب بالمحتوى المفيد عن تعلم البرمجة للمبتدئين وحتى الخبراء أي على كافة المستويات، ستجد فيها أقسامًا تشرح لغات البرمجة وتقنياتها كلها. ولدينا قسم للأسئلة البرمجية التي يمكنك أن تطرح فيه سؤالك وسيجيب عليه أحد أفراد مجتمع أكاديمية حسوب. أضف إلى ذلك أن الأكاديمية توفر قسمًا للدورات المتخصصة التي تبدأ معك من الصفر وحتى احتراف لغة البرمجة التي تريد تعلمها مع كادر من المدربين المختصين الذي يقدمون لك المساعدة ويجيبون عن جميع استفساراتك. وهنالك قناة للأكاديمية على يوتيوب ننشر فيها دوريًا دروسًا قصيرةً عن تساؤلات محددة ومفاهيم البرمجة وخلافه. لا تنسَ الاشتراك في قناة الأكاديمية لتصلك الفيديوهات الجديدة. ماذا لو أردتَ التعمق أكثر في لغة معيّنة؟ تأتي هنا موسوعة حسوب التي توفِّر توثيقًا عربيًا كاملًا وعالي الجودة، مدعّمًا بالأمثلة لمختلف لغات البرمجة وتقنيات تطوير الويب والجوال. ستكون الموسوعة مرجعًا تعود إليه في مسيرتك البرمجية، وتستعين بها لمعرفة التفاصيل الدقيقة عن لغات البرمجة. فأنت لست مضطرًا لحفظ كل شيء في لغة البرمجة، إذ حتى المبرمجون المختصون ذوو الخبرة يعودون إلى التوثيقات بين الفينة والأخرى أثناء عملهم. لننطلق الآن للتحدث عن مجالات البرمجة الأساسية وما اللغات والتقنيات المستعملة فيها. تطوير واجهات المستخدم يبدأ أغلبية المطورين مشوارهم من خلال تعلم تطوير واجهات المستخدم عند اتخاذ قرارهم لدخول مجال تطوير وبرمجة مواقع الويب، وذلك لسهولة اللغات المستعملة في هذا المجال. هدف هذا المجال هو تطوير صفحات الويب التي تعرض محتوى مختلف مواقع الويب، وهي الصفحات التي تراها عند زيارتك لموقع أكاديمية حسوب أو موسوعة حسوب أو مستقل أو أي موقع آخر. تتألف صفحة الويب من مجموعة من المكونات، وتُكتَب هذه المكونات باستخدام لغة HTML، وبعد كتابة البنية الهيكلية للصفحة سنأتي على تنسيقها باستخدام لغة CSS، وهي اللغة المستعملة لإضفاء شكل وهيئة على عناصر HTML. أي أن عناصر HTML تصف محتوى الصفحة (مثل الترويسات والقوائم والمحتوى الرئيسي والفقرات والروابط والصور والفيدوهات)، وقواعد CSS تُعرِّف كيف يجب أن تبدو هذه العناصر (سواءً من ناحية الألوان أو المساحات أو الخلفيات أو الخطوط أو خلاف ذلك). تأتي لغة JavaScript مكملةً لهما وتستعمل لإعطاء بعض عناصر الصفحة صفاتٍ تفاعلية، مثل شريط متحرك من الصور أو قوائم تظهر عند وقوع حدث معيّن …إلخ. هنالك تقنيات كثيرة تستعمل في تسهيل إنشاء الواجهات الأمامية وسنذكر بعضها: إطار Bootstrap لتسهيل تنسيق عناصر الصفحة. مكتبة jQuery لتسهيل التعامل مع عناصر الصفحة باستخدام JavaScript. مكتبة React JS: لتسهيل تطوير واجهات المستخدم بالاعتماد على مكونات قابلة لإعادة الاستخدام. لغة Sass لإنشاء ملفات CSS بسرعة وسلاسة. أدوات بناء مثل Webpack الذي يسهِّل تحويل الملفات المصدرية للتطبيق إلى النسخة النهائية التي ستعرَض للمستخدم. لتعلم تطوير واجهات المستخدم، ننصحك بالتسجيل في دورة تطوير واجهات المستخدم المقدمة من أكاديمية حسوب، والتي تحتوي على أكثر من 72 ساعة فيديو تتوزع على ثمانية مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب لتتعرف على توثيق لغات البرمجة المذكورة، وذلك للاطلاع على تفاصيل وأمثلة أكثر عن كل جزئية من الجزئيات المشروحة في دورة تطوير واجهات المستخدم. اللغات والتقنيات المستخدمة في تطوير واجهات المستخدم: HTML و CSS و JavaScript و Bootstrap و Sass و jQuery و Webpack. تطوير الواجهات الخلفية قد تتساءل: ماذا يعني تطوير الواجهات الخلفية (backend)؟ وما الفرق بينه وبين تطوير واجهات المستخدم (frontend)؟ الفرق بينهما هو أن الواجهات الخلفية هي البرمجيات التي تُنفَّذ على الخوادم وتجري عمليات عليها مثل التعامل مع قواعد البيانات والملفات والخدمات الخارجية، أما واجهات المستخدم فهي الصفحات التي تظهر على شاشة الزائر في متصفحه. سأطرح عليك الخيارات المتاحة أمامك للبدء في مجال تطوير الواجهات الخلفية، وجميع اللغات المذكورة هنا هي لغات ناجحة وقوية ولا يهم أي لغة تختار منها، المهم أن تتطلع على شيفرات بسيطة من كل لغة وتتخذ قرار تعلمها، واحذر من تضييع وقتك في التنقل بين لغات البرمجة والبحث عن أفضلها، فكلُ لغةٍ ممتازةٌ في مجالها. تعلم البرمجة باستخدام لغة PHP بعد تبيان الفرق بين واجهات المستخدم والواجهات الخلفية، يمكن القول بأن أشهر لغة لتطوير الواجهات الخلفية هي لغة PHP، وتتفوق على اللغات المنافسة لها أضعافًا مضاعفة. تعلم البرمجة بلغة PHP أمر سلس، فهي لغة سهلة التعلم وبسيطة الشكل، والمجتمع حولها كبير وتطويرها مستمر. هذه اللغة هي خيار استراتيجي لمن يريد الدخول إلى مجال تطوير الواجهات الخلفية. هنالك عدد من البرمجيات المكتوبة بلغة PHP مثل ووردبريس WordPress ودروبال Drupal وميدياويكي MediaWiki (التي تشغِّل ويكيبيديا وموسوعة حسوب) وغيرها الكثير؛ إضافةً إلى عددٍ كبير من إطارات العمل مثل Laravel و CodeIgniter و Symfony و CakePHP و Yii وغيرها، وهذا ما يدل على إمكانيات اللغة الكبيرة والمجتمع الكبير الذي يحيط بها. لتعلم تطوير الواجهات الخلفية باستخدام PHP، ننصحك بالتسجيل في دورة تطوير تطبيقات الويب باستخدام PHP المقدمة من أكاديمية حسوب، والتي تحتوي على 75 ساعة فيديو تتوزع على اثني عشر مسارًا تعليميًا تبدأ بأساسيات لغة البرمجة PHP للمبتدئين، مرورًا بشرح أمثلة عملية تطبيقية بالتفصيل، ووصولًا لتطوير التطبيقات باستخدام إطار العمل Laravel، وشرح تطوير ووردبريس. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة PHP وإطار العمل Laravel. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام PHP هي: PHP و Laravel وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم البرمجة باستخدام لغة روبي - Ruby إذا كنتَ تبحث عن لغةٍ أنيقة وسهلة الاستعمال فستجد ضالتك في لغة روبي Ruby فهي من أجمل اللغات وأسلسها كتابةً، وهي لغة برمجة عامة يمكن استخدامها لتطوير مختلف أنواع التطبيقات ومن ضمنها تطوير تطبيقات الويب. ذاع صيت روبي في تطوير الويب بعد نشر إطار العمل Ruby on Rails (يشار إليه اختصارًا "ريلز"). هنالك إطارات عمل أخرى مثل سيناترا Sinatra لكن يبقى ريلز أشهرها. لتعلم تطوير الواجهات الخلفية باستخدام روبي، ننصحك بالتسجيل في دورة تطوير تطبيقات الويب باستخدام روبي المقدمة من أكاديمية حسوب، والتي تحتوي على 20 ساعة فيديو تتوزع على أربعة مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا، وتشرح تطوير التطبيقات باستخدام إطار العمل ريلز. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة روبي وإطار العمل ريلز. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام روبي: روبي و ريلز وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم البرمجة باستخدام لغة جافا سكربت - JavaScript نعم! تستعمل JavaScript في تطوير الواجهات الخلفية أيضًا. الفضل يعود لبيئة Node.js التي تسمح للمطورين باستخدام JavaScript لكتابة برمجيات تعمل من جهة الخادم وذلك لتوليد صفحات ويب ديناميكية قبل إرسالها إلى المتصفح، وتستطيع Node.js التعامل مع الملفات وقواعد البيانات ومختلف أنظمة الشبكات وخدمات أنظمة التشغيل. هل يوجد أجمل من استخدام نفس اللغة لبرمجة الواجهات الأمامية لمواقع الويب والواجهات الخلفية؟ وكل ذلك باستخدام لغة سهلة التعلم والاستعمال ومدعومة دعمًا ممتازًا من المجتمع. تعلم لغة JavaScript لتطوير الواجهات الخلفية من خلال التسجيل في دورة تطوير التطبيقات باستخدام JavaScript المقدمة من أكاديمية حسوب، والتي تحتوي على 69ساعة فيديو تتوزع على أحد عشرمسارًا تعليميًّا تشرح أمثلة عملية تطبيقية شرحًا مفصلًا، وتشرح تطوير الواجهة الخلفية باستخدام Node.js. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة JavaScript وبيئة العمل Node.js. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام JavaScript: لغة JavaScript وبيئة Node.js وإطار العمل Express.js وقواعد البيانات (مثل MongoDB و MySQL و PostgreSQL وGraphQL وغيرها). تعلم البرمجة باستخدام لغة بايثون - Python لغة بايثون متعددة الاستعمالات، ويمكن عدّها على أنها أسهل لغة برمجة على الإطلاق، إذ تبدو شيفرتها البرمجية كأنها مقالة مكتوبة باللغة الإنكليزية. إذا أردتَ لغةً سهلةً ومدعومةً دعمًا ممتازًا ولها أطر عمل كثيرة فأنت تبحث عن لغة بايثون. الخيارات المتاحة أمامك هي إطار العمل جانغو (Django) وفلاسك (Flask) وغيرها، يمكنك تعلم لغة البرمجة بايثون لتطوير الواجهات الخلفية من خلال قراءة سلاسل المقالات عن تعلم بايثون في قسم البرمجة في أكاديمية حسوب، ثم الانتقال إلى تعلم إطار العمل جانغو أو فلاسك. يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة بايثون. تعلم لغة بايثون لتطوير الواجهات الخلفية من خلال التسجيل في دورة تطوير التطبيقات باستخدام Python المقدمة من أكاديمية حسوب، والتي تحتوي على 69 ساعة فيديو تتوزع على عشرةمسارات تعليمية تشرح أساسيات لغة بايثون للمبتدئين، ثم تطبق عمليًا بأمثلة واقعية، وتشرح إطار العمل جانغو Django وفلاسك Flask. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام بايثون: لغة بايثون وإطارات العمل المبنية عليها (مثل جانغو وفلاسك) وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم تطوير تطبيقات الجوال ازداد عدد تطبيقات الجوال لأنظمة أندرويد و iOS ازديادًا كبيرًا في الفترة الماضية، وأصبح لكل شركة أو خدمة تطبيق خاص بها يسهِّل على مستخدميها الوصول إلى الخدمات التي توفرها. النظامان الرئيسيان المسيطران على سوق الجوال حاليًا هما أندرويد ثم iOS. يمكن برمجة تطبيقات أندرويد بلغة Java أو Kotlin (أو غيرهما) وبرمجة تطبيقات iOS باستخدام Swift (وغيرها). ستكتشف أنَّ عليك تطوير تطبيقين منفصلين تمامًا، واحد لهواتف أندرويد وآخر لهواتف iOS، وذلك يسبب زيادةً في حجم العمل المطلوب وصعوبةً في إدارة التغييرات. بسبب ذلك، ظهر مفهوم "التطبيقات متعددة المنصات"، وهي تطبيقات تعمل على نظام أندرويد و iOS دون أي تعديلات، وذلك باستخدام تقنيات مشتركة وهي في الغالب تقنيات الويب. أي أصبح بإمكان مطوري الويب الاستفادة من معلوماتهم في تطوير تطبيقات الجوال باستخدام منصات مثل آيونيك Ionic. تسمح آيونيك Ionic للمبرمجين بالتعامل مع مختلف وظائف الجهاز باستخدام لغة JavaScript، مثل الوصول إلى الموقع الجغرافي، والتقاط صور بالكاميرا، والتعامل مع الملفات وخلاف ذلك. طوِّرت في الفترة الماضية تقنيات أخرى مبنية على JavaScript مثل React Native المبنية على مكتبة React.js والتي تسمح للمطورين بكتابة تطبيقات أصيلة باستخدام تقنيات الويب. تستطيع تعلم تطوير تطبيقات الجوال عبر Ionic وعبر React Native من خلال التسجيل في دورة تطوير التطبيقات باستخدام لغة JavaScript المقدمة من أكاديمية حسوب، والتي تحتوي على 69 ساعة فيديو تشرح أمثلة عملية تطبيقية شرحًا مفصلًا. وكالعادة يمكنك أثناء مشاهدتك للدورة أن تعود إلى موسوعة حسوب للاطلاع على التوثيقات اللازمة. اللغات والتقنيات المستخدمة في تطوير تطبيقات الجوال: Java و Swift و Kotlin و Ionic و React Native وغيرها. تطوير الألعاب تطوير الألعاب هو المجال الذي يحلم جميع مستخدمي الحاسوب بالدخول إليه. فالأغلبية تعرفوا على الحاسوب من خلال ألعاب الفيديو ومن ثم بدؤوا برحلة الاستكشاف عن البرمجة والتطوير. أغلب من يجيب عن تطوير الألعاب يقول "عليك بتعلم لغة C++" لكن دعني أفصِّل لك الأمر قليلًا. برمجة الألعاب تتطلب عملًا كثيرًا من فريق عمل كبير، مدعوم من شركة تجارية. من الصعب على مطوِّر وحيد أن ينشئ لعبة كاملة من الصفر دون فريق. تُطور أغلبية الألعاب باستخدام محرِّك ألعاب Game Engine والذي يسهِّل الأمر على المطورين ويتيح بيئة تطوير مناسبة للألعاب، ويتيح الميزات الأساسية لجميع الألعاب مثل التحكم بالكاميرا ونمذجة الشخصيات ثلاثية الأبعاد وتحريكها والأمور الفيزيائية الأخرى. هنالك عدد كبير من محركات تطوير الألعاب، ومن المرجح أنك شاهدت شعارها في الألعاب التي لعبتها من قبل، ومن أشهرها: Unreal Engine و Unity و Godot. يمكن التعامل مع هذه المحركات باستخدام عدِّة لغات، مثل C++ (وهي أشهرها)، وجافا (خصوصًا للألعاب على هواتف أندرويد) وحتى يمكن استخدام JavaScript في التعامل مع بعضها. تذكر أنّ الألعاب غير محدودة بتطبيقات سطح المكتب أو الهواتف، فهنالك ألعاب كثيرة تعمل على المتصفحات باستخدام تقنيات HTML5 و JavaScript. اللغات والتقنيات المستخدمة في تطوير الألعاب: C++ و Java و JavaScript ومحركات Unity و Unreal Engine و Godot. برمجة الذكاء الاصطناعي يُعد الذكاء الاصطناعي (AI) من المجالات الرائدة والمطلوبة بكثرة في سوق العمل اليوم، لاسيما بعد أن أصبحت تطبيقاته في متناول الجميع وبات يستخدم في العديد من المجالات فتعلمك لهذا المجال الرائد يعزز قدراتك كمبرمج ويساعد على تعزيز تطبيقاتك بقدرات الذكاء الاصطناعي القوية. يتفرع الذكاء الاصطناعي لعدة مجالات من أبرزها تعلم الآلة والتعلم العميق التي تُمكّن الحواسيب من التعلم من البيانات وتحسين أدائها بمرور الوقت دون الحاجة لبرمجة تقليدية. كي تتعلم برمجة الذكاء الاصطناعي تحتاج لامتلاك فهم جيد للبرمجة وتحليل البيانات ويمكنك استخدام لغات برمجة متعددة في هذا المجال أشهرها لغة البرمجة بايثون التي توفر الكثير من المكتبات والأطر المساعدة مثل تنسرفلو TensorFlow وكيراس Keras وبايتورش PyTorch وباندا Pandas. إذا كنت مهتمًا بتعلم هذا الاختصاص القوي فستوفر لك دورة الذكاء الاصطناعي من أكاديمية حسوب التي تحتوي على 58 ساعدة تدريبية ممتدة على ثمانية مسارات تدريبية شاملة كل ما تحتاج إليه للبدء بتطوير تطبيقات ذكاء اصطناعي قوية واكتساب مهارة في تحليل البيانات من الصفر ودون الحاجة لامتلاك معرفة مسبقة. اللغات والتقنيات المستخدمة في برمجة الذكاء الاصطناعي هي Python و MongoDB وPymongo و TensorFlow و Keras و Pandas والنماذج اللغوية الكبيرة LLMs المختلفة. تطوير الأنظمة المدمجة الأنظمة المدمجة هي أنظمة حاسوبية شبيهة بالحاسوب ولكنها لا تملك كل ميزات الحاسوب الذي تراه أمامك الآن. بعبارة أخرى، النظام المدمج هو حاسوب صغير مبرمج لأداء مهام محددة فقط ومدمج ضمن الجهاز أو البيئة المراد استخدامه فيها. أنت الآن محاط بالكثير من الأنظمة المدمجة الآن مثل جهاز مقياس مستوى المياه وجهاز التحكم بالتلفاز وجهاز إنذار الحريق وأجهزة المراقبة …إلخ. حتى إشارات المرور وتنظيم السير وألعاب الأطفال الآلية تصنَّف على أنها أنظمة مدمجة. هل سمعت أيضًا بمصطلح "إنترنت الأشياء"؟ إنترنت الأشياء هو نظام مدمج متصل بالإنترنت. نعم، بهذه البساطة! لابد الآن أن يتبادر إلى ذهنك الساعات والثلاجات والغسالات الذكية وطائرات الدرون وأنظمة المراقبة عن بعد وأنظمة البيوت الذكية، إذ كلها أمثلة على إنترنت الأشياء. كيفية برمجة الأنظمة المدمجة أشهر وأكثر لغة برمجة تستعمَل في برمجة الأنظمة المدمجة وإنترنت الأشياء هي لغة C (أي لغة سي) وكل اللغات المشتقة منها (مثل لغة أردوينو C). تُستعمَل لغة C++ كثيرًا في هذا المجال، إذ تعدُّ لغة ذات مستوى أعلى من لغة C لدعمها للبرمجة كائنية التوجه. أضف إلى ذلك أنه بدأ حديثًا استعمال لغة بايثون في برمجة تطبيقات الأنظمة المدمجة مع أنها لم ترتبط تقليديًّا بهذا المجال سابقًا. صحيح أنَّ لغة بايثون ليست بقوة لغة C و C++ في هذا المجال إلا أنها تستمد ميزاتها وفعاليتها من المكتبات الهائلة المتوافرة فيها. بعيدًا عن C وبايثون، تستعمل في مجال الأنظمة المدمجة أيضًا لغات أخرى تنضوي ضمن "لغات توصيف العتاد" (Hardware Description Languages)؛ لغتي VHDL و Verilog هما من أشهر لغات توصيف العتاد المستعملة في هذا المجال. تُستعمَل مثل هذه اللغات في برمجة "مصفوفة البوابات المنطقية القابلة للبرمجة" (FPGA أي Field Programmable Gate Array). أخيرًا، قد تجد بعض المراجع تشرح برمجة الأنظمة المدمجة بلغة أخرى تدعى "لغة التجميع" (Assembly Language) التي تصنف من اللغات منخفضة المستوى. يتطلب تعلم البرمجة باستخدام هذه اللغة فهمًا واسعًا بمعمارية وحدة التحكم المركزية والمعالج بالمجمل لأنها أقرب لغة يفهمها الحاسوب. الانتقال إلى هذه اللغة قد يكون في مستويات متقدمة من تعلمك لبرمجة الأنظمة المدمجة وتطبيقات إنترنت الأشياء. من ميزات البرمجة بهذه اللغة هي التحكم الواسع بالعتاد والمعالج الذي لا توفره لغات أخرى. يقال أن هذه اللغة صعبة بعض الشيء ومعقدة، ولكن لا أرى ذلك! قد يكون سبب قول ذلك هو أن لغة التجميع هي لغة منخفضة المستوى وأقرب شيء إلى لغة الآلة ولا يستطيع من يلقي نظرة على شيفرة مكتوبة فيها فهمها مطلقًا إن لم يعرفها. تطوير تطبيقات سطح المكتب مجال تطوير تطبيقات سطح المكتب كالمحيط الواسع؛ إن لم تملك بوصلة وتعرف إلى أين تريد الاتجاه، ستضيع فيه حتمًا. هنالك الكثير من أنظمة التشغيل أشهرها - وأكثرها سيطرةً على السوق حاليًا - هي: نظام التشغيل ويندوز، ولينكس، وماك (macOS)، ويملك كل نظام تشغيل تطبيقات مكتبية خاصة به. لذلك، يجب عليك أولًا -قبل الدخول إلى سوق برمجة تطبيقات سطح المكتب- تحديد نظام التشغيل المستهدف. أي يجب الإجابة على السؤال التالي: هل يستهدف تطبيقك نظام تشغيل محدد، أم تريد لتطبيقك أن يعمل على عدة أنظمة تشغيل في آن واحد؟! بعد تحديد نظام التشغيل المستهدف، اطلع على اللغات المفضل استعمالها في ذلك النظام لبرمجة تطبيقاته؛ فعلى سبيل المثال، اللغات C و C++ و C# و VB.NET هي الأكثر استعمالًا في برمجة تطبيقات نظام التشغيل ويندوز، واللغات C و C++ و Bash هي الأكثر استعمالًا في برمجة تطبيقات توزيعات نظام التشغيل لينكس. أمَّا نظام الشغيل ماك، فينفرد باستعمال لغة Objective-C. حسنًا، دعني أخبرك الحقيقة، كل لغة برمجة عامية الغرض يمكن استعمالها في برمجة التطبيقات، إذ أشهر اللغات التي تُدرَّس أكاديميًّا في هذا المجال هي لغة جافا (Java). لا يخفى على القارئ دخول لغة بايثون بقوة على هذا المجال نظرًا لامتلاكها الكثير من المكتبات الرائعة وسهولة صياغتها. دخلت مؤخرًا لغة جافاسكريبت على سوق برمجة تطبيقات سطح المكتب عبر إطار العمل Electron (إلكترون)، إذ توظف في هذا المجال تقنيات تطوير الويب (HTML و CSS و JavaScript …إلخ.). بدأ هذا الإطار ينتشر كالنار في الهشيم مما دفع شركات كبيرة لتطوير تطبيقات سطح المكتب الخاصة بها باستعمال هذا الإطار ومنها شركة Slack التي استعملت هذا الإطار لتطوير تطبيقها المكتبي. أعلم أنك الآن تشعر بالضياع من كثرة لغات البرمجة والتقنيات المستعملة في هذا المجال؛ معك حق، فقد أخبرتك بذلك منذ قليل. دخول هذا السوق يحتاج منك تحديد هدفك منه بالضبط. هل لديك فكرة تطبيق وتريد إنشاءه والربح منه؟ هل تريد العمل لدى شركة محددة؟ ما هي مواصفات التطبيق الذي تريد إنشاءه أو تريد العمل على تطويره؟ كل ذلك يلعب دورًا في تحديد لغة البرمجة الأنسب لك لتعلمها. في النهاية، إن تعلمت أساسيات البرمجة وأتقنت العمل على لغة برمجية محددة، سيسهل عليك الانتقال إلى لغة برمجة أخرى، إذ أغلب لغات البرمجة تشبه بعضها بعضًا من ناحية المفهوم والمضمون وتختلف بعض الشيء من ناحية الصياغة والشكل. لذلك، اطمئن من هذه الناحية. كيفية اختيار لغة البرمجة التي تناسبك يمكنك اختيار لغة البرمجة اعتمادًا على المجال الذي تحب العمل فيه، سألخص لك مسار التعلم لمختلف مجالات العمل: العمل كمطور ويب full-stack: يعني ذلك تعلم تطوير واجهات المستخدم أو الواجهات الأمامية، وتطوير الواجهات الخلفية. يمكن التخصص بمجال واحد من هذين المجالين فقط، إذ يُطلَب كثيرًا في سوق العمل مبرمجين متخصصين في واجهات المستخدم أو الواجهات الخلفية. العمل كمطور تطبيقات للهواتف المحمولة: يمكنك تعلم برمجة تطبيقات أندرويد أو iOS كلًا على حدة، أو استعمال تقنيات مثل كوردوفا لتطوير لكلا النظامين معًا. العمل كمطور تطبيقات سطح المكتب: يمكنك البدء بالتخصص في تطوير تطبيقات مكتبية لنظام تشغيل محدَّد (مثل نظام التشغيل ويندوز أو لينكس) عبر تعلم لغة البرمجة المستعملة في ذاك المجال (كما أشرنا إلى ذلك في قسم تطوير تطبيقات سطح المكتب)؛ خيار آخر هو تعلم اللغات والتقنيات التي تمكنك من تطوير تطبيقات عابرة للمنصات (تعمل على عدة أنظمة تشغيل) مثل استعمال إطار العمل Electron. العمل كمطور للأنظمة المدمجة والأنظمة الذكية: لغة C هي أساس هذا المجال، سواءً كنتَ تتعامل مع المتحكمات مباشرةً، أو تتعامل مع شريحة مثل أردوينو (والتي تمتلك لغةً مشتقةً من C). يساعدك هذا الفيديو على معرفة المعايير التي من خلالها ستتمكن من اختيار لغة البرمجة التي تتناسب مع تطلعاتك وأهدافك المستقبلية. نصائح لتعلم البرمجة مشوار تعلم البرمجة من الصفر طويل وشائق، ولكنه جميل ومسلٍ بذات الوقت، قد تصاب بالإحباط في بداية طريقك لكثرة الأمور التي عليك الإلمام بها، لذا جهزت إليك النصائح الآتية من تجربتي في البرمجة: حدد هدفك من تعلم لغة البرمجة وسوق العمل الذي تريد دخوله واجعله واقعيًا. بدون هدف، أبشرك بأنك ستتخلى عن فكرة تعلم البرمجة بعد حين. انتبه إلى أن يكون هدفك واقعيًا وقابلًا للقياس والتجزيء على مراحل. بدون ذلك، ستفشل من أول عقبة وتترك تعلم البرمجة. أعرف نفسك جيدًا ونقاط قوتك وضعفك. كلنا لديه نقاط قوة وضعف، ولكن المفلح من عمل على ترميم وتحسين نقاط ضعفه في المجال الذي يرغب بتعلمه. رشح دورة واحدة وكتابًا واحدًا وابدأ بقراءة الكتاب ومتابعة الدورة تدريجيًّا ثم انتقل بعد الانتهاء إلى دورة أخرى وكتاب آخر، إذ سيجنبك ذلك التشتت بين الدورات الكثيرة والكتب العديدة. الشيء الذي أفعله قبل بداية تعلم شيء جديد هو ترشيح قائمة من عدة كتب ودورات ثم ترتيب هذه الكتب والدورات بحسب جودتها ومدى بساطتها وتعقيدها. أرقم الكتب والدورات وأبدأ بالخيار الأول منها. أحدد الوقت التقريبي الذي يأخذه كل خيار لدراسته وأجدول الخيارات البقية على رزنامتي الخاصة. لا تأخذ العلم إلا ممن تثق بعلمه، فالكثير من المبتدئين يحاولون مساعدة غيرهم وقد يضعون معلومات مغلوطة دون قصد. طبق ما تعلمته مباشرة، وأنشئ أي شيء من كل أمر جديد تتعلمه حتى لو كان رسمة بسيطة أو شيفرة من عدة أسطر فقط. فرحة إنجاز شيء مما تعلمته تدفعك لتعلم المزيد والاستمرار في طلب العلم. نظم وقتك بورقة وقلم، حدد بداية كل أسبوع خطةً لسائره والتزم بتنفيذها. أخبر أصدقائك أن لديك التزامات وأمور مهمة عليك إنجازها. خصص وقتًا للاستراحة بالطبع ولا تنسَ نصيبك منها. في نهاية كل أسبوع، وازن مدى الإنجاز الذي حققته ومدى تطبيق الخطة التي وضعها وحاول أن تصل النسبة إلى 100%. أنصحك بقراءة ومتابعة استراتيجيات تنظيم الوقت ورفع الإنتاجية. تمرس على حل المشكلات وتحدى نفسك باستمرار وتابع المسابقات البرمجية واشترك بها إن استطعت، أنصحك بقراءة مقالة حل المشكلات وأهميتها في احتراف البرمجة بعد هذه المقالة. لا تنسَ أن تكافئ نفسك في كل مرة تنهي فيها كتابًا أو تكمل العمل على مشروع. لا تنسَ حظك من الاستراحة، لأن طريق البرمجة قد يكون له بداية ولكن النهاية بعيدة ومتعبة -مثله مثل أي مجال آخر-. في النهاية أرجو لك كل التوفيق في مشوارك البرمجي. وأرجو منك أن تشاركنا تجربتك في تعلم البرمجة، لعل غيرك يستفيد منها. اقرأ أيضًا كيف تتعلم البرمجة المدخل الشامل إلى تعلم علوم الحاسوب المرجع الشامل إلى تعلم لغة بايثون تعرف على أعلى تخصصات البرمجة أجرا تعلم لغة HTML قواعد البرمجة ما هي فوائد تعلم البرمجة؟ أسهل لغات البرمجة أهمية البرمجة1 نقطة
-
اريد اسم برنامج يكون بسيط لعمل اديتينج للفيديو بس يكون بيلغى background noise .. بس يكون برنامج بسيط انا لا اريد كامتازيا ستوديو1 نقطة
-
1 نقطة
-
1 نقطة
-
مرحبا مختار، أظن هذه الخدمة يجب أن تكون مقدمة من قبل شركة الاتصالات. لايوجد أي برنامج يستطيع تحويل الرسائل النصية المستقبلة من رقم هاتف إلى رقم آخر. (طالما الsim خارج الهاتف لايوجد طريقة لنقل البيانات منه) حاول التواصل مع الدعم الفني لشركة الاتصالات لديك بالتوفيق1 نقطة
-
حسنا لقد حلت المشكلة شكرا لك1 نقطة
-
1 نقطة
-
مرحباً بك، عندما بحثت عن الأمر وجدت أنه متعلق بإصدار c++ المُستخدم في جهازك بالإضافة إلى الcompiler فالإصدار أقل من 11 سيُسبب لك بعض الأخطاء في حين إذا كان 11 أو أكثر لن تحدث لك أي أخطاء وسيشتغل البرنامج بشكل جيد، إذا اردت تشغيل البرنامج على جهازك بالإصدار الموجود حالياً بإمكانك عمل بعض التغييرات على كل من الميثود Print التي تكتب في الملف بالشكل التالي: void Print(string file) { ofstream f; f.open (file.c_str()); // تغيير هذا السطر f << "The Name is : " << name << endl; f << "Total is : " << Total << endl; f << "TVs: " << endl; for(int i=0;i<2;i++){ f << "TVs[" << i << "] = (Number = " << TVs[i].getNo() << ", Price = " << TVs[i].getPrice() << ")" << endl; } f.close(); } بالإضافة إلى أن الدالة to_string لن تعمل على الإصدار أيضا فيجب تغيير الطريقة إلى شكل آخر و هذه الدالة main: int main() { Invoice* x[2]; //customer y; for(int i=0; i<2;i++){ char name[20]; char filename[20]; sprintf(name, "Invoice %d", i); x[i] = new Invoice(name); sprintf(filename, "output%d.txt", i); x[i]->Print(filename); x[i]->getMin(); cout<<"********************************"<<endl; } return 0; } و هذا الكود ككل: #include <iostream> #include <fstream> using namespace std; /*---------------------------------------------------*/ class Customer{ protected: string name; public: Customer(string name = "Unknown"){ this->name = name; } string getName(){ return name; } void setName(string name = "Unknown"){ this->name = name; } }; /*---------------------------------------------------*/ class TV{ private: int no; public: float price; TV(){ cout << "The TV Number is : " << endl; cin >> no; cout << "The TV Price is : " << endl; cin >> price; } float getPrice(){ return price; } int getNo(){ return no; } void setPrice(float p){ price = p; } }; /*---------------------------------------------------*/ class Invoice : protected Customer{ private: float Total = 0; public: TV TVs[2]; void ReadPrice(){ for(int i=0;i<2;i++){ cout << "TVs[" << i << "] = (Number = " << TVs[i].getNo() << ", Price = " << TVs[i].getPrice() << ")" << endl; } }; int getTotal(){ return Total; } Invoice(string name) : Customer(name){ for(int i=0;i<2;i++){ Total += TVs[i].price; } } void Print(){ cout << "The Name is : " << name << endl; cout << "Total is : " << Total << endl; cout << "TVs: " << endl; this->ReadPrice(); } void Print(string file) { ofstream f; f.open (file.c_str()); f << "The Name is : " << name << endl; f << "Total is : " << Total << endl; f << "TVs: " << endl; for(int i=0;i<2;i++){ f << "TVs[" << i << "] = (Number = " << TVs[i].getNo() << ", Price = " << TVs[i].getPrice() << ")" << endl; } f.close(); } int getMin(){ float min; min = TVs[0].price; for(int i=0 ;i < 2; i++){ if (TVs[i].price < min) min = TVs[i].price; } cout<<"The minimum :" << min <<endl; return min; } }; /*---------------------------------------------------*/ int main() { Invoice* x[2]; //customer y; for(int i=0; i<2;i++){ char name[20]; char filename[20]; sprintf(name, "Invoice %d", i); x[i] = new Invoice(name); sprintf(filename, "output%d.txt", i); x[i]->Print(filename); x[i]->getMin(); cout<<"********************************"<<endl; } return 0; } جربت كلتا الطريقتين على جهازي و اشتغل بشكل جيد، إذا لم ينجح معك بإمكانك تحديث إصدار c++ بالإضافة إلى الcompiler إلى آخر نُسخة بإمكانك إستخدام برنامج code blocks و تحميل الملف الذي يأتي معه compiler ثم تنصيبه على جهازك. بالتوفيق.1 نقطة
-
أنصحك بطريقة يمكن استخدامها في الطباعة سوف تعين مقاسات الصفحة تلقائياً مع مقاس الورقة المستخدمة بالطريقة التي تحددها وسوف تعمل توافق تلقائياً بواسطة برنامج adobe acrobat وهو من أفضل البرامج للطباعة وبه العديد من المميزات يمكنك من خلاله تحويل المستند إلى pdf او استخراج المستند pdf من قبل وفتح البرنامج وظبط هذه الإعدادات التالية : اختر File > Print. أنقر فوق Size، وحدد أحد الخيارات التالية: Fit: تعديل حجم الصفحات الصغيرة لأعلى والصفحات الكبيرة لأسفل لملائمة الورق. Shrink Oversize Pages : تغيير حجم الصفحات الكبيرة فقط لملائمة الورق وطباعة الصفحات الصغيرة كما هي. Custom scale : يغير حجم الصفحات بالنسبة المئوية التي تحددها. يمكنك البحث حول هذا البرنامج وأكتشافه وتعمق فيه سوف تستفيد كثيراً من مميزاته في الطباعة .1 نقطة
-
تُصنَّف التَعْليمات البرمجية (statements) بأي لغة برمجة -ومنها الجافا Java- إلى تَعْليمات بسيطة (simple) وأخرى مُركَّبة (compound). تُعدّ التَعْليمات البسيطة -مثل تَعْليمَة الإِسْناد (assignment) وتَعْليمَة اِسْتدعاء البرامج الفرعية (subroutine call)- اللَبِنة الأساسية لأيّ برنامج. في المُقابل، تتكون التَعْليمات المُركَّبة -مثل تَعْليمَة حَلْقة التَكْرار while وتَعْليمَة التَفْرِيع الشَّرطيّة if- من عدد من التَعْليمات البسيطة، وتُعرَف باسم بُنَى التحكُّم (control structures)؛ نظرًا لأنها تَتَحكَّم بترتيب تَّنْفيذ التَعْليمات. سنتناول بُنَى التحكُّم (control structures) المُدَعَّمة بلغة الجافا Java بشئٍ من التفصيل خلال الأقسام الخمسة التالية، بحيث نبدأ في هذا القسم بتَعْليمتي الحَلْقة while و do..while. سنتَعرَّض، خلال ذلك، لكثير من الأمثلة البرمجية التي تُوظِّف بُنَى التحكُّم تلك، كما أننا وبينما نقوم بذلك، سنُطبِّق التقنيات المُتَّبَعة بتصميم الخوارزميات (algorithms)، والتي قد تَعرَّضنا لها بالفصل السابق. تَعْليمَة while تُكتَب تَعْليمَة حَلْقة التَكْرار (loop) while -والتي قد تَعرَّضت لها مُسْبقًا بالقسم ٣.١- بالصياغة التالية: while ( <boolean-expression> ) <statement> لا يُشترَط أن تتكوَّن التعليمة -المُشار إليها بالأعلى، والمعروفة باسم مَتْن حَلْقة التَكْرار (loop body)- من تَعْليمَة واحدة فقط، فبالطبع يُمكنها أن تكون كُتليّة البِنْية (block statement)، بحيث تَتضمَّن عدة تَعْليمَات مُحَاطة بزوج من الأقواس. يَتكرَّر تَّنْفيذ مَتْن الحَلْقة (body of the loop) طالما كان التعبير -المُشار إليه بالأعلى- مُتحقِّقًا. تتكون تلك العبارة من تَعبير منطقي (boolean expression)، وتُعرَف باسم الشَّرْط الاستمراري (continuation condition) لحَلْقة التَكْرار أو باسم اختبار حَلْقة التَكْرار. نحتاج الآن لإِيضاح عدة نقاط بشيء من التفصيل. أولًا، ماذا يَحدُث إذا لم يَتحقَّق شَّرْط حَلْقة التَكْرار ولو لمرة واحدة على الأقل، بمعنى أنه لم يَكُن متحقِّقًا قبل التَّنْفيذ الأوَّليّ لمَتْن الحَلْقة؟ في هذه الحالة، لا يُنفَّذ مَتْن الحَلْقة (body) نهائيًا، وهو ما يعني أن مَتْن حَلْقة while يُمكِن أن يُنفَّذ أيّ عدد من المرات بما في ذلك الصفر. ثانيًا، ماذا لو كان شَّرْط حَلْقة التَكْرار مُتحقِّقًا، لكن وبينما يُنفَّذ مَتْن الحَلْقة لم يَعُدْ الشَّرْط كذلك؟ هل يَتوقَف تَّنْفيذ الحَلْقة بمجرد حُدُوث ذلك؟ ببساطة لا؛ حيث يَستمِر الحاسوب بتَّنْفيذ مَتْن الحَلْقة بالكامل حتى يَصِل إلى نهايته، وعِندها فقط يَقفز عائدًا إلى بداية حَلْقة التَكْرار لفَحْص شَّرْطها (continuation condition) مُجددًا، ومِنْ ثَمَّ، يَستطيع في تلك اللحظة فقط إِيقاف الحَلْقة وليس قبل ذلك. لنفْحَص إِحدى المشكلات التي تُحلّ باِستخدَام حَلْقة التَكْرار while: حِساب قيمة متوسط (average) مجموعة من الأعداد الصحيحة الموجبة (positive integers)، والتي يَتم إِدْخالها من قِبَل المُستخدِم. تُحسَب قيمة المتوسط (average) عمومًا بحِساب حاصل مجموع الأعداد ثم قِسمته على عَدَدها. سيَطلب البرنامج من المُستخدِم إِدْخال عدد صحيح (integer) وحيد في المرة، وسيَحتفِظ دومًا بقيمة المجموع الكلي للأعداد الصحيحة المُدْخَلة حتى اللحظة الراهنة، وكذلك عَدَدها، كما سيُبقي هذه القيم مُحْدَثة مع كل عملية إِدْخال. هاك خوارزمية البرنامج (algorithm) مَكتوبة بأسلوب الشيفرة الوهمية (pseudocode): Let sum = 0 Let count = 0 // طالما لا يوجد المزيد من الأعداد الصحيحة للمعالجة while there are more integers to process: // اقرأ عدد صحيح Read an integer // أضف قيمته إلى المتغير sum Add it to the sum // أزد قيمة العداد Count it // اقسم المجموع sum على count لحساب قيمة المتوسط Divide sum by count to get the average // اطبع قيمة المتوسط Print out the average لكن كيف سنتحقَّق فعليًا من شَّرْط الحَلْقة بالأعلى "طالما لا يوجد المزيد من الأعداد الصحيحة للمعالجة"؟ أحد الحلول هو إِبلاغ المُستخدِم بأن يُدخِل القيمة صفر بعد أن يَنتهِي من إِدْخال جميع البيانات الفعليَّة. ستَنجح تلك الطريقة نظرًا لأننا نَفترِض أن البيانات الفعليَّة المُدْخَلة لابُدّ وأن تَكون من الأعداد الصحيحة الموجبة، وهو ما يَعني أن الصفر يُعدّ قيمة غيْر صالحة من الأساس. لاحِظ أن الصفر هنا ليس قيمة بحدّ ذاتِها يَنبغي تَضْمِينها مع الأعداد المطلوب حِساب مُتوسطها، وإِنما هي فقط مطلوبة كعلامة للإشارة إلى نهاية البيانات الفعليَّة. يُطلق أحيانًا على قِيَم البيانات المُستخدَمة بطريقة مُشابهة اسم بَيَان البداية/النهاية (sentinel value). سنُعدِّل الآن اختبار حَلْقة التَكْرار while ليُصبِح "طالما العدد الصحيح المُدخل لا يساوي الصفر". ستُواجهنا مشكلة آخرى، عندما يُنفَّذ اختبار حَلْقة التَكْرار (loop) لأول مرة، أي قَبْل التَّنْفيذ الأوَّليّ لمَتْن الحَلْقة، لن يكون هناك عدد صحيح قد قُرأ بَعْد، بل ليس هناك أيّ بيانات قد قُرِأت أساسًا، مما يَعني أنه لا يوجد "عدد صحيح مُدخل". ولهذا، يُصبِح اختبار الحَلْقة "طالما العدد الصحيح المُدخل لا يساوي الصفر" -ضِمْن هذا السياق- غَيْر ذي مَعنى. لذلك يَنبغي القيام بأمر ما قَبل البدء بتَّنْفيذ حَلْقة التَكْرار while للتأكد من صلاحية الاختبار حتى مع أول تَّنْفيذ له. تُعرَف هذه العملية باسم التهيئة المبدئية (priming the loop) لحَلْقة التَكْرار، وفي هذه الحالة بالتحديد، يُمكننا ببساطة تهيئة الحَلْقة عن طريق قراءة أول عدد صحيح مُدْخَل قَبل بدء تَّنْفيذ حَلْقة التَكْرار. هاك الخوارزمية (algorithm) المُعدَّلة: Let sum = 0 Let count = 0 // اقرأ عدد صحيح Read an integer // طالما العدد الصحيح المُدخل لا يساوي الصفر while the integer is not zero: // أضف قيمته إلى المتغير sum Add the integer to the sum // أزد قيمة العداد Count it // اقرأ عدد صحيح Read an integer // اقسم المجموع sum على count لحساب قيمة المتوسط Divide sum by count to get the average // اطبع قيمة المتوسط Print out the average لاحِظ إعادة ترتيب تَعْليمَات مَتْن حَلْقة التَكْرار (loop body)؛ لأنه لمّا أصْبَحت أول محاولة لقراءة عدد صحيح (integer) تَحدُث قَبْل بداية حَلْقة التَكْرار (loop)، كان لابُدّ لمَتْن الحَلْقة من أن يبدأ أولًا بمُعالجة ذلك العدد. ثُمَّ بنهاية المَتْن، سيُحاول الحاسوب قراءة عدد صحيح جديد، ليَقفِز بَعْدها عائدًا لبداية الحَلْقة ليَختبِر العدد الجديد المَقروء للتو. عندما يَقرأ الحاسوب قيمة بَيَان النهاية (sentinel value)، ستَتوقَف الحَلْقة، ومِنْ ثَمَّ لن تُعالَج تلك القيمة، كما أنها لن تُضاف إلى حاصل المجموع sum، أو هذه هى الطريقة التي ينبغي أن تَعمَل بها الخوارزمية على الأقل؛ لأن قيمة بَيَان النهاية ليست جزءً من البيانات الفعليَّة كما ذَكَرنا مُسْبَّقًا. لا تَعمَل الخوارزمية الأصلية طِبقًا لذلك بطريقة صحيحة -بفَرْض إِمكانية تَّنْفيذها بدون إِجراء التهيئة المبدئية (priming) بطريقة ما-؛ لأنها ستَحسِب حاصل مجموع جميع الأعداد الصحيحة وكذلك عَدَدها (count) بما يتَضمَّن قيمة بَيَان النهاية (sentinel value). لمّا كانت تلك القيمة -المُتفق عليها- تُساوي الصفر، فإن حاصل المجموع سيظِلّ لحسن الحظ صحيحًا، ولكن في المقابل سيكون عَدَدها (count) خطأ بفارق واحد. يُطلَق على الأخطاء المُشابهة اسم الأخطاء بفارق الواحد (off-by-one errors)، وهي واحدة من أكثر الأخطاء شيوعًا. اِتضح أن العَدّ أصعب مما قد يبدو عليه. سنُحوِّل الآن الخورازمية إلى برنامج كامل، نُلاحِظ أولًا أنه لا يُمكِن اِستخدَام التَعْليمَة average = sum/count; لحساب قيمة المتوسط (average)؛ لأنه لمّا كانت قيمة كُلًا من المُتَغيِّرين sum و count من النوع العددي الصحيح int، فإن حاصل قِسمة الأول على الثاني sum/count ستكون أيضًا من النوع العددي الصحيح int، في حين ينبغي للمتوسط أن يكون عددًا حقيقيًا (real number). لقد واجهنا هذه المشكلة مِن قَبْل، ويَتلخَّص حَلّها بضرورة تَحْوِيل واحد من القيمتين إلى النوع double؛ وذلك لإجبار الحاسوب على حِسَاب قيمة حاصل القِسمة (quotient) كعَدَد حقيقي (real number). يُمكِن القيام بذلك عن طريق إجراء عملية التَحْوِيل بين الأنواع (type-casting) على أحد المُتَغيِّرين على الأقل، بحيث يُحوَّل إلى النوع double عن طريق اِستخدَام (double)sum. ولهذا يَحسِب البرنامج قيمة المتوسط باستخدام التعبير average = ((double)sum) / count;. يَتوفَّر حلّ آخر، وهو التَّصْريح (declaration) عن المُتَغيِّر sum بحيث يكون أساسًا من النوع double. حُلَّت مشكلة آخرى بالبرنامج التالي، وهي أنه في حالة إِدْخال المُستخدِم القيمة صفر كأول قيمة مُدْخَل، فلن تتوفَّر أيّ بيانات للمُعالجة، ولهذا يُمكننا اختبار حُدُوث تلك الحالة بفَحْص ما إذا كانت قيمة المُتَغيِّر count ما تزال مُساوية للصفر حتى بَعْد انتهاء حَلْقة التَكْرار while. قد تبدو هذه المشكلة ثانوية، ولكن لابُدّ أن يُغطي أيّ مبرمج مُنتبِه جميع الحالات المُمكنة. انظر شيفرة البرنامج بالكامل: import textio.TextIO; public class ComputeAverage { public static void main(String[] args) { int inputNumber; // إحدى القيم المدخلة من قبل المستخدم int sum; // حاصل مجموع الأعداد الصحيحة الموجبة int count; // عدد الأعداد الصحيحة double average; // قيمة متوسط الأعداد الصحيحة الموجبة // هيئ قيمة كلا من متغير المجموع والعداد sum = 0; count = 0; // اقرأ مدخل من المستخدم لمعالجته System.out.print("Enter your first positive integer: "); inputNumber = TextIO.getlnInt(); while (inputNumber != 0) { // أضف قيمة المتغير للمجموع الحالي sum += inputNumber; // أزد قيمة العداد بمقدار الواحد count++; System.out.print("Enter your next positive integer, or 0 to end: "); inputNumber = TextIO.getlnInt(); } // اعرض النتائج if (count == 0) { System.out.println("You didn't enter any data!"); } else { average = ((double)sum) / count; System.out.println(); System.out.println("You entered " + count + " positive integers."); System.out.printf("Their average is %1.3f.\n", average); } } // نهاية البرنامج main } // نهاية الصنف ComputeAverage تعليمة do..while تَفحْص تَعْليمَة حَلْقة التَكْرار while الشَّرْط الاستمراري لحَلْقة التَكْرار (continuation condition) ببداية الحَلْقة، ولكن أحيانًا ما يكون من الأنسب فَحْصه بنهاية الحَلْقة لا بدايتها، وهذا في الواقع ما تُوفِّره تَعْليمَة do..while. تَتشابه صياغة (syntax) تَعْليمَة الحَلْقة do..while مع تَعْليمَة while تمامًا باستثناء بعض التَغْيِيرات الطفيفة، حيث تُنْقَل كلمة while مصحوبة مع الشَّرْط إلى نهاية الحَلْقة، في حين تُضاف كلمة do في بداية الحَلْقة بدلًا منها. تُكتَب تَعْليمَة حَلْقة التَكْرار do..while بالصياغة التالية: do <statement> while ( <boolean-expression> ); إذا كانت التعليمة كُتلَة (block) بذاتها، تُصاغ التَعْليمَة كالتالي: do { <statements> } while ( <boolean-expression> ); لاحِظ وجود الفاصلة المَنقوطة (semicolon) ; بنهاية التَعْليمَة do..while. تُعدّ هذه الفاصلة جزءً أساسيًا من التَعْليمَة do..while، مثلما تُعدّ الفاصلة بنهاية أي تَعْليمَة أخرى -كالإِسْناد (assignment) أو التَّصْريح (declaration)- جزءً أساسيًا منها. ولهذا سيؤدي حَذْف الفاصلة المنقوطة ;، في هذه الحالة، إلى التَسبُّب بحُدوث خطأ في بناء الجملة (syntax error). ينبغي عمومًا أن تنتهي أيّ تَعْليمَة برمجية -بلغة الجافا Java- إِمّا بفاصلة منقوطة ; أو بقوس مُغلِق {. عندما يُنفَّذ الحاسوب تَعْليمَة حَلْقة التَكْرار do..while، فإنه يبدأ أولًا بتَّنْفيذ مَتْنها (loop body)-التَعْليمَة أو مجموعة التَعْليمَات بداخل الحَلْقة-، ثم يَحسِب قيمة التعبير المنطقي (boolean expression)، فإذا كانت مُساوية للقيمة المنطقية true، فإنه يَقفِز عَائدًا إلى بداية الحَلْقة ويُعيد تَّنْفيذ مَتْنها، لتستمر العملية بَعْدها بنفس الطريقة. أما إذا كانت قيمة التعبير المنطقي مُساوية للقيمة المنطقية false، فإنه يَتوقَف عن تَّنْفيذ الحَلْقة، وينتقل إلى الجزء التالي من الشيفرة. لاحظ أنه لمّا كان شَّرْط الحَلْقة لا يُفحْص إلا بنهاية الحَلْقة، فإن مَتْن حَلْقة التَكْرار do..while دائمًا ما يُنفَّذ مرة واحدة على الأقل. على سبيل المثال، اُنظر الشيفرة الوهمية (pseudocode) التالية لبرنامج لعبة. ستجد أن استخدام تَعْليمَة الحَلْقة do..while أكثر مُلائمة في هذه الحالة؛ لأنها ستَضمَن أن المُستخدِم قد لعِبَ مباراة واحدة على الأقل. علاوة على ذلك، فإن شَّرْط حَلْقة التَكْرار بالأسفل لن يكون له أيّ مَعنى في حالة تَّنْفيذه في بداية الحَلْقة. do { // العب مباراة Play a Game // اسأل المستخدم إذا ما كان يريد اللعب مرة آخرى Ask user if he wants to play another game // اقرأ رد المستخدم Read the user's response // أعد التكرار طالما كان رد المستخدم هو نعم } while ( the user's response is yes ); سنُحوِّل الآن هذه الشيفرة الوهمية إلى لغة الجافا. بدايةً ولكي نتجنب الخَوْض في تفاصيل ليست ذا أهمية، سنفترض أن لدينا صَنْف (class) يَحمِل اسم Checkers. أحد أعضاء (member) هذا الصَنْف هو البرنامج الفرعي الساكن (static subroutine) playGame(). يُغلِّف هذا البرنامج الفرعي التفاصيل الخاصة باللعبة، ويُمثِل استدعائه إجراء مباراة دَّامَا (checkers) واحدة مع المُستخدِم. يُمكننا الآن اِستخدَام تَعْليمَة استدعاء البرنامج الفرعي (subroutine call) الساكن Checkers.playGame(); كبديل عن سَطْر الشيفرة "العب مباراة". سنَستخدِم الصنف TextIO لتَلَقِّي رد المُستخدِم على سؤال من النوع نعم أم لا؛ حيث يُوفِّر هذا النوع الدالة (function) TextIO.getlnBoolean()، والتي تَسمَح للمُستخدِم بإِدْخال إحدى القيمتين "Yes/No" ضِمْن عدة ردود أُخرى صالحة، بحيث يؤول الرد "Yes" إلى القيمة المنطقية true، بينما يؤول الرد "No" إلى القيمة المنطقية false. سنحتاج الآن إلى مُتَغيِّر لتخزين رد المُستخدِم، والذي سيكون بطبيعة الحال من النوع المنطقي (boolean). يُمكن كتابة الخوارزمية كالتالي: // تساوي True في حالة رغب المستخدم باللعب مجددًا boolean wantsToContinue; do { Checkers.playGame(); System.out.print("Do you want to play again? "); wantsToContinue = TextIO.getlnBoolean(); } while (wantsToContinue == true); وفقًا لشَّرْط حَلْقة التَكْرار بالأعلى، فإنه عندما تَتَساوى قيمة المُتَغيِّر المنطقي wantsToContinue مع القيمة المنطقية false، سيكون ذلك إشارة (signal) إلى ضرورة تَوقُف حَلْقة التَكْرار. يُطلَق عادةً على المُتَغيِّرات المنطقية (boolean variables) المُستخدَمة بهذه الطريقة اسم راية (flag) أو مُتَغيِّر راية -هي متغيرات تُضبَط (set) بمكان ما بالشيفرة لتُفحَص قيمتها بمكان آخر-. بالمناسبة، عادةً ما يَسخَر بعض المبرمجين -ربما قد يَصفهم البعض بالمُتَحذلِقين- من شَّرْط حَلْقة التَكْرار while (wantsToContinue == true)؛ وذلك لأنه مُكافئ تمامًا للشَّرْط while (wantsToContinue)؛ فاختبار ما إذا كان التعبير wantsToContinue == true يُعيد القيمة المنطقية true لا يَختلف نهائيًا عن اختبار ما إذا كان المُتَغيِّر wantsToContinue يَحمِل القيمة true ويُعيدها. بالمثل، -وبفَرْض أن لدينا مُتَغيِّر اسمه flag من النوع المنطقي (boolean variable)- يُكافئ التعبير flag == false، والذي يُعدّ أقل فَجاجة من التعبير السابق، تمامًا التعبير !flag، حيث الحرف ! هو عَامِل النفي المنطقي (negation operator). يُفضَّل عمومًا كتابة while (!flag) بدلًا من while (flag == false)، وبالمثل، كتابة if (!flag) بدلًا من if (flag == false). على الرغم من أن اِستخدَام تَعْليمَة حَلْقة التَكْرار do..while أحيانًا ما يكون أكثر ملائمة في بعض المسائل، فعمومًا لا يَجعل وجود نوعين مختلفين من تَعْليمَة الحَلْقة (loops) لغة البرمجة أكثر قوة؛ فبالنهاية، أي مشكلة تُحلّ بإحداهما، ستجد أنه من الممكن حلّها بالآخرى. في الواقع، بفرض أن العبارة تُمثل أي كُتلَة شيفرة، فإن التالي: do { <doSomething> } while ( <boolean-expression> ); يُكافئ تمامًا: <doSomething> while ( <boolean-expression> ) { <doSomething> } بالمثل: while ( <boolean-expression> ) { <doSomething> } يُكافئ: if ( <boolean-expression> ) { do { <doSomething> } while ( <boolean-expression> ); } بدون أي تَغْيِير بمَقصِد البرنامج نهائيًا. تعليمتي break و continue تَسمَح لك صيغتي حَلْقتي التَكْرار while و do..while باختبار الشَّرْط الاستمراري (continuation condition) إِمّا ببداية الحَلْقة أو بنهايتها على الترتيب. ولكن أحيانًا سترغب في إجراء عملية اختبار شَّرْط أثناء تَّنْفيذ الحَلْقة، أيّ داخل مَتْن الحَلْقة نفسها، أو حتى قد ترغب بإجراء أكثر من عملية اختبار بأماكن مختلفة داخل نفس الحَلْقة. تُستخدَم تَعْليمَة break بلغة الجافا لإيقاف تَّنْفيذ أيّ حَلْقة تَكْرار (loop)، والخروج منها، وذلك بمجرد استدعائها بأيّ مكان داخل الحَلْقة. تُكتَب كالتالي: break; عندما يُنفِّذ الحاسوب تَعْليمَة break داخل حَلْقة (loop)، فإنه سيَقفِز مُباشرةً خارج الحَلْقة، ويُنفِّذ الشيفرة التالية بالبرنامج والموجودة أسفل الحَلْقة. انظر المثال التالي: while (true) { // يبدو وكأنها ستنفذ بشكل لا نهائي System.out.print("Enter a positive number: "); N = TextIO.getlnInt(); // إما أن تكون قيمة المدخل سليمة أو اقفز خارج الحلقة if (N > 0) break; System.out.println("Your answer must be > 0."); } // اكمل هنا بعد break في المثال بالأعلى، إذا أَدْخَل المُستخدِم عدد أكبر من الصفر، سيتحقَّق الشَّرْط، وستُنفَّذ تَعْليمَة break، ولهذا سيَقفِز الحاسوب مباشرة خارج الحَلْقة إلى ما بَعْدها. في المقابل، إذا لم يتحقَّق الشَّرْط، ستُطبَع السِلسِلة النصية "Your answer must be > 0"، ثم سيَقفِز الحاسوب عَائدًا إلى بداية الحَلْقة ليَقرأ مُدْخَل جديد. قد يبدو السَطْر الأول من الشيفرة بالأعلى while (true) غَيْر مألوف نوعًا ما، ولكنه في الواقع سليم تمامًا. يُسمَح عمومًا لأيّ تعبير منطقي (boolean-valued expression) بأن يكون شَّرْطًا لحَلْقة التَكْرار، أيّ لابُدّ فقط أن تؤول قيمته النهائية إلى قيمة منطقية؛ ليَفحْصها الحاسوب لمعرفة ما إذا كانت مُساوية للقيمة true أم للقيمة false. تُعدّ القيمة المُجرَّدة (literal) true تعبيرًا منطقيًا، والذي يؤول دائمًا إلى القيمة المنطقية true، ولذلك يُمكِن اِستخدَامها كشَّرْط للحَلْقة. يُستخدَم الشَّرْط while (true) لكتابة حَلْقة لا نهائية (infinite loop) أو حَلْقة يُفْترَض الخروج منها باِستخدَام تَعْليمَة break. يُسمَح باِستخدَام حَلْقات التَكْرار المُتداخِلة (nested loops)، بمَعنى وجود تَعْليمَة حَلْقة تَكْرار داخل أُخرى، ولذلك لابُدّ لنا من فهم طريقة عَمَل تَعْليمَة break ضِمْن هذا السياق. كقاعدة عامة، تُوقِف تَعْليمَة break حَلْقة التَكْرار الأقرب لها فقط، بمَعنى أنها إذا اُستخدِمت بداخل الحَلْقة الداخلية (inner loop)، فإنها ستُوقِف فقط تلك الحَلْقة، لا الحَلْقة الخارجية (outer loop) التي تَشَمَلها. أما إذا أردت إيقاف تَّنْفيذ الحلقة الخارجية، يُمكِنك اِستخدَام ما يُعرَف باسم تَعْليمَة break المُعنوَنة (labeled break). تَسمَح تَعْليمَة break المُعنوَنة بتحديد صريح للحَلْقة المطلوب الخروج منها. اِستخدَام هذه التَعْليمَة المُعنوَنة غَيْر شائع، ولذلك سنمر عليها سريعًا. تَعمَل العناوين (labels) كالتالي: يُمكِنك ببساطة عَنوَنة أيّ حَلْقة تَكْرار بوَضْع مُعرَّف (identifier) مَتبوع بنقطتان رأسيتان : بمقدمة الحَلْقة. فمثلًا، يمكن عَنوَنة حَلْقة تَكْرار while باِستخدَام mainloop: while.... تستطيع الآن اِستخدَام تَعْليمَة break المُعنوَنة بأيّ مكان داخل هذه الحَلْقة عن طريق اِستخدَام الصيغة break mainloop;، وذلك بهدف الخروج من هذه الحَلْقة تحديدًا. على سبيل المثال، تَفحْص الشيفرة بالأسفل ما إذا كانت السِلسِلتين النصيتين s1 و s2 تحتويان على حرف مُشتَرَك. في حالة تَحقُّق الشَّرْط، ستُسْنَد القيمة المنطقية false إلى مُتَغيِّر الراية nothingInCommon، ثُمَّ تُسْتَدعى تَعْليمَة break المُعنوَنة لإيقاف المعالجة عند تلك النقطة: boolean nothingInCommon; // افترض أن السلسلتين لا يشتركان بأي حرف nothingInCommon = true; // متغيرات حلقتي التكرار والتي ستأخذ قيم حروف السلسلتين int i,j; i = 0; bigloop: while (i < s1.length()) { j = 0; while (j < s2.length()) { // إذا كان هناك حرفًا مشتركا if (s1.charAt(i) == s2.charAt(j)) { // اضبط قيمة المتغير إلى القيمة false nothingInCommon = false; // اخرج من الحلقتين break bigloop; } j++; // Go on to the next char in s2. } i++; //Go on to the next char in s1. } تُعدّ تَعْليمَة continue مرتبطة نوعًا ما بتَعْليمَة break، لكنها أقل اِستخدَامًا منها. تتَخَطَّى تَعْليمَة continue الجزء المُتبقي من التَكْرار الحالي (current iteration) فقط، أي أنها لا تتَسبَّب بالقفز خارج الحَلْقة تمامًا إلى ما بَعْدها كتَعْليمَة break؛ وإنما تَقفِز عائدة إلى بداية نفس الحَلْقة لتَّنْفيذ التَكْرار التالي (next iteration) -بالطبع إذا كان الشَّرْط الاستمراري (continuation condition) لحَلْقة التَكْرار ما زال مُتحقِّقًا. عندما تُستخدَم تَعْليمَة continue داخل حَلْقة مُتداخِلة (nested loop)، فإنها، مثل تَعْليمَة break، تُجرِي هذه العملية على حَلْقة التَكْرار الأقرب لها فقط، أيّ الحَلْقة الداخلية (inner nested loop). وبالمثل، تتوفَّر تَعْليمَة continue المُعنوَنة (labeled continue)، وذلك لتخصيص حَلْقة التَكْرار المراد إجراء عملية continue عليها. يمكن اِستخدَام تَعْليمَتي break و continue داخل جميع حَلْقات التَكْرار مثل while، و do..while، و for. سنتحدث عن الأخيرة تفصيليًا بالقسم التالي. يُمكن أيضًا اِستخدَام تَعْليمَة break للخروج من تَعْليمَة switch، وهو ما سنتناوله بالقسم ٣.٦. يُسمَح أيضًا باِستخدَامها داخل تَعْليمَة التَفْرِيع الشَّرْطيّة if إذا كانت تَعْليمَة التَفْرِيع موجودة إِمّا ضِمْن حَلْقة تَكْراريّة أو ضِمْن تَعْليمَة switch، ولكن عندها لا يكون المقصود هو الخروج من تَعْليمَة التَفْرِيع if، وإنما الخروج من التَعْليمَة التي تَشتَملها، أي من تَعْليمَة الحَلْقة أو من تَعْليمَة switch. بالمثل، يُمكِن اِستخدَام تَعْليمَة continue داخل تَعْليمَة if بنفس الطريقة، وتتبِّع نفس القواعد بالأعلى. ترجمة -بتصرّف- للقسم Section 3: The while and do..while Statements من فصل Chapter 3: Programming in the Small II: Control من كتاب Introduction to Programming Using Java.1 نقطة
-
ملاحظة: يفترض هذا الدليل أنك تستخدم مصرّف Babel، كما يتطلّب استخدام إعدادات babel-preset-airbnb المسبقة أو ما يماثلها. ويفترض أيضًا أنّك ثبّت ترقيعات متعدّدة (Polyfills/Shims)، عبر airbnb-browser-shims أو ما يماثلها. أنواع البيانات الأنواع البدائية (Primitives) عندما تتعامل مع نوع بدائي فأنت تعمل مباشرةً على قيمته. سلاسل المحارف string، الأعداد number، القيم المنطقية boolean، القيمة المعدومة null، القيمة غير المعرَّفة undefined، الرموز symbol، الأعداد الصحيحة الكبيرة bigint. const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9 لا يمكن ترقيع النوعين symbol وbigint بدقّة، لذا ينبغي ألّا تُستخدَم عند استهداف المتصفحات/البيئات التي لا تدعمها تلقائيّا. الأنواع المركبة (Complex) عند التعامل مع نوع مركّب فأنت تعمل على مرجعٍ لقيمته. الكائنات object، المصفوفات array، الدوال function. const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9 المراجع References استخدم const لجميع مراجعك. وتجنب استخدام var. استخدم قاعدتي prefer-const وno-const-assign في ESlint. لماذا؟ لأنّ هذا سيضمن لك ألّا تعيد تعيين مراجعك، وهو ما يمكن أن يؤدي إلى أخطاء، ويُصعّب فهم الشفرة البرمجية. // سيئ var a = 1; var b = 2; // جيّد const a = 1; const b = 2; إن كنت مضطرًّا لإعادة تعيين المراجع، استخدم let بدلاً من var. استخدم قاعدة no-var في ESlint. لماذا؟ لأن نطاق تعريف let محدود بالكتلة البرمجية (Block-scoped) وليس محدودًا داخل الدالة (Function-scoped) كما هو الحال مع var. // سيئ var count = 1; if (true) { count += 1; } // جيد، استخدم let. let count = 1; if (true) { count += 1; } تذكر أن نطاق كل من let و constمحدود بالكتلة. // لا توجد المتغيّرات المصرَّح عنها ب let وconst إلّا بداخل الكتل المُصرَّح فيها { let a = 1; const b = 1; } console.log(a); // خطأ في المرجع ReferenceError console.log(b); // خطأ في المرجع ReferenceError الكائنات Objects استخدم صياغة تصنيف النوع (Literal syntax) لإنشاء الكائنات. استخدم قاعدة no-new-object في ESLint. // سيئ const item = new Object(); // جيّد const item = {}; استخدم أسماء محسوبة للخاصيّات عند إنشاء كائنات بأسماء خاصيّات ديناميكية. لماذا؟ لأن ذلك سيسمح لك بتعريف جميع خاصيّات الكائن في مكان واحد. function getKey(k) { return `a key named ${k}`; } // سيئ const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // جيّد const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, }; استخدم أسلوب التعريف المختصر لتوابع الكائن. (قاعدة object-shorthand في ESLint). // سيئ const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // جيّد const atom = { value: 1, addValue(value) { return atom.value + value; }, }; استخدم التعريف المختصر لقيمة الخاصية (قاعدة object-shorthand في ESLint). لماذا؟ لأنه أقصر وأوضح. const lukeSkywalker = 'Luke Skywalker'; // سيئ const obj = { lukeSkywalker: lukeSkywalker, }; // جيّد const obj = { lukeSkywalker, }; اجمع الخاصيّات المُختصرة في بداية التصريح بالكائن (Object declaration). لماذا؟ لأنّ هذه الطريقة تسهّل معرفة أي الخاصيّات تستخدم الاختصار. const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker'; // سيئ const obj = { episodeOne: 1, twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, }; // جيّد const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, }; لا تضع بين علامات التنصيص إلّا الخاصيّات التي لها أسماء غير صالحة. لماذا؟ بشكل عام، لأنّه أسهل للقراءة ويُحسّن وضوح الكود، كما أنّه يسهُل استخدامه من قبل محركات الجافا سكريبت. // سيئ const سيئ = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // جيّد const جيّد = { foo: 3, bar: 4, 'data-blah': 5, }; لا تستدع توابع Object.prototype ، مثل hasOwnProperty، propertyIsEnumerable، و isPrototypeOf، لا تستدعها مباشرة. استخدم قاعدة no-prototype-builtins في ESLint. لماذا؟ لأنّ خاصيّات الكائن قد تغطّي تلك التوابع - انظر مثلًا إلى {hasOwnProperty: false} – علاوة على أن الكائن قد يكون معدومًا (Object.create(null)). // سيئ console.log(object.hasOwnProperty(key)); // جيّد console.log(Object.prototype.hasOwnProperty.call(object, key)); // أفضل const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. /* أو */ import has from 'has'; // https://www.npmjs.com/package/has // ... console.log(has.call(object, key)); يفضل تطبيق عامل التمديد (Spread operator) على الكائن بدلًا من استخدام التابع Object.assign إن كنت تريد النسخ السطحي (Shallow-copy) للكائنات. أو يمكنك استخدام عامل الاستناد (Rest operator) للحصول على كائن جديد مع حذف خصائص معينة. // سيئ جدًّا const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // يتسبّب في التعديل على الكائن `original` delete copy.a; // الأمر نفسه هنا // سيئ const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } // جيّد const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 } المصفوفات Arrays استخدم صياغة تصنيف النوع (Literal syntax) لإنشاء المصفوفة (قاعدة no-array-constructor في ESLint). // سيئ const items = new Array(); // جيّد const items = []; استخدم Array#push بدلًا من الإسناد المباشر لإضافة عناصر إلى المصفوفة. const someStack = []; // سيئ someStack[someStack.length] = 'abracadabra'; // جيّد someStack.push('abracadabra'); استخدم تمديد المصفوفات ... (Array spreads) لنسخ المصفوفات. // سيئ const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // جيّد const itemsCopy = [...items]; استخدم عامل التمديد ... بدلًا من التابع Array.from لتحويل كائن مُكرَّر (Iterable) إلى مصفوفة. const foo = document.querySelectorAll('.foo'); // جيّد const nodes = Array.from(foo); // أفضل const nodes = [...foo]; استخدم التابع Array.from بدلًا من عامل التمديد ... لتحويل كائن شبيه بالمصفوفات إلى مصفوفة. const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; // سيئ const arr = Array.prototype.slice.call(arrLike); // جيّد const arr = Array.from(arrLike); استخدم التابع Array.from بدلًا من عامل التمديد ... لتطبيق الدالة map على الكائنات المُكرّرة، بهدف تجنّب خلق مصفوفة مؤقتة. // سيئ const baz = [...foo].map(bar); // جيّد const baz = Array.from(foo, bar); استخدم التعليمة return في رد نداء توابع المصفوفات (Method callbacks). لا ضير في حذف التعليمة return إن كان متن الدالة يتكون من تعليمة واحدة من دون آثار جانبية. استخدم قاعدة array-callback-return في ESLint. // جيّد [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // جيّد [1, 2, 3].map(x => x + 1); // سيئ -عدم وجود قيمة مُرجَعة يعني أن "acc" يصبح غير معرّف بعد عملية التكرار الأولى [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memo[index] = flatten; }); // جيّد [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memo[index] = flatten; return flatten; }); // سيئ inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } else { return false; } }); // جيّد inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } return false; }); استخدم سطرًا جديدًا بعد معقوفة الفتح وقبل معقوفة إغلاق المصفوفة إذا كان المصفوفة متعددة الأسطر. // سيئ const arr = [ [0, 1], [2, 3], [4, 5], ]; const objectInArray = [{ id: 1, }, { id: 2, }]; const numberInArray = [ 1, 2, ]; // جيّد const arr = [[0, 1], [2, 3], [4, 5]]; const objectInArray = [ { id: 1, }, { id: 2, }, ]; const numberInArray = [ 1, 2, ]; التفكيك Destructuring استخدم تفكيك الكائن عند التعامل مع عدة خاصيّات للكائن. لماذا؟ التفكيك يُعفيك من الحاجة إلى إنشاء مراجع مؤقتة لتلك الخصائص. // سيئ function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`; } // جيّد function getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}`; } // أفضل function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`; } استخدم تفكيك المصفوفات. const arr = [1, 2, 3, 4]; // سيئ const first = arr[0]; const second = arr[1]; // جيّد const [first, second] = arr; استخدم تفكيك الكائن لأجل إرجاع أكثر من قيمة، وليس تفكيك المصفوفات. لماذا؟ يمكنك إضافة خاصيات جديدة مع الوقت وتغيير الترتيب دون مشاكل. // سيئ function processInput(input) { // then a miracle occurs return [left, right, top, bottom]; } // تحتاج إلى أخذ ترتيب البيانات المُرجَعة في الحسبان const [left, __, top] = processInput(input); // جيّد function processInput(input) { // then a miracle occurs return { left, right, top, bottom }; } // تختار البيانات التي تحتاجها فقط const { left, top } = processInput(input); السلاسل النصية Strings استخدم علامات التنصيص المفردة '' لتحديد السلاسل النصيّة (القاعدة quotes في ESLint). // سيئ const name = "Capt. Janeway"; // سيئ - يجب أن تحتوي القوالب مصنَّفة النوع على حشو (Interpolation) أو أسطر جديدة. const name = `Capt. Janeway`; // جيّد const name = 'Capt. Janeway'; ينبغي ألّا تُكتَب النصوص التي يتجاوز طولها 100 حرف على أسطر متعددة باستخدام ضمّ النصوص (Concatenation). لماذا؟ النصوص التي فيها أخطاء تكون مزعجةً وتجعل الكود أقل قابلية للبحث. // سيئ const errorMessage = 'This is a super long error that was thrown because \ of Batman. When you stop to think about how Batman had anything to do \ with this, you would get nowhere \ fast.'; // سيئ const errorMessage = 'This is a super long error that was thrown because ' + 'of Batman. When you stop to think about how Batman had anything to do ' + 'with this, you would get nowhere fast.'; // جيّد const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; عند بناء النصوص برمجيًّا، استخدم القوالب النصيّة بدلًا من ضمّ النصوص (قاعدة prefer-template template-curly-spacing في ESLint). لماذا؟ تجعل القوالب النصيّة الشفرة أكثر مقروئية وإيجازًا، مع أسطر جديدة مناسبة وميزات حشو النصوص. // سيئ function sayHi(name) { return 'How are you, ' + name + '?'; } // سيئ function sayHi(name) { return ['How are you, ', name, '?'].join(); } // سيئ function sayHi(name) { return `How are you, ${ name }?`; } // جيّد function sayHi(name) { return `How are you, ${name}?`; } لا تطبّق أبدًا ()eval على النصوص، لأنها تتسبّب في الكثير من الثغرات. استخدم قاعدة no-eval في ESLint. تجنب تخليص الأحرف (Escape characters) في النصوص قدر الإمكان. لماذا؟ الخط المائل العكسي يضر بالمقروئية، وبالتالي يجب ألًا يُستخدم إلا عند الضرورة. // سيئ const foo = '\'this\' \i\s \"quoted\"'; // جيّد const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`; الدوال استخدم الدوال العبارات (Function expressions) المُسماة بدلًا من التصريح بالدوال (قاعدة func-style في ESLint). لماذا؟ يضع محرَك جافاسكريبت الدوال المُصرَّح بها في بداية النطاق (تُعرَف هذه العملية بالرفع Hoist)، وبالتالي يمكن استخدام الدالة قبل تعريفها في الملف. يعني ذلك أنه سيكون من السهل الإحالة إلى الدالة قبل أن تُعرَّف في الملف، وهو ما يضر المقروئية وقابلية الصيانة. إذا وجدت أن تعريف دالة ما كبير أو معقد حدّ الإرباك، فربما حان الوقت لوضعه في وحدة خاصة به! لا تنس أن تُسمّي العبارة صراحة، بغض النظر عما إذا كان الاسم مستنتجًا من المتغير الحاوي (كما هو الحال غالبًا في المتصفحات الحديثة أو عند استخدام مصرّفات مثل Babel) أم لا. تلغي هذه الطريقة أي افتراضات حول مكدس النداء إلى الخطأ Error’s call stack // سيئ function foo() { // ... } // سيئ const foo = function () { // ... }; // جيّد // اسم العبارة الدالة مغاير لاسم الاستدعاء الذي يحيل إلى المتغيّر const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... }; ضع الدوال العبارات فورية الاستدعاء (Immediately-invoked Function Expression، أو IIFE اختصارًا)، ضعها بين قوسين. استخدم قاعدة wrap-iife في ESLint. لماذا؟ الدوال العباراة فورية الاستدعاء هي وحدة منفردة، لذلك وضعها، هي وعبارة استدعائها، بين قوسين يجعل الشفرة واضحة. وإن كان من المستبعد جدّا أن تحتاج الدوال فورية الاستدعاء (IIFE) في مشاريع تكثر من استخدام الوحدات الوظيفية (Modules). // immediately-invoked function expression (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }()); لا تصرّح أبدًا بالدوال في كتلة غير خاصة بالدوال (مثل if، وwhile، …إلخ). أسند الدالة إلى متغير بدلًا من ذلك. تسمح المتصفحات بالتصريح بالدالة في تلك الكتل، إلّا أنها ستفسرها بطرق مختلفة، وهو أمر لا يحبه المبرمجون. استخدم قاعدة no-loop-func في ESLint. ملحوظة: يعرّف معيار ECMA-262 الكتلة (Block) بأنها قائمة من التعليمات البرمجية (Statements). والتصريح بدالة (Function decleration) ليس تعليمة // سيئ if (currentUser) { function test() { console.log('Nope.'); } } // جيّد let test; if (currentUser) { test = () => { console.log('Yup.'); }; } لا تسمّي معاملًا بالاسم arguments. لأنه سيأخذ الأسبقية على الكائن arguments الذي يُحدد تلقائيّا في نطاق كل دالة. // سيئ function foo(name, options, arguments) { // ... } // جيّد function foo(name, options, args) { // ... } لا تستخدم أبدًا المعامل arguments، واستخدم بدلًا منه عامل التمديد (...). لماذا؟ عامل التمديد واضح في تحديد الوسائط التي تريد سحبها. بالإضافة إلى ذلك، وسائط عامل التمديد هي مصفوفة حقيقة، وليست شبيهة بالمصفوفة مثل الكائن arguments. استخدم قاعدة prefer-rest-params في ESLint. // سيئ function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // جيّد function concatenateAll(...args) { return args.join(''); } استخدم الصيغة الافتراضية للمعاملات بدلًا من التعديل على وسائط الدالة. // سيئ جدّا function handleThings(opts) { // لا! يجب ألّا نعدّل على وسائط الدالة. // أمر سيئ آخر: إذا كانت قيمة الوسيط opts تساوي القيمة المنطقية false فسيُسنَد كائن إلى الوسيط opts، وهو ما قد تريده إلّا أنه يتسبب في ثغرات تصعب ملاحظتها opts = opts || {}; // ... } // سيئ أيضا function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // جيّد function handleThings(opts = {}) { // ... } تجنب الآثار الجانبية في المعاملات الافتراضية. لماذا؟ لأنّها مربكة. var b = 1; // سيئ function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3 ضع دائمًا المعاملات الافتراضية في الأخير. // سيئ function handleThings(opts = {}, name) { // ... } // جيّد function handleThings(name, opts = {}) { // ... } لا تستخدم أبدًا منشئ الدوال (Function constructor) لإنشاء دالة جديدة. استخدم قاعدة no-new-func في ESLint. لماذا؟ إنشاء دالة بهذه الطريقة يتضمّن تقييم نص كما تفعل ()eval، وهو ما يفتح نقاط ضعف. // سيئ var add = new Function('a', 'b', 'return a + b'); // سيئ كذلك var subtract = Function('a', 'b', 'return a - b'); ضع مسافات في توقيع الدالة. استخدم قاعدتي space-before-function-paren وspace-before-blocks في ESLint. لماذا؟ الاتساق أمر جيد، كما لن تكون مضطرًا لإضافة أو إزالة مسافة عند إضافة أو إزالة اسم. // سيئ const f = function(){}; const g = function (){}; const h = function() {}; // جيّد const x = function () {}; const y = function a() {}; لا تعدّل أبدًا على المعاملات. استخدم قاعدة no-param-reassign في ESLint. لماذا؟ يمكن للتعديل على الكائنات التي مُرُرت كمعاملات أن يتسبب في آثار جانبية غير مرغوب فيها في المستدعي الأصلي. // سيئ function f1(obj) { obj.key = 1; } // جيّد function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; } لا تُعد إسناد المعاملات. استخدم قاعدة no-param-reassign في ESLint. لماذا؟ إعادة إسناد المعاملات يمكن أن يؤدي إلى سلوك غير متوقع، خصوصًا عند التعامل مع الكائن arguments. كما يمكن أن يسبب مشاكل في الأداء، خصوصا في V8. // سيئ function f1(a) { a = 1; // ... } function f2(a) { if (!a) { a = 1; } // ... } // جيّد function f3(a) { const b = a || 1; // ... } function f4(a = 1) { // ... } من الأفضل استخدام استخدام عامل التمديد (...) لاستدعاء الدوال متغيرة عدد الوسائط (Variadic functions). استخدم قاعدة prefer-spread في ESLint. لماذا؟ لأنها أوضح، فلست مضطرًّا لتجهيز السياق، كما لا يمكنك أن تجمع بسهولة new مع apply. // سيئ const x = [1, 2, 3, 4, 5]; console.log.apply(console, x); // جيّد const x = [1, 2, 3, 4, 5]; console.log(...x); // سيئ new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // جيّد new Date(...[2016, 8, 5]); الدوال التي لها توقيعات أو استدعاءات متعددة الأسطر، ينبغي أن تكون مسافاتها البادئة تمامًا مثل كل القوائم متعددة الأسطر الأخرى في هذا الدليل، بمعنى أن كل عنصر في سطر، مع فاصلة زائدة بعد العنصر الأخير. // سيئ function foo(bar, baz, quux) { // ... } // جيّد function foo( bar, baz, quux, ) { // ... } // سيئ console.log(foo, bar, baz); // جيّد console.log( foo, bar, baz, ); الدوال السهمية Arrow Functions استخدم صيغة الدالة السهمية عندما تكون مضطرًّا لاستخدام دالة مجهولة (Anonymous function)، مثلًا عند تمرير ردّ نداء (Callback) على السطر. استخدم قاعدتي prefer-arrow-callback وarrow-spacing في ESlint. لماذا؟ لأنها تخلق نسخة من الدالة تُنفَّذ في السياق this، وهو عادةً ما ترغب فيه، كما أنها أكثر إيجازا. متى تتخلّى عنها؟ إذا كانت لديك دالة معقدة، فيمكنك نقل وظيفتها إلى دالة عبارة مُسمّاة. // سيئ [1, 2, 3].map(function (x) { const y = x + 1; return x * y; }); // جيّد [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); إن كان متن الدالة يتكون من تعليمة واحدة تُرجِع تعبيرًا دون آثار جانبية، فاحذف القوسين المعقوصيْن ({}) واستخدم الإرجاع الضمني (بدون التعليمة return). خلافًا لذلك، أبق على الأقواس المعقوصة واستخدم التعليمة return. استعن بقاعدتي arrow-parens و arrow-body-style في ESLint. لماذا؟ تسهيل قراءة الشفرة عند استخدام دوال بالتسلسل. // سيئ [1, 2, 3].map(number => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // جيّد [1, 2, 3].map(number => `A string containing the ${number}.`); // جيّد [1, 2, 3].map((number) => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`; }); // جيّد [1, 2, 3].map((number, index) => ({ [index]: number, })); // لا يوجد إرجاع ضمني لكن توجد آثار جانبية function foo(callback) { const val = callback(); if (val === true) { // افعل شيئًا هنا إذا كان رد النداء إيجابيا } } let bool = false; // سيئ foo(() => bool = true); // جيّد foo(() => { bool = true; }); في حال امتد التعبير عبر عدة أسطر، ضعه بين قوسين لمقروئية أكبر. لماذا؟ لتوضيح أين تبدأ وأين تنتهي الدالة. // سيئ ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // جيّد ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) )); أضف دائمًا أقواسًا حول الوسائط من أجل الوضوح والتناسق. استعن بقاعدة arrow-parens في ESlint. لماذا؟ تقليل الأخطاء عند إضافة وسائط أو حذفها. // سيّئ [1, 2, 3].map(x => x * x); // جيّد [1, 2, 3].map((x) => x * x); // سيّئ [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // جيّد [1, 2, 3].map((number) => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // سيّئ [1, 2, 3].map(x => { const y = x + 1; return x * y; }); // جيّد [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); تجنب الخلط بين صياغة الدوال السهمية (=>) وبين عوامل المقارنة (<= , >=). استعن بقاعدة no-confusing-arrow. // سيئ const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; // سيئ const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; // جيّد const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); // جيّد const itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height > 256 ? largeSize : smallSize; }; افرض موقع متن الدوال السهميّة عن طريق الإرجاع الضمني (Implicit return). استعن بقاعدة implicit-arrow-linebreak. // سيّئ (foo) => bar; (foo) => (bar); // جيّد (foo) => bar; (foo) => (bar); (foo) => ( bar ) الأصناف والمُنشِئات Classes & Constructors استخدم دائمًا الكلمة المفتاحية class. وتجنب التعامل مع الخاصيّة prototype مباشرة. لماذا؟ العبارة class أكثر إيجازا ووضوحا. // سيئ function Queue(contents = []) { this.queue = [...contents]; } Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value; }; // جيّد class Queue { constructor(contents = []) { this.queue = [...contents]; } pop() { const value = this.queue[0]; this.queue.splice(0, 1); return value; } } استخدم الكلمة المفتاحية extends للتوارث بين الأصناف. لماذا؟ لأنها وسيلة مدمجة لوارثة الوظائف من النموذج الأولي دون التسبب بمشاكل عند استخدام العامل instanceof. // سيئ const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); } inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; }; // جيّد class PeekableQueue extends Queue { peek() { return this.queue[0]; } } يمكن للتوابع أن تُرجع الكائن this للمساعدة استخدام التوابع بالتسلسل. // سيئ Jedi.prototype.jump = function () { this.jumping = true; return true; }; Jedi.prototype.setHeight = function (height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // جيّد class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump() .setHeight(20); لا بأس في تخصيص التابع ()toString، لكن تأكد من أنه يعمل بسلاسة ولا تتسبب في أي آثار جانبية. class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } } توجد منشئات افتراضية للأصناف يُلجَا إليها إنْ لم يُحدّد منشئ سلفا. لذا، فلا حاجة لمنشئات فارغة أو منشئات تقتصر على الإنابة عن الصنف الأب. استعن بقاعدة no-useless-constructor في ESLint. // سيئ class Jedi { constructor() {} getName() { return this.name; } } // سيئ class Rey extends Jedi { constructor(...args) { super(...args); } } // جيّد class Rey extends Jedi { constructor(...args) { super(...args); this.name = 'Rey'; } } تجنب تكرار عناصر الصنف. استعن بقاعدة no-dupe-class-members في ESLint. لماذا؟ في حال التصريح المكرر لعنصر من عناصر الصنف، فالقيمة الأخيرة فقط هي التي ستُعتمَد. وجود التكرار يعني بالضرورة وجود علّة في الشفرة البرمجية. // سيئ class Foo { bar() { return 1; } bar() { return 2; } } // جيّد class Foo { bar() { return 1; } } // جيّد class Foo { bar() { return 2; } } يجب أن تستخدم توابع الصنف الكائن this أو تُدرَج في تابع ثابت (Static) إلّا إذا استدعت مكتبة خارجية أو إطار عمل استخدام توابع غير ثابتة. كون التابع مرتبطًا بنظير كائن (Instance) يجب أن يشير إلى أنه يتصرف بسلوك مختلف حسب خاصيّات الكائن المستقبل. استعن بالقاعدة class-methods-use-this. // سيّئ class Foo { bar() { console.log('bar'); } } // جيّد، استخدام this class Foo { bar() { console.log(this.bar); } } // جيّد، المنشئ مستثنى من القاعدة constructor() { // ... } } // جيّد، يفترض ألا تستخدم التوابع الثابتة الكائن this class Foo { static bar() { console.log('bar'); } } الوحدات Modules استخدم دائمًا الكلمتيْن المفتاحيتيْن (import/export) لاستيراد أو تصدير الوحدات، بدلًا من الطرق الأخرى غير القياسية. يمكنك دائمًا تصريف الوحدات المفضلة لديك إلى شفرة جافاسكريبت (Transpile). لماذا؟ الوحدات هي المستقبل، دعونا نُدشن المستقبل منذ الآن. // سيئ const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // مقبول import AirbnbStyleGuide from './AirbnbStyleGuide'; export default AirbnbStyleGuide.es6; // أفضل import { es6 } from './AirbnbStyleGuide'; export default es6; لا تعتمد على محارف البدل (Wild card) في استيراد الوحدات (الاستيراد بالجملة). لماذا؟ لتتأكد من أنّ لديك تصديرًا افتراضيّا واحدا. // سيئ import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // جيّد import AirbnbStyleGuide from './AirbnbStyleGuide'; لا تُصدّر مباشرةً من الاستيراد. لماذا؟ على الرغم من أن الكتابة في سطر واحد تكون أوجز، إلّا أن وجود طريقة واضحة واحدة للاستيراد وأخرى للتصدير يجعل الأمور متسقة. // سيئ // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // جيّد // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6; استورد من مسار معيّن في مكان واحد فقط. استعن بقاعدة no-duplicate-imports. لماذا؟ وجود عدة أسطر تستورد من المسار نفسه يمكن أن يجعل الشفرة أقل قابلية للصيانة. // سيئ import foo from 'foo'; // … some other imports … // import { named1, named2 } from 'foo'; // جيّد import foo, { named1, named2 } from 'foo'; // جيّد import foo, { named1, named2, } from 'foo'; لا تُصدر الارتباطات المتحوّلة (Mutable bindings). استعن بقاعدة no-duplicate-imports. لماذا؟ يُفضّل عمومًا تجنب العناصر المتحولة، ولكن على وجه الخصوص عند تصدير الارتباطات المتحولة. على الرغم من أن هذا الأسلوب قد يكون ضروريّا في حالات خاصة، إلّا أنّ القاعدة الأساسية هي ألّا تُصدرَ إلا المراجع الثابتة. // سيئ let foo = 3; export { foo }; // جيّد const foo = 3; export { foo }; في الوحدات التي فيها تصدير واحد فقط، يفضل استخدام التصدير الافتراضي بدلًا من التصدير المسمى named export. استعن بقاعدة import/prefer-default-export. لماذا؟ لتشجيع استخدام الملفات التي لا تصدر إلًا شيئًا واحدًا فقط، لأن ذلك أفضل وأسهل للقراءة والصيانة. // سيئ export function foo() {} // جيّد export default function foo() {} ضع كل عبارات import فوق عبارات الاستيراد الأخرى. استعن بقاعدة import/first في ESLint. لماذا؟ بما أن محرّك جافاسكريبت يرفع تعليمات import إلى أعلى النطاق (Hoisted)، فوضعها كلها في الجزء العلوي يمنع أي سلوك غير متوقع. // سيئ import foo from 'foo'; foo.init(); import bar from 'bar'; // جيّد import foo from 'foo'; import bar from 'bar'; foo.init(); يجب أن توضع مسافة بادئة قبل عناصر الاستيراد ذي الأسطر المتعدّدة، مثله مثل المصفوفات متعددة الأسطر والكائنات مصنّفة النوع (Object literals). استعن بالقاعدة object-curly-newline. لماذا؟ تتبع الأقواس المعقوصة قواهد المسافات البادئة ذاتها التي تتبعها كتل الأقواس المعقوصة الأخرى في هذا الدليل. الأمر نفسه ينطبق على الفواصل الزائدة (Trailing commas). // سيئ import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // جيّد import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path'; لا تسمح بصيغة Webpack للتحميل في تعليمات استيراد الوحدات. استعن بالقاعدة import/no-webpack-loader-syntax. لماذا؟ لأن استخدام صيغة Webpack في الاستيراد تجعل الشفرة معتمدة على محزّم (Bundler). من الأفضل استخدام صيغة Webpack في الملف webpack.config.js. // سيئ import fooSass from 'css!sass!foo.scss'; import barCss from 'style!css!bar.css'; // جيّد import fooSass from 'foo.scss'; import barCss from 'bar.css'; لا تُضمّن امتداد ملفات جافاسكريبت في الاستيراد. استعن بالقاعدة import/extensions. لماذا؟ لأن تضمين الامتدادات يصعّب إعادة كتابة الشفرة، ويضع لدى جميع العملاء تفاصيل غير مناسبة عن الوحدة التي تستوردها. // سيّئ import foo from './foo.js'; import bar from './bar.jsx'; import baz from './baz/index.jsx'; // جيّد import foo from './foo'; import bar from './bar'; import baz from './baz'; المُكرّرات والمولّدات لا تستخدم المكررات (Iterators). من الأفضل استخدام دوال الدرجات العليا لجافاسكريبت بدلًا من الحلقات مثلfor-in أو for-of. لماذا؟ هذا يتماشى مع قاعدة انعدام التحول (Immutable rule) التي ننتهجها. التعامل مع الدوال التي تُرجع قيمًا أسهل للفهم مقارنة بالدوال ذات الآثار الجانبية. استعن بقاعدتي no-iterator وno-restricted-syntax. استخدم الدوال ()map / every() / filter()، find() / findIndex() / reduce() / some / للمرور على المصفوفات، والتوابع ()Object.keys / Object.values() / Object.entries لإنتاج مصفوفات حتى تتمكن من المرور على عناصر الكائن. const numbers = [1, 2, 3, 4, 5]; // سيئ let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // جيّد let sum = 0; numbers.forEach((num) => { sum += num; }); sum === 15; // أفضل (استخدام فعالية الدوال، برمجة وظيفية) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; // سيئ const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // جيّد const increasedByOne = []; numbers.forEach((num) => { increasedByOne.push(num + 1); }); // أفضل (الاعتماد على التصور الوظيفي) const increasedByOne = numbers.map(num => num + 1); لا تستخدم المولدات (Generators) في الوقت الراهن. لماذا؟ لأنها لا تُصرَّف على نحو جيّد إلى ES5. إذا كان عليك استخدام المُولدات، أو إذا أردت تجاهل نصيحتنا، تأكد من أن تواقيع دوالها متباعدة بشكل صحيح. استعن بالقاعدة generator-star-spacing. لماذا؟ function و * هما كلمتان مفتاحيتان من نفس المفهوم، فالكلمة المفتاحية * ليست تعديلًا للكلمة المفتاحية function، و function* مختلفة عن function. // سيئ function * foo() { // ... } // سيئ const bar = function * () { // ... }; // سيئ const baz = function *() { // ... }; // سيئ const quux = function*() { // ... }; // سيئ function*foo() { // ... } // سيئ function *foo() { // ... } // سيئ جدّا function * foo() { // ... } // سيئ جدّا const wat = function * () { // ... }; // جيّد function* foo() { // ... } // جيّد const foo = function* () { // ... }; الخصائص استخدم أسلوب الترميز بالنقطة (Dot notation) عند الدخول إلى الخصائص. const luke = { jedi: true, age: 28, }; // سيئ const isJedi = luke['jedi']; // جيّد const isJedi = luke.jedi; استخدام المعقوفتين [] عند الدخول إلى الخاصيّات بواسطة متغير. const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi'); استخدام العامل الأسي ** عند حساب الأسّ. // سيئ const binary = Math.pow(2, 10); // جيّد const binary = 2 ** 10; المتغيرات استخدم دومًا const أو let للتصريح بالمتغيرات. عدم القيام بذلك سيؤدي إلى إنشاء متغيرات عامة، في حين نريد تجنب تلويث فضاء الأسماء العام (Global namespace). استعن بقاعدتيْ no-undef وprefer-const. // سيئ superPower = new SuperPower(); // جيّد const superPower = new SuperPower(); صرّح باستخدام const أو let لكل متغيّر على حدة. استعن بقاعدة one-var في ESlint. لماذا؟ من الأسهل إضافة متغيرات جديدة بهذه الطريقة، ولن تكون مضطرا لإبدال الفاصلة المنقوطة ; بفاصلة , كل ما أردت إضافة متغيّر جديد، وستتخلّص من التعديلات المقتصرة على علامات التنقيط. ستتمكّن كذلك من المرور خلال التنقيح (Debugging) على كل متغيّر على حدة، بدلًا من القفز عليها كلها في وقت واحد. // سيئ const items = getItems(), goSportsTeam = true, dragonball = 'z'; // سيئ (قارن بالتصريح فوقه لتعثر على الخطأ) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // جيّد const items = getItems(); const goSportsTeam = true; const dragonball = 'z'; جمّع كل التصريحات بالكلمة المفتاحية const ثم بعدها التصريحات بالكلمة المفتاحية let. لماذا؟ سيكون هذا مفيدًا لاحقًا عندما تحتاج إلى إسناد متغير اعتمادًا على متغير مُسنَد مسبقا. // سيئ let i, len, dragonball, items = getItems(), goSportsTeam = true; // سيئ let i; const items = getItems(); let dragonball; const goSportsTeam = true; let len; // جيّد const goSportsTeam = true; const items = getItems(); let dragonball; let i; let length; أسند المتغيرات حيث تحتاج لذلك، لكن ضعها في مكان مناسب. لماذا؟ المتغيّرات المصرّح عنها بـ let أو const ذات نطاق كتلي (Block scoped) وليست ذات نطاق دالّي (Function scoped). // سيئ، لا حاجة لنداء الدالة function checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name; } // جيّد function checkName(hasName) { if (hasName === 'test') { return false; } const name = getName(); if (name === 'test') { this.setName(''); return false; } return name; } تجنب الإسناد المتسلسل للمتغيّرات. استعن no-multi-assign بقاعدة في ESLint. لماذا؟ لأن الإسناد المتسلسل ينتج متغيرات عامة. // سيئ (function example() { // يفسّر جافاسكريبت التعليمة أدناه على النحو التالي // let a = ( b = ( c = 1 ) ); // لا يُطبّق التصريح بـ let إلا على المتغيّر a، // المتغيّران b وc يصبحان عامين. let a = b = c = 1; }()); console.log(a); // يتسبّب في خطأ في المرجع ReferenceError console.log(b); // 1 console.log(c); // 1 // جيّد (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // يتسبّب في خطأ في المرجع ReferenceError console.log(b); // يتسبّب في خطأ في المرجع ReferenceError console.log(c); // يتسبّب في خطأ في المرجع ReferenceError // الأمر نفسه ينطبق على `const` تجنب استخدام عمليات الزيادة والإنقاص الأحادية (++, --). استعن بقاعدة no-plusplus. لماذا؟ تخضع عمليات الزيادة والإنقاص الأحادية، حسب وثائق ESLint، لقاعدة الإدراج التلقائي للفواصل المنقوطة، ويمكن أن تتسبب في حدوث أخطاء صامتة مع زيادة أو إنقاص قيمة ضمن التطبيق. كما أنه من الأكثر وضوحًا استخدام num += 1 بدلًا من num++ أو num ++ لتغيير قيم المتغيّرات. تجنُّب استخدام عامل الزيادة والإنقاص الأحادي سيجنبك الزيادة (أو الإنقاص) قبل إجراء عملية، وهو ما يمكن أيضًا أن يسبب سلوكًا غير متوقع في برامجك. // سيئ const array = [1, 2, 3]; let num = 1; num++; --num; let sum = 0; let truthyCount = 0; for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } } // جيّد const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; const sum = array.reduce((a, b) => a + b, 0); const truthyCount = array.filter(Boolean).length; تجنّب إدراج الأسطر قبل علامة الإسناد = أو بعدها. أحط القيمة بقوسيْن إنْ كان الإسناد يخرق قاعدة max-len (طول السطر). استعن بقاعدة operator-linebreak. لماذا لأن الأسطر حول = يمكن أن تعتّم على قيمة الإسناد. // سيّئ const foo = superLongLongLongLongLongLongLongLongFunctionName(); // سيّئ const foo = 'superLongLongLongLongLongLongLongLongString'; // جيّد const foo = ( superLongLongLongLongLongLongLongLongFunctionName() ); // جيّد const foo = 'superLongLongLongLongLongLongLongLongString'; لا تسمح بمتغيّرات غير مستخدمة. استعن بالقاعدة no-unused-vars. لماذا؟ المتغيّرات المُصرّح بها غير المستخدمة في أي جزء من الشفرة هي في الغالب خطأ ناتج عن إعادة هيكلة غير مكتملة. تأخذ هذه المتغيّرات مساحة من الشفرة ويمكن أن تتسبّب في خلط لدى من يقرأه. // سيّئ var some_unused_var = 42; // متغيّرات يقتصر استخدامها على تغيير قيمتها لا تعد مستخدمة. var y = 10; y = 5; // قراءة المتغيّر فقط من أجل التعديل عليه لا يعدّ استخداما var z = 0; z = z + 1; // وسائط غير مستخدمة في الدالة function getX(x, y) { return x; } // جيّد function getXPlusY(x, y) { return x + y; } var x = 1; var y = a + 2; alert(getXPlusY(x, y)); // لا تنطبق القاعدة هنا على المتغيّر type الذي على الرغم من أنه غير مستخدم إلا أن له علاقة بالمتغيّر المُفكَّك // هذه طريقة لاستخراج كائن بإسقاط المفاتيح المحدّدة. var { type, ...coords } = data; // يطابق الكائنُ coords الكائنَ data، غير أنه لا توجد فيه الخاصيّة type التي استخرجناها منه. الرفع إلى أعلى النطاق Hoisting تُرفَع المتغيّرات المُصرَّح عنها بالكلمة المفتاحية var إلى أعلى نطاق الدالة المحتوية الأقرب، إلّا أن هذا الأمر لا ينطبق على عمليّات الإسناد. للمتغيّرات المُعرَّفة بالكلمتيْن المفتاحيتيْن const و let ميزة جديدة تُسمى المناطق الميتة الظرفية (Temporal dead zones)؛ لذا من المهم أن تعرف لماذا لم يعد استخدام الدالة typeof آمنا. // نعرف أن التعليمات التالية لن تعمل (على فرض // أنه لا يوجد متغيّر باسم notDefined) function example() { console.log(notDefined); // يتسبب في خطأ ReferenceError } // تستطيع التصريح بمتغيّر بعد الإحالة إليه // لأن التصريح سيُرفَع إلى أعلى النطاق // ملحوظة: إسناد القيمة true إلى المتغيّر لا يُرفَع // إلى أعلى النطاق. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // يرفع مفسّر جافاسكريبت التصريح بالمتغيّر // إلى أعلى النطاق، وهو ما يعني أنه بإمكاننا // إعادة كتابة المثال السابق على النحو التالي function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; } // استخدام const وlet function example() { console.log(declaredButNotAssigned); // تتسبّب في خطأ ReferenceError console.log(typeof declaredButNotAssigned); // تتسبّب في خطأ ReferenceError const declaredButNotAssigned = true; } يُرفَع اسم متغيّر الدوال العبارات غير المُسمّاة إلى أعلى النطاق، بخلاف إسناد الدالة. function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { console.log('anonymous function expression'); }; } في الدوال العبارات المُسماة يُرفع اسم المتغير، بخلاف اسم الدالة أو متنها. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } // الأمر نفسه يحدث عندما يكون اسم الدالة // هو نفسه اسم المتغيّر. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); }; } يرفع التصريح بالدوال اسم الدالة ومتنها إلى أعلى النطاق. function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } } عوامل المقارنة والمساواة استخدم === و !== بدلًا من == و !=. تقيّم التعليمات الشرطية مثل العبارة if العبارات عبر آلية الإجبار (Coercion) عن طريق الدالة المجرَّدة ToBoolean والتي تتبع دائمًا القواعد البسيطة التالية: تُمنَح للكائنات القيمة true تُعيَّن للنوع Undefined القيمة false تُحدَّد للعبارة Null القيمة false البيانات من النوع المنطقي (Booleans) تُعطى لها القيمة المنطقية الموافقة إذا كانت قيمة العدد تساوي 0+ أو 0- أو NaN تحدّد قيمته المنطقية بـ false، وإلّا يأخذ القيمة true إذا كانت سلسلة المحارف (String) خاوية ("") تُعيَّن قيمتها إلى false، وإلاّ تأخذ القيمة true. if ([0] && []) { // true // المصفوفة (حتى وإنْ كانت فارغة) هي كائن، والكائنات تُقيّم بالقيمة true } استخدم الاختصارات عند التعامل مع قيم منطقية، بالمقابل استخدم المقارنات الصريحة للنصوص والأرقام. // سيئ if (isValid === true) { // ... } // جيّد if (isValid) { // ... } // سيئ if (name) { // ... } // جيّد if (name !== '') { // ... } // سيئ if (collection.length) { // ... } // جيّد if (collection.length > 0) { // ... } استخدم الأقواس المعقوصة ({}) لإنشاء الكتل في البنود case و default التي تحتوي التصريح بمتغيّرات (على سبيل المثال let، const ، function ، وclass). استعن بالقاعدة no-case-declarations. لماذا؟ التصريحات مرئية في كامل كتلة switch، ولكن لا يُعاد تعيينها إلّا عندما تُسنَد لها قيمة، وهو ما لا يحدث إلا عند بلوغ بند case، وهو ما يسبب مشاكل عندما يحاول أكثر من بند case تعريف الشيء نفسه. // سيئ switch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // ... } break; default: class C {} } // جيّد switch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // ... } break; } case 4: bar(); break; default: { class C {} } } لا ينبغي أن تتداخل عوامل المقارنة الثلاثية Ternaries، وفي الغالب ينبغي أن تكون على سطر واحد. استعن بقاعدة no-nested-ternary. // سيئ const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null; // التقسيم إلى عبارتين تستخدمان مقارنات ثلاثية const maybeNull = value1 > value2 ? 'baz' : null; // أفضل const foo = maybe1 > maybe2 ? 'bar' : maybeNull; // الأفضل const foo = maybe1 > maybe2 ? 'bar' : maybeNull; تجنب استخدام المقارنات الثلاثية التي لا لزوم لها. استعن بقاعدة no-unneeded-ternary. // سيئ const foo = a ? a : b; const bar = c ? true : false; const baz = c ? false : true; // جيّد const foo = a || b; const bar = !!c; const baz = !c; استخدم الأقواس عند مزج العمليات، والاستثناء الوحيد هي العمليات الحسابية القياسية (+، -، *، /) بما أنّ الأسبقية فيها مفهومة جيّدا. ننصح بوضع / و* بين قوسين، لأنّ الأسبقية بينهما قد تكون غير واضحة عند المزج بينهما. استعن بقاعدة no-mixed-operators. لماذا؟ هذا يحسّن المقروئية ويوضّح قصد المطوّر. // سيئ const foo = a && b < 0 || c > 0 || d + 1 === 0; // سيئ const bar = a ** b - 5 % d; // سيئ // one may be confused into thinking (a || b) && c if (a || b && c) { return d; } // جيّد const foo = (a && b < 0) || c > 0 || (d + 1 === 0); // جيّد const bar = (a ** b) - (5 % d); // جيّد if (a || (b && c)) { return d; } // جيّد const bar = a + b / c * d; الكتل Blocks استخدم الأقواس المعقوصة للكتل متعددة الأسطر. // سيئ if (test) return false; // جيّد if (test) return false; // جيّد if (test) { return false; } // سيئ function foo() { return false; } // جيّد function bar() { return false; } إذا كنت تستخدم الكتل متعددة الأسطر مع if و else ، ضع else على السطر نفسه الذي يوجد عليه القوس المعقوص الذي يغلق كتلة if. // سيئ if (test) { thing1(); thing2(); } else { thing3(); } // جيّد if (test) { thing1(); thing2(); } else { thing3(); } إذا كانت كتلة if تُرجع قيمة دائمًا (أي تنتهي بالتعليمة return)، فكتلة else المتعلّقة بها غير ضرورية. تعليمة return في كتلة else if موالية لكتلة if تحتوي على return يمكن تقسيمها إلى عدة كتل if. استعن بقاعدة no-else-return. // سيئ function foo() { if (x) { return x; } else { return y; } } // سيئ function cats() { if (x) { return x; } else if (y) { return y; } } // سيئ function dogs() { if (x) { return x; } else { if (y) { return y; } } } // جيّد function foo() { if (x) { return x; } return y; } // جيّد function cats() { if (x) { return x; } if (y) { return y; } } //جيّد function dogs(x) { if (x) { if (z) { return y; } } else { return z; } } تعليمات التحكم Control Statements إذا تجاوزت عبارات التحكم (if, while …) الحد الأقصى لطول السطر، فيمكن وضع كل شرط (أو مجموعة من الشروط) في سطر جديد. كما يجب أن يكون العامل المنطقي في بداية السطر. لماذا؟ وضع العوامل في بداية السطر يحافظ على محاذاة العوامل ويتبع نمطًا مماثلا لتسلسل التوابع. كما يحسّن القراءة من خلال توضيح العمليات المنطقية المعقدة. // سيئ if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { thing1(); } // سيئ if (foo === 123 && bar === 'abc') { thing1(); } // سيئ if (foo === 123 && bar === 'abc') { thing1(); } // سيئ if ( foo === 123 && bar === 'abc' ) { thing1(); } // جيّد if ( foo === 123 && bar === 'abc' ) { thing1(); } // جيّد if ( (foo === 123 || bar === "abc") && doesItLookجيّدWhenItBecomesThatLong() && isThisReallyHappening() ) { thing1(); } // جيّد if (foo === 123 && bar === 'abc') { thing1(); } لا تستخدم عوامل الاختيار بدلًا من تعليمات التحكم // سيّئ !isRunning && startRunning(); // جيّد if (!isRunning) { startRunning(); } التعليقات استخدم /** ... */ للتعليقات متعددة الأسطر. // سيئ // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) { // ... return element; } // جيّد /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; } استخدم // لأجل التعليقات ذات السطر الواحد. ضع التعليقات ذات السطر الواحد على سطر جديد فوق التعليمة البرمجية موضوع التعليق. وضع سطرًا فارغًا قبل التعليق إلا إذا كان على السطر الأول من كتلة. // سيئ const active = true; // is current tab // جيّد // is current tab const active = true; // سيئ function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // جيّد function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // جيّد أيضا function getType() { // set the default type to 'no type' const type = this.type || 'no type'; return type; } ابدأ كل التعليقات بمسافة لجعلها أسهل للقراءة. استعت بقاعدة spaced-comment. // سيئ //is current tab const active = true; // جيّد // is current tab const active = true; // سيئ /** *make() returns a new element *based on the passed-in tag name */ function make(tag) { // ... return element; } // جيّد /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; } ابدأ التعليقات التي تهدف إلى لفت انتباه المطورين الآخرين إلى أنّ هناك مشكلةً تحتاج إلى المراجعة بالكلمات FIXME أو TODO، أو إذا كنت تقترح حلّا لتنفيذه. تختلف هذه عن التعليقات العادية لأنها قابلة للتنفيذ. الإجراءات قد تكون: FIXME: -- need to figure this out أو TODO: -- need to implement مثلا. استخدم // FIXME: للإشارة إلى مشكلة. class Calculator extends Abacus { constructor() { super(); // FIXME: shouldn’t use a global here total = 0; } } استخدم // TODO: لاقتراح حل لمشكلة. class Calculator extends Abacus { constructor() { super(); // TODO: total should be configurable by an options param this.total = 0; } } المسافات Whitespace أضف فراغيْن بسيطيْن (زر المسافة مرتيْن) للمسافات البادئة. استعن بقاعدة indent. // سيئ function foo() { ∙∙∙∙let name; } // سيئ function bar() { ∙let name; } // جيّد function baz() { ∙∙let name; } ضع مسافة واحدة قبل القوس المعقوص الأول. استعن بالقاعدة space-before-blocks. // سيئ function test(){ console.log('test'); } // جيّد function test() { console.log('test'); } // سيئ dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog', }); // جيّد dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog', }); ضع مسافةً قبل القوس الفاتح في تعليمات التحكم (if, while …). لا تضع أي مسافات بين لائحة الوسائط Arguments واسم الدالة عند استدعاء دالة أو التصريح بها. استعن بقاعدة keyword-spacing. // سيئ if(isJedi) { fight (); } // جيّد if (isJedi) { fight(); } // سيئ function fight () { console.log ('Swooosh!'); } // جيّد function fight() { console.log('Swooosh!'); } ضع مسافات بين العوامل. استعن بقاعدة space-infix-ops. // سيئ const x=y+5; // جيّد const x = y + 5; أنهِ الملفات بمحرف الرجوع إلى السطر. استعن بقاعدة eol-last // سيئ import { es6 } from './AirbnbStyleGuide'; // ... export default es6; // سيئ import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ↵ // جيّد import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ استخدم المسافة البادئة عند استخدام سلسة طويلة من التوابع (أكثر من اثنتين). استخدم نقطة في البداية، لكي تبين أنّ السطر هو استدعاء لتابع، وليس تعليمةً جديدة. استعن بالقاعدتيْن newline-per-chained-call وno-whitespace-before-property. // سيئ $('#items').find('.selected').highlight().end().find('.open').updateCount(); // سيئ $('#items'). find('.selected'). highlight(). end(). find('.open'). updateCount(); // جيّد $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount(); // سيئ const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // جيّد const leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // جيّد const leds = stage.selectAll('.led').data(data); ضع سطرًا فارغًا بعد الكتل وقبل التعليمة الموالية. // سيئ if (foo) { return bar; } return baz; // جيّد if (foo) { return bar; } return baz; // سيئ const obj = { foo() { }, bar() { }, }; return obj; // جيّد const obj = { foo() { }, bar() { }, }; return obj; // سيئ const arr = [ function foo() { }, function bar() { }, ]; return arr; // جيّد const arr = [ function foo() { }, function bar() { }, ]; return arr; لا تحش الكتل بأسطر فارغة. استعن بالقاعدة padded-blocks. // سيئ function bar() { console.log(foo); } // سيئ if (baz) { console.log(qux); } else { console.log(foo); } // سيئ class Foo { constructor(bar) { this.bar = bar; } } // جيّد function bar() { console.log(foo); } // جيّد if (baz) { console.log(qux); } else { console.log(foo); } لا تستخدم عدة أسطر فارغة لحشو شفرتك البرمجية. استعن بالقاعدة no-multiple-empty-lines. // سيّئ class Person { constructor(fullName, email, birthday) { this.fullName = fullName; this.email = email; this.setAge(birthday); } setAge(birthday) { const today = new Date(); const age = this.getAge(today, birthday); this.age = age; } getAge(today, birthday) { // .. } } // جيّد class Person { constructor(fullName, email, birthday) { this.fullName = fullName; this.email = email; this.setAge(birthday); } setAge(birthday) { const today = new Date(); const age = getAge(today, birthday); this.age = age; } getAge(today, birthday) { // .. } } لا تضف مسافات داخل الأقواس. استعن بالقاعدة space-in-parens. // سيئ function bar( foo ) { return foo; } // جيّد function bar(foo) { return foo; } // سيئ if ( foo ) { console.log(foo); } // جيّد if (foo) { console.log(foo); } لا تضف مسافات داخل الأقواس المعكوفة ([]). استعن بالقاعدة array-bracket-spacing. // سيئ const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // جيّد const foo = [1, 2, 3]; console.log(foo[0]); أضف مساحات داخل الأقواس المعقوصة. استعن بالقاعدة object-curly-spacing. // سيئ const foo = {clark: 'kent'}; // جيّد const foo = { clark: 'kent' }; تجنب التعليمات البرمجية التي يتجاوز طولها 100 محرف (باحتساب المسافات). النصوص الطويلة - حسب قاعدة مذكورة أعلاه - مستثناة من هذه القاعدة، وينبغي ألّا تُفكك. استعن بالقاعدة max-len. لماذا؟ لضمان سهولة القراءة والصيانة. // سيئ const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // سيئ $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // جيّد const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // جيّد $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.')); افرض مسافات متناسقة بعد القوس المعقوص البادئ لكتلة وبعد القوس المعقوص البادئ لكتلة موالية لها على السطر نفسه. الأمر نفسه ينطبق على الأقواس المعقوصة المكمّلة للكتلتيْن. استعن بقاعدة block-spacing. // سيّئ function foo() {return true;} if (foo) { bar = 0;} // جيّد function foo() { return true; } if (foo) { bar = 0; } تجنّب المسافات قبل الفواصل وافرض مسافة بعد الفاصلة. استعن بالقاعدة comma-spacing. // جيّد var foo = 1,bar = 2; var arr = [1 , 2]; // سيّئ var foo = 1, bar = 2; var arr = [1, 2]; افرض عدم استخدام المسافات داخل الأقواس المعكوفة لخاصيّة محسوبة. استعن بالقاعدة computed-property-spacing. // سيّئ obj[foo ] obj[ 'foo'] var x = {[ b ]: a} obj[foo[ bar ]] // جيّد obj[foo] obj['foo'] var x = { [b]: a } obj[foo[bar]] تجنّب المسافات بين دالة واستدعائها. استعن بالقاعدة func-call-spacing. // سيئ func (); func (); // جيّد func(); افرض المسافات بين المفاتيح والقيم في الخاصيّات المصنّفة النوع في الكائنات. استعن بالقاعدة key-spacing. // سيّئ var obj = { foo : 42 }; var obj2 = { foo:42 }; // جيّد var obj = { foo: 42 }; تجنّب المسافات الباقية بعد نهاية الأسطر. استعن بالقاعدة no-trailing-spaces. تجنّب عدة أسطر فارغة، ولاتسمح إلا بسطر واحد جديد في نهاية الملفات. تجنّب كذلك وجود سطر جديد في بداية الملفات. استعن بالقاعدة no-multiple-empty-lines. // سيّئ - عدة أسطر فارغة. var x = 1; var y = 2; // سيّئ - سطران جديدان بعد نهاية الملف var x = 1; var y = 2; // سيّئ - سطر جديد في بداية الملف var x = 1; var y = 2; // جيّد var x = 1; var y = 2; الفواصل تجنب الفواصل في البداية. استعن بالقاعدة comma-style. // سيئ const story = [ once , upon , aTime ]; // جيّد const story = [ once, upon, aTime, ]; // سيئ const hero = { firstName: 'Ada' , lastName: 'Lovelace' , birthYear: 1815 , superPower: 'computers' }; // جيّد const hero = { firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers', }; استخدم فاصلة إضافية. استعن بالقاعدة comma-dangle. لماذا؟ هذا يؤدي إلى توضيح الاختلافات بين الشفرات البرمجية في إيداعات Git . علاوة على ذلك، تحذف مصرّفات مثل Babel الفواصل الزائدة في الشفرة الناتجة عن التصريف، ممّا يعني أنه لا داعي للقلق من مشكلة الفاصلة المُجرجَرة (Trailing comma) في المتصفحات القديمة. // سيئ، فرق بين إيداعين في Git بدون فاصلة إضافية const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // جيّد، فرق بين إيداعين في Git بوجود فاصلة إضافية const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], }; // سيئ const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ]; // جيّد const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // سيئ function createHero( firstName, lastName, inventorOf ) { // does nothing } // جيّد function createHero( firstName, lastName, inventorOf, ) { // does nothing } // جيّد (انتبه إلى أنه يجب ألا تظهر فاصلة بعد عامل الاستناد Rest element) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // سيئ createHero( firstName, lastName, inventorOf ); // جيّد createHero( firstName, lastName, inventorOf, ); // جيّد (انتبه إلى أنه يجب ألا تظهر فاصلة بعد عامل الاستناد Rest element) createHero( firstName, lastName, inventorOf, ...heroArgs ); الفاصلة المنقوطة Semicolons استخدم الفاصلة المنقوطة. استعن بالقاعدة semi. لماذا؟ عندما يصادف مفسّر جافاسكريبت عودة إلى السطر بدون فاصلة منقوطة، فإنه يستخدم مجموعة من القواعد تسمى الإدراج التلقائي للفاصلة المنقوطة (Automatic Semicolon Insertion) لتحديد ما إذا كان يجب احتساب نهاية السطر على أنها نهاية للتعليمة البرمجية، ويُدرِج، كما يوحي الاسم، فاصلة منقوطة في الشفرة البرمجية قبل نهاية السطر إذا رأى أن التعليمة قد انتهت. الإدراج التلقائي للفاصلة المنقوطة ترافقه سلوكيات شاذة، قد تتسبّب في إساءة فهم الشفرة البرمجية. تصبح هذه القواعد أكثر تعقيدًا مع إضافة ميزات جديدة إلى جافاسكريبت. سيساعد الوضوح في إنهاء التعليمات البرمجية وإعداد أداة جودة الشفرة (مثل ESLint) لتحديد الفواصل المنقوطة المفقودة في تجنب تلك المشاكل. // سيئ، تنتج عنه استثناءات (Exceptions) const luke = {} const leia = {} [luke, leia].forEach(jedi => jedi.father = 'vader') // سيئ، تنتج عنه استثناءات (Exceptions) const reaction = "No! That's impossible!" (async function meanwhileOnTheFalcon(){ // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()) // سيّئ، يُرجع `undefined` بدلًا من القيمة الموجودة في السطر الموالي // يحدث هذا دائمًا عندما تكون التعليمة return مفصولة بسطر عن القمية المرجَعة // وذلك بسبب حدوث الإدراج التلقائي للفاصلة المنقوطة function foo() { return 'search your feelings, you know it to be foo' } // جيّد const luke = {}; const leia = {}; [luke, leia].forEach((jedi) => { jedi.father = 'vader'; }); // جيّد const reaction = "No! That's impossible!"; (async function meanwhileOnTheFalcon(){ // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()); // جيّد function foo() { return 'search your feelings, you know it to be foo'; } التحويل بين أنواع البيانات وفرض نوع معيّن Type Casting & Coercion حوّل نوع البيانات في بداية التعليمة. سلاسل المحارف: // => this.reviewScore = 9; // سيئ const totalScore = new String(this.reviewScore); // المتغيّر totalScore كائن "object" وليس سلسلة محارف "string" // سيئ const totalScore = this.reviewScore + ''; // يستدعي التابع this.reviewScore.valueOf() // سيئ const totalScore = this.reviewScore.toString(); // لا تضمن إرجاع سلسلة محارف // جيّد const totalScore = String(this.reviewScore); بالنسبة للأعداد: استخدم Number لأجل التحويل، أما لتحليل النصوص فاستخدم دائمًا التابع parseInt مع أساس radix . استعن بالقاعدتيْن radix وno-new-wrappers. const inputValue = '4'; // سيئ const val = new Number(inputValue); // سيئ const val = +inputValue; // سيئ const val = inputValue >> 0; // سيئ const val = parseInt(inputValue); // جيّد const val = Number(inputValue); // جيّد const val = parseInt(inputValue, 10); ضع تعليقًا يشرح ما الذي تفعله ولماذا إذا كنت مضطرّا لاستعمال parseInt واحتجت إلى استخدام Bitshift لأسباب تتعلق بالأداء. // جيّد /** * parseInt was the reason my code was slow. * Bitshifting the String to coerce it to a * Number made it a lot faster. */ const val = inputValue >> 0; احذر عند استخدام عمليات الإزاحة bitshift ، فالأعداد مُمثلة بقيم من 64 بتا. ولكنّ عمليات الإزاحة دائما تُرجع أعدادًا ممثلة بقيم من 32 بت (المرجع). يمكن أن تؤدي الإزاحة إلى نتائج غير متوقعة عند استخدام قيم عددية أكبر من 32 بتا. أكبر قيمة للأعداد ذات الإشارة الممثلة على 32 بتا هي 2,147,483,647. (نقاش). 2147483647 >> 0; // => 2147483647 2147483648 >> 0; // => -2147483648 2147483649 >> 0; // => -2147483647 القيم المنطقية: const age = 0; // سيئ const hasAge = new Boolean(age); // جيّد const hasAge = Boolean(age); // best const hasAge = !!age; اصطلاحات التسمية تجنب الأسماء المكونة من حرف واحد. استخدم أسماء معبّرة. استعن بالقاعدة id-length. // سيئ function q() { // ... } // جيّد function query() { // ... } استخدم أسلوب camelCase لتسمية الكائنات، والدوال، والنظائر (Instances). استعن بالقاعدة camelCase. // سيئ const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // جيّد const thisIsMyObject = {}; function thisIsMyFunction() {} لا تستخدم أسلوب التسمية PascalCase إلا عند تسمية المُنشئات أو الأصناف. استعن بالدالة new-cap. // سيئ function user(options) { this.name = options.name; } const سيئ = new user({ name: 'nope', }); // جيّد class User { constructor(options) { this.name = options.name; } } const جيّد = new User({ name: 'yup', }); لا تستخدم العارضة السفلية في البداية أو النهاية. استعن بالقاعدة camelcno-underscore-danglease. لماذا؟ لا يوجد في جافاسكريبت مفهوم الخصوصية عندما يتعلّق الأمر بالخاصيّات أو التوابع. على الرغم من أن الكثيرين ينظرون إلى وضع العارضة السفلية في بداية الاسم على أنه اصطلاح يعني “خاص”، إلّا أن هذه الخاصيّات عامة كلها، وعلى هذا النحو، فهي جزء من الواجهة البرمجية API العمومية. هذا الاصطلاح قد يؤدي بالمطورين للاعتقاد خطأً بأن التغيير لن يؤثّر سلبًا على الشفرة البرمجية، أو أنه ليست هناك حاجة للاختبار. إن بدا لك هذا الشرح طويلًا، فتذكر هذه الجملة: إذا كنت تريد لشيء أن يكون “خاصّا”، فيجب ألا تضعه في مكان ملحوظ. // سيئ this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // جيّد this.firstName = 'Panda'; // جيّد، في البيئات التي يكون WeakMaps متوفّرًا فيها، راجع الرابط التالي // see https://kangax.github.io/compat-table/es6/#test-WeakMap const firstNames = new WeakMap(); firstNames.set(this, 'Panda'); لا تحفظ مرجعًا إلى this. واستخدم الدوال السهمية أو التابع Function#bind . // سيئ function foo() { const self = this; return function () { console.log(self); }; } // سيئ function foo() { const that = this; return function () { console.log(that); }; } // جيّد function foo() { return () => { console.log(this); }; } يجب أن يتطابق اسم الملف القاعدي (Base filename) تمامًا مع اسم التصدير الافتراضي. // file 1 contents class CheckBox { // ... } export default CheckBox; // file 2 contents export default function fortyTwo() { return 42; } // file 3 contents export default function insideDirectory() {} // in some other file // سيئ import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export // سيئ import CheckBox from './check_box'; // PascalCase import/export, snake_case filename import forty_two from './forty_two'; // snake_case import/filename, camelCase export import inside_directory from './inside_directory'; // snake_case import, camelCase export import index from './inside_directory/index'; // requiring the index file explicitly import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly // جيّد import CheckBox from './CheckBox'; // PascalCase export/import/filename import fortyTwo from './fortyTwo'; // camelCase export/import/filename import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" // ^ supports both insideDirectory.js and insideDirectory/index.js استخدم أسلوب التسمية camelCase عندما التصدير الافتراضي لدالة. يجب أن يكون اسم الملف الخاص بك مطابقًا لاسم دالتك. function makeStyleGuide() { // ... } export default makeStyleGuide; استخدم أسلوب التسمية PascalCase عندما تصدّر منشئًا أو صنفًا أو صنفًا أو مكتبة دوال أو كائن مجرّدا. const AirbnbStyleGuide = { es6: { }, }; export default AirbnbStyleGuide; يجب أن تكون كل حروف المختصرات والكلمات المنحوتة إما مكتوبة بأحرف كبيرة وإما بأحرف صغيرة. لماذا؟ تهدف التسميات لتسهيل قراءة الشفرة على الإنسان، وليس لاسترضاء خوارزميات الكمبيوتر. // سيئ import SmsContainer from './containers/SmsContainer'; // سيئ const HttpRequests = [ // ... ]; // جيّد import SMSContainer from './containers/SMSContainer'; // جيّد const HTTPRequests = [ // ... ]; // جيّد أيضا const httpRequests = [ // ... ]; // أفضل import TextMessageContainer from './containers/TextMessageContainer'; // أفضل const requests = [ // ... ]; اختياريًّا، يمكنك كتابة ثابت بحروف كبيرة إذا تحقّقت الشروط التالية: 1) التصدير، 2) التصريح بالكلمة const ، أي أنه لا يمكن إعادة إسناده، 3) يمكن للمبرمج أن يثق أنه لم يتغيّر لا هو ولا الخاصيّات المتفرّعة عنه. لماذا؟ هذه أداة إضافية للحالات التي يكون المبرمج فيها غير متأكد من أن المتغيّر ستتغيّر قيمته. تخبر المتغيّرات المكتوبة بحروف كبيرة (مثل UPPERCASE_VARIABLES) المبرمج أن بإمكانه الوثوق من أن تلك الثوابت (وخاصيّاتها) لن تتغيّر قيمتها. هل ينطبق الأمر على كل المتغيّرات المعرّفة بالكلمة const؟ لا حاجة لذلك، وبالتالي يجب ألا تُستخدَم الحروف الكبيرة في تسمية الثوابت داخل ملف، ولكنها يجب أن تُستخدَم للثوابت المُصدَّرة. ماذا عن الكائنات المُصدَّرة؟ استخدم الحروف الكبيرة في المستوى الأعلى من التصدير (مثلًا EXPORTED_OBJECT.key) وتأكّد من أن الخاصيّات المتفرّعة كلها لا تتغيّر. // سيّئ const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file'; // سيّئ export const THING_TO_BE_CHANGED = 'should obviously not be uppercased'; // سيّئ export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables'; // --- // مرخَّص به، لكنه لا يضيف قيمة دلالية export const apiKey = 'SOMEKEY'; // أفضل في أغلب الحالات export const API_KEY = 'SOMEKEY'; // --- // سيّء، حروف كبيرة غير ضرورية في الاسم مع انعدام القيمة الدلالية export const MAPPING = { KEY: 'value' }; // جيّد export const MAPPING = { key: 'value' }; المسترجعات (Accessors) ليس مفروضًا وجود توابع الاسترجاع للوصول إلى الخاصيّات. لا تستخدم توابع الاسترجاع أو التعديل التي توفّرها جافاسكريبت لأنّ لها آثارًا جانبيةً غير متوقعة، ويصعب اختبارها وصيانتها والتعامل معها. إنْ أردت استخدام المسترجعات (أو المعدّلات) فمن الجيّد استخدام التوابع ()getVal و('setVal('hello لهذا الغرض. // سيئ class Dragon { get age() { // ... } set age(value) { // ... } } // جيّد class Dragon { getAge() { // ... } setAge(value) { // ... } } استخدم()isVal أو ()hasVal للخاصيّات والتوابع المنطقية. // سيئ if (!dragon.age()) { return false; } // جيّد if (!dragon.hasAge()) { return false; } لا ضير في إنشاء دوال ()get و ()set، ولكن يجب أن تكون متسقة. class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } } الأحداث عند ربط حمولات البيانات بالأحداث (سواء كانت أحداث DOM أوأحداث مكتبات خاصّة مثل Backbone)، مرّر كائنًا مصنّف النوع (معروف أيضًا باسم “hash”) بدلًا من قيمة خام. سيسمح ذلك لاحقًا بإضافة المزيد من البيانات إلى حمولة الحدث دون الحاجة إلى إيجاد وتحديث كل معالجات الحدث. على سبيل المثال، بدلًا من: // سيئ $(this).trigger('listingUpdated', listing.id); // ... $(this).on('listingUpdated', (e, listingID) => { // do something with listingID }); من الأفضل استخدام: // جيّد $(this).trigger('listingUpdated', { listingID: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { // do something with data.listingID }); jQuery ضع السابقة $ قبل متغيرات jQuery . // سيئ const sidebar = $('.sidebar'); // جيّد const $sidebar = $('.sidebar'); // جيّد const $sidebarBtn = $('.sidebar-btn'); أضف عمليات البحث المؤقت في jQuery إلى التخبئة (Cache). // سيئ function setSidebar() { $('.sidebar').hide(); // ... $('.sidebar').css({ 'background-color': 'pink', }); } // جيّد function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ... $sidebar.css({ 'background-color': 'pink', }); } بالنسبة لاستعلامات DOM استخدم $('.sidebar ul') أو parent > child $('.sidebar > ul'). راجع jsperf. استخدم التابع find في الاستعلام عن الكائنات في نطاق jQuery. // سيئ $('ul', '.sidebar').hide(); // سيئ $('.sidebar').find('ul').hide(); // جيّد $('.sidebar ul').hide(); // جيّد $('.sidebar > ul').hide(); // جيّد $sidebar.find('ul').hide(); المكتبة القياسية تحوي المكتبة القياسية أدوات مساعدة لم تعد تُستخدم ولكن يُيقى عليها للتوافق مع المتصفحات القديمة. استخدم التابع Number.isNaN بدلًا من التابع العام isNaN. استعن بالقاعدة no-restricted-globals. لماذا؟ يحوّل التابع isNaN القيم غير العددية إلى أعداد، ويُرجع القيمة true لأي شيء يتحوّل إلى NaN. إذا كان السلوك هو ما ترغب فيه، فكن صريحًا في ذلك. // سيئ isNaN('1.2'); // false isNaN('1.2.3'); // true // جيّد Number.isNaN('1.2.3'); // false Number.isNaN(Number('1.2.3')); // true استخدم التابع Number.isFinite بدلًا من التابع العام isFinite. استعن بالقاعدة no-restricted-globals. لماذا؟ isFinite تحوّل القيم غير العددية إلى أعداد، وتُرجع القيمة true لأي شيء يتحوّل إلى عدد منته. إذا كان السلوك هو ما ترغب فيه، فكن صريحًا في ذلك. // سيئ isFinite('2e3'); // true // جيّد Number.isFinite('2e3'); // false Number.isFinite(parseInt('2e3', 10)); // true الاختبار يجب أن تكتب اختبارات، وليس أساسيًّا الإطار الذي تستخدمه لذلك. المهم أن تكتبها. احرص على كتابة العديد من الدوال البسيطة والصغيرة، وقلّل من استخدام البيانات المتحوّلة . كن حذرًا عند استخدام أصناف stubs و mocks لأنها يمكن أن تجعل اختباراتك أكثر هشاشة. نستخدم mocha وjest في Airbnb. يُستخدَم tape كذلك من حين لآخر في وحدات صغيرة ومعزولة. محاولة اختبار 100٪ من الشفرة هو هدف جيد، حتى لو لم يكن دائمًا عمليّا. كلما أصلحت خللًا، قم بكتابة اختبار ارتداد (Regression test). فمن المؤكد أنّه بدونه ستعود الثغرات مجدّدا. ترجمة - وبتصرّف - للمقال Airbnb JavaScript Style Guide1 نقطة
-
انا محتار بين تعليم php و دورة html و css و JavaScript مع العلم عندي أساسيات بكل. اريد مساعدة والنصيحة لان اريد انا اشتغل بها في ألمانيا وشكرا....1 نقطة
-
مرحباً بك @Sharbl Talia. لا يوجد هنالك مجال للمقارنة بين PHP و HTML و JavaScript لأن كل لغة تقوم بإستيفاء غرض معين فريد عن البقية. PHP تستعمل لتصميم السيرفرات ومعالجة بيانات الويب، HTML تستعمل لبناء هياكل صفحات الويب، JavaScript تستعمل لإضافة ديناميكية على صفحات الويب. يجب عليك التحديد أولاً هل تريد أن تكون مطور Front-End أي تقوم بتصميم واجهات المستخدم باستخدام HTML/CSS/JavaScript أم مطور Back-End لتقوم بتطوير السيرفرات التي تعالج بيانات الويب ويمكنك استخدام عدد كبير من اللغات هنا ليس PHP فحسب أيضاً Ruby أو Python مثلاً، أم ترغب أن تكون Full-Stack والذي يعني المقدرة على تطوير واجهات المستخدم والسيرفرات معاً.1 نقطة
-
السلام عليكم صديقي @Sharbl Talia . يمكنك البدء بدورة html مع css ثم Javascript أو PHP , يختلف الأمر على حسب تخصصك الذي تريده إذا كان Frontend أو Backend. يمكنك تصفح عن التقنيات الأكثر شيوعاً في ألمانيا حتى تتعلم مهارات مواكبة مع سوق العمل عندك . Frontend )html) : هي لغة متخصصة لبناء هيكل الموقع , وضع النص , الأزرار , الحقول ... كل شيء يتم وضعه بإستخدام html و معها نستعمل css لكل أنواع تجميل و تصميم الموقع . Backend) php ): هي لغة تتحكم في ألية عمل html , حفظ المعلومات إلى قاعدة البيانات , أداء وظيفة عند الضفط على زر إلخ... من جميع وظائف الموقع.1 نقطة