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

البحث في الموقع

المحتوى عن 'الدوال'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 6 نتائج

  1. لا شك أننا نتعامل مع الكثير من الدوال في برنامج مايكروسوفت إكسل، فهنالك المئات من الدوال التي تُبسّط علينا العمل دائمًا، ولكن توجد بعض العمليات التي تحتاج إلى بعض الاحتيال عليها للوصول إلى حل المسائل التي تعترضنا، وربما نستخدم خلالها العديد من الدوال المتداخلة مع بعضها البعض لحل المسألة، وباستخدام برمجة المايكروسوفت إكسل عن طريق الفيجوال بيسك يمكننا صناعة دوال خاصة بعملنا، والتي تخدم الوصول لحل المشاكل التي نتعامل معها دومًا بأقصر الطرق. تعرفنا سابقًا على آلية كتابة الإجراءات باستخدام فيجوال بيسك في درس مدخل إلى البرمجة باستخدام VBA في مايكروسوفت إكسل، ووجدنا أن الإجراء من النوع sub-routine يُنفذ أمرًا أو مجموعةً معينةً من الأوامر، وأن الإجراء من النوع Function يُضيف دوال على برنامج مايكروسوفت إكسل للاستخدام الاعتيادي كما لو أنها من ضمن دوال البرنامج الأساسية. صناعة الدوال توجد طريقتان لكتابة الإجراء هما: إجراء مع وسطاء (معطيات) وتكون الشيفرة كما يلي: Function name(arguments) as type Code End Function حيث إن: name: اسم للدالة التي نصنعها، ويُفضل أن يكون الاسم مُعبرًا عن عمل الدالة. arguments: الوسطاء التي ستعمل الدالة مع محتوياتها. type: نوع المُخرَج للدالة التي نصنعها. Code: مجموعة الشيفرات التي ستُنفذها الدالة. مثال لدينا البيانات التالية، ونريد صناعة دالة خاصة تعمل على دمج الاسم الأول مع الأخير للحصول على الاسم الكامل. لدينا وسيطان هنا، هما: الاسم الأول والاسم الأخير من النوع النصي، لذا نكتب الإجراء بالشكل التالي: Function FullName(FirstName As String, LastName As String) As String FullName = FirstName & " " & LastName End Function عند العودة إلى برنامج مايكروسوفت إكسل سنجد أن الدالة أُضيفت إلى الدوال الأساسية فيه، وتعمل مثل باقي الدوال الاعتيادية. مثال لدينا البيانات التالية، ونريد صناعة دالة خاصة تحسب الراتب بعد الزيادة، وذلك وفقًا لتقييم كل موظف. حيث يتقاضى الموظف زيادةً قدرها 0.2 عندما يكون تقييمه أكثر من 7، وزيادةً قدرها 0.1 عندما يكون تقييمه أكثر من 5 وأقل من 7، وزيادةً أخرى قدرها 0.08 عندما يكون أكثر من 1، وزيادةً قدرها 0.04 إذا لم نُسند خانة التقييم للدالة، وعليه يكون الإجراء كالتالي: Function NewSalary(Salary As Currency, Optional Rate As Integer) As Currency If Rate = 0 Then NewSalary = Salary + (Salary * 0.04) ElseIf Rate >= 7 Then NewSalary = Salary + (Salary * 0.2) ElseIf Rate >= 5 Then NewSalary = Salary + (Salary * 0.1) ElseIf Rate >= 1 Then NewSalary = Salary + (Salary * 0.08) End If End Function بتنفيذ الدالة على البيانات السابقة نجد التالي: إجراء بدون وسطاء (معطيات) وتكتب الشيفرة كما يلي: Function name() as type Code End Function حيث إن: name: اسم للدالة التي نصنعها، ويُفضل أن يكون الاسم مُعبرًا عن عمل الدالة. type: نوع المُخرَج للدالة التي نصنعها. Code: مجموعة الشيفرات التي ستُنفذها الدالة. مثال لنَعُد إلى المثال السابق، حيث نريد إضافة عمود يحتوي أرقامًا تعريفيةً عشوائيةً للموظف، لذلك نكتب الإجراء التالي: Function RandCode() As Long RandCode = Rnd() * 10000 End Function عند تنفيذ الدالة على البيانات السابقة فسنحصل على النتيجة التالية: التعامل مع الأحداث تعلمنا في المقالات السابقة كيفية كتابة إجراءات لتنفيذ مجموعة من الأوامر، وذلك بطريقتين: الأولى عندما نطلب منه تنفيذ الإجراء يدويًا، والثانية عن طريق إضافة أزرار تُنفذ الإجراء عند الضغط عليها، لكن في بعض الأحيان نحتاج أن تُنفَّذ هذه الإجراءات آليًا عند وقوع حدث معين، مثل فتح ملف، أو ظهور رسالة ترحيب عند فتح ملف إكسل، أو عند الطباعة، أو عند تعبئة خلية، وهذه الإجراءات تسمى أحداثًا، وهي موجودة في كل لغات البرمجة، ونستطيع برمجة برنامج الإكسل لتنفيذها عند وقوع الحدث. الصيغة العامة لكتابة الأحداث Sub Event_Name (variables) Our code End sub نجد أن الصيغة العامة للحدث هي نفسها للإجراء الروتيني، ولكن يجب تسميتها بأسماء محددة تدل على حدث معين، أي أن الاسم هنا ليس اختياريًا، بل يكون مُعرّفًا عن طريق لغة البرمجة، وتوجد بعض القوانين لكتابة الأحداث وهي: تُكتب الأحداث في محرر الفيجوال بيسك ضمن الأوراق أو ضمن الملف، وليس ضمن model؛ لأن الحدث هو خاص بورقة العمل أو الملف، وبالنقر مرتين على اسم الصفحة أو اسم الملف يفتح المحرر الخاص بالصفحة أو الملف. الحدث خاص، فلا يمكن جعله ضمن المحرر عامًا، ولتنفيذ ذلك توجد قائمة منسدلة تعمل على تبديل الإجراء من عام إلى إجراء خاص بالصفحة أو الملف. اسم الحدث هو اسم معين ليس اختياريًا، أي أن محرر الشيفرات هو من يزودنا به ولا يمكن إنشاؤه، ونُحدد الحدث الذي نريده من القائمة المنسدلة في الزاوية اليمنى من واجهة المحرر. سنتعرف على بعض الأمثلة التي تشرح آلية عمل الأحداث. مثال 1 نريد إنشاء حدث يُظهر رسالة ترحيب عند فتح صفحة معينة، لذا فأول ما نفعله البحث عن الحدث الذي يُنفذ عند فتح الصفحة، وهو مُعرّف بالاسم SheetActivate، وهذا الحدث يعمل على مستوى الملف، ويكون الإجراء كالتالي: Private Sub Workbook_SheetActivate(ByVal Sh As Object) If Sh.Name = "Sheet2" Then MsgBox "hello" End If End Sub حيث إن المتغير sh هو متغير مُعرف في الحدث، يُعبر عن الصفحات كمتغيرات. مثال 2 نريد إظهار نفس الرسالة السابقة عند فتح ملف الإكسل. Workbook_Open: هو الحدث الذي يُنفّذ عند فتح ملف الإكسل، ولذلك نكتب الإجراء التالي: Private Sub Workbook_Open() MsgBox "welcome back" End Sub مثال 3 نريد كتابة إجراء يُضيف الصفحات الجديدة في نهاية صف الصفحات، ولتنفيذ ذلك علينا تقسيم المراحل لعمل الإجراء ومعرفة الأوامر الضرورية لفعل ذلك: Workbook_NewSheet: هو الحدث الذي يُنفذ عند إضافة صفحة عمل جديدة. يجب في البداية أن نعرف عدد الصفحات في الملف، لذا لنُعرّف المتغير sheet_count، ونُخزن فيه قيمة الأمر الذي يحصي عدد الصفحات، وهو الأمر sheet.count. نكتب الأمر المسؤول عن تحريك الصفحة الجديدة لآخر الصف، وهو الأمر التالي: object.move after:=sheets("عدد الصفات في الملف") حيث يُعبّر object هنا عن الصفحة الجديدة، ويُصبح الإجراء كالتالي: Private Sub Workbook_NewSheet(ByVal Sh As Object) Dim sheet_count As Integer sheet_count = Sheet.Count Sh.Move after:=Sheets("sheet_count") End Sub مثال 4 نريد تنفيذ إجراء عند الانتقال من خلية إلى أخرى يتغير لون عمود وسطر تقاطع الخلية، لذا نكتب الإجراء كما يلي: في البداية نريد دالةً تحذف الألوان السابقة عن طريق التعليمة التالية: Cells.Interior.ColorIndex = xlNone حيث تعني القيمة xlNone جعل الخلية بلا لون، وهذه التعليمة تُلغي اللون لكل خلايا الصفحة. الآن نكتب التعليمة الخاصة بجعل خلايا السطر كاملًا تأخذ لونًا موحدًا كالتالي: EntireRow.Interior.Color = RGB(14, 148, 148) ونفس الحالة بالنسبة لإعطاء خلايا العمود لونًا واحدًا، ولكن نُسبق التعليمتين بالأمر Target الذي يُعبر هنا عن مجال الصفحة، ومن هنا يصبح الإجراء بالشكل التالي: Private Sub Worksheet_SelectionChange(ByVal Target As Range) Cells.Interior.ColorIndex = xlNone Target.EntireRow.Interior.Color = RGB(14, 148, 148) Target.EntireColumn.Interior.Color = RGB(14, 148, 148) End Sub أهم الأحداث المرتبطة بملف العمل الأحداث المرتبطة بملف العمل كثيرة، ولكن سنذكر أهمها فيما يلي: Workbook_BeforeSave: الحدث قبل إتمام حفظ الملف. Workbook_BeforePrint: الحدث قبل البدء بالطباعة. Workbook_AfterSave: الحدث بعد إتمام حفظ الملف. Workbook_SheetActivate: الحدث عند تنشيط (فتح) الصفحة (ورقة عمل معينة). Workbook_NewSheet: الحدث عند إضافة ورقة عمل جديدة. Workbook_SheetChange: الحدث عند تعديل ورقة عمل. Workbook_BeforeClose: الحدث قبل إغلاق ملف الإكسل. Workbook_Open: الحدث عند فتح ملف الإكسل. أهم الأحداث المرتبطة بورقة العمل Worksheet_Activate: الحدث عند تنشيط الصفحة. Worksheet_BeforeDelete: الحدث قبل تنفيذ حذف الصفحة. Worksheet_Change: الحدث عند إجراء تغييرات على الصفحة. Worksheet_Deactivate: الحدث عند إلغاء تنشيط الصفحة (الانتقال إلى صفحة أخرى). خاتمة لا تقل الأحداث أهميةً عن الإجراءات التي تعلمناها سابقًا، فلكلٍ منها عملٌ مخصص ضمن الشيفرات، كما تساعد كثيرًا في صناعة البرمجيات بصورة أفضل ومضبوطة أكثر في نفس الوقت، وتوجد الكثير من الأحداث الأخرى التي سنتطرق إليها، والتي سنشرح آلية عملها وقت الحاجة إليها أثناء العمل. اقرأ أيضًا المقال السابق: استخدام أدوات مايكروسوفت إكسل لتنسيق الخلايا عن طريق الشيفرات VBA استخدام الشيفرات لتنفيذ العمليات الأساسية في مايكروسوفت إكسل VBA التفاعل مع المستخدم في مايكروسوفت إكسل باستخدام VBA الحلقات التكرارية في مايكروسوفت إكسل باستخدام VBA العمليات الشرطية في مايكروسوفت إكسل باستخدام VBA مدخل إلى البرمجة باستخدام VBA في مايكروسوفت إكسل
  2. يمكنك الحصول على الشيفرة الخاصة توجد بهذا المقال في cumulative.py في مستودع ThinkStats2 على GitHub. حدود دوال الكتلة الاحتمالية تؤدي دوال الكتلة الاحتمالية probability mass functions -أو PMFs اختصارًا- وظيفتها بصورة جيدة عندما يكون عدد القيم صغيرًا، لكن كلما ازداد عدد القيم، أصبح الاحتمال المرتبط بكل قيمة أصغر ويزداد أثر الضجيج العشوائيّ. قد نرغب بدراسة توزيع أوزان الأطفال الخدّج في بيانات المسح الوطني لنمو الأسرة NSFG على سبيل المثال، حيث يسجِّل المتغير totalwgt_lb وزن الطفل عند ولادته مقدرًا بالرطل، ويُظهر الشكل التالي دالة الكتلة الاحتمالية لأوزان الأطفال الأوائل والأطفال الآخرين -أي جميع الأطفال الذين يولدون بعد الطفل الأول للأم ذاتها- عند الولادة. يوضِّح الشكل السابق دالة الكتلة الاحتمالية لأوزان المواليد، كما يظهر هذا الشكل وجود حدود على دوال الكتلة الاحتمالية، إلا أنه لا يمكن موازنتها بصريًا. تشبه هذه التوزيعات عمومًا شكل الجرس كما هو الحال في التوزيع الطبيعي normal distribution مع توضُّع الكثير من القيم بالقرب من المتوسط mean، وتوضُّع قلة قليلة من القيم في كلا الطرفين -أي أعلى وأخفض من المتوسط بكثير-، ولكن يصعب تفسير بعض هذه أجزاء هذا الشكل، إذ توجد العديد من القمم -الارتفاعات- والانخفاضات بالإضافة إلى بعض الفروقات الواضحة بين التوزيعات، إلا أنه من الصعب معرفة إن كانت هذه الميزات ذات معنى، ومن الصعب أيضًا رؤية الأنماط العامة، مثل ما هو برأيك التوزيع الذي يحتوي على المتوسط الأعلى؟ يمكن تخفيف هذه المشاكل عن طريق تصنيف binning البيانات وهي تقسيم مجالات القيم إلى مجالات غير متداخلة بالإضافة إلى حساب عدد القيم في كل مجال مصنَّف bin. قد تكون عملية تصنيف مفيدة، إلا أنّ الحصول على الحجم الصحيح للمجالات المصنَّفة ليس بالأمر السهل، فإذا كان حجمها كبيرًا بما يكفي لتخفيف الضجيج، فستتسبب في تنعيم معلومات مفيدة. تُعَدّ دوال التوزيع التراكمي cumulative distribution functions -أو CDF اختصارًا- حلًا بديلًا يجنبنا هذه المشاكل، وسيكون هذا المفهوم موضوع دراسة المقال الحالي. سنستهل حديثنا بشرح مفهوم المئين percentiles قبل البدء بشرح دوال التوزيع التراكمي CDFs. المئين percentiles إذا خضعت لامتحان موحد سابقًا، فمن المرجح أن تكون تلقيت نتيجتك على صورة مجموع صافي ورتبة مئين percentile rank، وتكون رتبة المئين في سياقنا هذا هي نسبة الأشخاص الذين حصلوا على مجموع أقل من المجموع الذي حصلت عليه أو مجموع يساويه، فإذا كنت في رتبة المئين التسعين، فهذا يعني أنّ أداءك في الامتحان كان بمستوى أو أفضل من تسعين بالمئة ممن خضعوا لهذا الامتحان. إليك طريقة حساب رتبة المئين لقيمة معيّنة (ويمثلها المتغيّر your_score) بالنسبة للقيم في متسلسلة sequence التي تحوي الدرجات scores: def PercentileRank(scores, your_score): count = 0 for score in scores: if score <= your_score: count += 1 percentile_rank = 100.0 * count / len(scores) return percentile_rank إذا كانت الدرجات في متسلسلة على سبيل المثال هي 55 و66 و77 و88 و99، وكانت نتيجتك هي 88، فستكون رتبة المئين هي 80 والتي تحصل عليها عن طريق المعادلة التالية: 100‎ * 4 / 5. فإذا كانت لديك قيمة معيّنة ،فسيكون حساب رتبة المئين سهل للغاية، إلا أنّ العملية العكسية أصعب بقليل، فإذا أُعطيت رتية مئين وتريد إيجاد القيمة الموافقة لها فسيكون أحد الخيارات المتاحة لديك هي ترتيب القيم، ومن ثم البحث عن القيمة التي تريدها كما في هذه الشيفرة التالية: def Percentile(scores, percentile_rank): scores.sort() for score in scores: if PercentileRank(scores, score) >= percentile_rank: return score نتيجة هذا الحساب هي المئين percentile، فالمئين الخمسين مثلًا هو القيمة ذات رتبة المئين percentile rank الخمسين، ويكون المئين الخمسين في توزيع درجات الامتحان هو 77. لا يُعَدّ تنفيذ دالة Percentile في الشيفرة السابقة فعالًا، إلا أنه توجد طريقة أفضل، وهي استخدام رتبة المئين percentile rank من أجل حساب فهرس المئين الموافق كما يلي: def Percentile2(scores, percentile_rank): scores.sort() index = percentile_rank * (len(scores)-1) // 100 return scores[index] قد يكون التمييز بين "المئين" percentile و"رتبة المئين" percentile rank أمرًا محيّرًا خاصةً أن الناس لا يستخدِمون المصطلحات بدقة دائمًا، ويمكننا القول باختصار تأخذ الدالة PercentileRank قيمة على أساس وسيط لها وتحسب رتبة المئين percentile rank لها في مجموعة من القيم؛ أما الدالة Percentile فهي تأخذ ترتيبًا مئيني وتحسب القيمة الموافقة له. دوال التوزيع التراكمي والآن بعد شرح مفهومي المئين ورتبة المئين أصبحنا جاهزين لتناول موضوع دالة التوزيع التراكمي Cumulative distribution function -أو CDF اختصارًا-، وهي دالة تحوِّل القيمة إلى ترتيب مئين percentile rank. تُعَدّ دالة التوزيع التراكمي دالةً لـ x، بحيث تكون x هي أيّ قيمة موجودة في التوزيع، ولتقييم دالة التوزيع التراكمي CDF(‎x‎)‎ لقيمة محدَّدة x علينا حساب نسبة القيم الموجودة في التوزيع والتي هي أقل أو تساوي القيمة x. لدينا فيما يلي الدالة التي تأخذ وسيطين هما sample وهو متسلسلة sequence وx القيمة التي لدينا: def EvalCdf(sample, x): count = 0.0 for value in sample: if value <= x: count += 1 prob = count / len(sample) return prob تشبه هذه الدالة دالة PercentileRank إلى حد التطابق تقريبًا، إلا أنّ الفرق الوحيد هو أنّ نتيجة دالة EvalCdf احتمالية في المجال 0-1، في حين تكون نتيجة PercentileRank هي رتبة مئين في المجال 0-100. لنستعرض مثالًا عن الفكرة السابقة، حيث لدينا عيّنة تحوي القيم [5, 3, 2, 2, 1]، وإليك قيم من دالة التوزيع التراكمي الخاصة بهذه العيّنة: CDF(0)=0 CDF(1)=0.2 CDF(2)=0.6 CDF(3)=0.8 CDF(4)=0.8 CDF(5)=1 يمكننا تخمين دالة التوزيع التراكمي لأيّ قيمة للمتغير x، أي ليس فقط للقيم الموجودة في العيّنة، فإذا كانت قيمة x أقل من أصغر قيمة موجودة في العيّنة، فإن دالة التوزيع التراكميّ هي 0 أي CDF(x)=0؛ أما إذا كانت قيمتها أعلى من أكبر قيمة، فستساوي دالة التوزيع التراكمي للمتغيّر x الواحد أي CDF(x)=1. يوضِّح الشكل التالي مثالًا عن دالة توزيع تراكمي CDF. يُعَدّ الشكل السابق تمثيلًا رسوميًا لدالة التوزيع التراكمي هذه، كما تُعَدّ دالة التوزيع التراكمي الخاصة بعيّنة دالة خطوة step function. تمثيل دوال التوزيع التراكمي تزوّدنا مكتبة thinkstats2 بصنف class يدعى Cdf ويمثِّل دوال التوزيع التراكمي، وفيما يلي التوابع الأساسية التي يزودنا بها صنف Cdf: (x)Prob: بفرض لدينا قيمة x، فإن هذا التابع يحسب الاحتمال p=CDF(x) (p)Value: بفرض أنه لدينا قيمة احتمال p، فسيحسب هذا التابع القيمة الموافقة x، أي أنّها دالة التوزيع التراكمي العكسية inverse CDF للاحتمال p. يوضِّح الشكل التالي دالة التوزيع التراكمي لمدة الحمل. يمكن أن يأخذ باني Cdf قائمةً list من القيم على أساس وسيط أو سلسلة بانداز pandas Series، أو Hist، أو Pmf، أو Cdf آخر. تمثِّل الشيفرة التالية Cdf لتوزيع احتمالي لمدة الحمل في المسح الوطني لنمو الأسرة: live, firsts, others = first.MakeFrames() cdf = thinkstats2.Cdf(live.prglngth, label='prglngth') يزوّدنا thinkplot بدالة تدعى Cdf ترسم دوال التوزيع التراكمي على صورة أسطر: thinkplot.Cdf(cdf) thinkplot.Show(xlabel='weeks', ylabel='CDF') يُظهر الشكل السابق النتيجة، وإحدى طرق قراءة دالة توزيع تراكمي هي المئينات. فمثلًا، يبدو أنّ 10% من حالات الحمل استمرت فترةً تقل عن 36 أسبوع، بينما لم تتعدى 90% من الحالات مدة 41 أسبوع. تزوّدنا دالة التوزيع التراكمي بتمثيل رسومي لشكل التوزيع، وتكون القيم الشائعة -التي تظهر عادةً- على شكل أجزاء حادة أو رأسية من دالة التوزيع التراكمي، حيث يكون المنوال mode في هذا المثال وضوحًا 39 أسبوع، كما يوجد عدد قليل من القيم أقل من 30 أسبوع، لذا تكون دالة التوزيع التراكمي في هذا المجال منبسطة flat إنّ التعوّد على دوال التوزيع التراكميّ ليس بالمهمة السهلة، فهي تحتاج إلى وقت طويل، لكن حالما تصبح مألوفةً بالنسبة لك ستجد أنها تزودنا بمعلومات أكثر من دوال الكتلة الاحتمالية PMFs وبوضوح أكبر منها أيضًا. موازنة دوال التوزيع التراكمي تتجلى فائدة دوال التوزيع التراكمي بصورة خاصة في موازنة التوزيعات. فمثلًا، إليك الشيفرة التي ترسم دالة التوزيع التراكمي لأوزان الأطفال الأوائل والأطفال الآخرين عند الولادة: first_cdf = thinkstats2.Cdf(firsts.totalwgt_lb, label='first') other_cdf = thinkstats2.Cdf(others.totalwgt_lb, label='other') thinkplot.PrePlot(2) thinkplot.Cdfs([first_cdf, other_cdf]) thinkplot.Show(xlabel='weight (pounds)', ylabel='CDF') يوضِّح الشكل السابق دالة التوزيع التراكمي لأوزان الأطفال الأوائل وبقية الأطفال، كما يُظهر النتيجة، ويمكن أن نلاحظ بالموازنة مع الشكل الأول في هذا المقال أن شكل التوزيعات والفرق بينهما تظهر بوضوح أكبر في الشكل السابق، كما نلاحظ أيضًا أن التوزيع يُظهر أنّ وزن الأطفال الأوائل أخف، بالإضافة إلى وجود تباين أكبر فوق المتوسط mean. الإحصائيات المبنية على أساس المئين يصبح حساب المئينات ورتبة المئين عمليةً سهلةً عند حساب دالة التوزيع التراكمي، ويزوّدنا الصنف Cdf بالتابعين التاليين: PercentileRank(x)‎: يحسب هذا التابع رتبة المئين لقيمة معيّنة x بالشكل: 100 CDF(x) Percentile(p)‎: يحسب هذا التابع القيمة الموافقة x لرتبة مئين p وهي تساوي Value(p/100)‎. يمكن استخدام التابع Percentile لحساب إحصائية موجزة مبينة على أساس المئين. فالمئين الخمسين مثلًا هو القيمة التي تقسم التوزيع إلى النصف، ويُعرف أيضًا باسم الوسيط median الذي يُعَدّ مقياسًا للنزعة المركزية لتوزيع معيّن مثل المتوسط mean. توجد في الواقع عدة تعريفات لمفهوم "الوسيط" ولكل منها خصائص مختلفة عن الآخر، إلا أنه من السهل حسابه عن طريق Percentile(50) وهي طريقة فعالة وبسيطة. يُعدّ الانحراف الربيعي interquartile range -أو IQR اختصارًا- إحصائيةً مبنيةً على أساس المئين، وهو مقياس للانتشار في توزيع معيّن، أي أنّ الانحراف الربيعي هو الفرق بين المئين الخامس والسبعين والمئين الخامس والعشرين. يُستخدَم المئين عمومًا لتلخيص شكل التوزيع، فغالبًا ما يكون توزيع الدخل مثلًا على صورة نقاط التجزيء الخمسية quintiles أي مقسومة عند المئينات العشرين والأربعين والستين والثمانين؛ أما التوزيعات الأخرى فتُقسم إلى 10 نقاط أعشارية deciles. يُدعى هذا النوع من الإحصائيات التي تمثل النقاط التي تبعد عن بعضها مسافات متساوية في دالة توزيع تراكمي نقاط التجزيء quantiles، ويمكنك زيارة الرابط لمزيد من المعلومات. الأعداد العشوائية لنفترض أننا اخترنا عيّنةً عشوائيةً من مجموعة الولادات الحية وننظر إلى رتبة المئين لأوزان الأطفال عند الولادة، ومن ثم لنفترض أننا حسبنا دالة التوزيع التراكمي لرتب المئين، برأيك كيف سيبدو التوزيع؟ إليك كيفية حسابه، لكن في البداية يجب إنشاء دالة التوزيع التراكمي Cdf لأوزان الولادات: weights = live.totalwgt_lb cdf = thinkstats2.Cdf(weights, label='totalwgt_lb') ومن ثم نولِّد عيّنةً ونحسب رتبة المئين لكل قيمة في العيّنة: sample = np.random.choice(weights, 100, replace=True) ranks = [cdf.PercentileRank(x) for x in sample] بحيث تكون sample عيّنةً عشوائيةً لأوزان 100 ولادة مُختارة مع الاستبدال replacement، أي أنّه يمكن اختيار القيمة نفسها أكثر من مرة، وكذلك تكون ranks قائمةً list من رتب المئين. يمكننا الآن إنشاء ورسم دالة التوزيع التراكمي لرتب المئين: rank_cdf = thinkstats2.Cdf(ranks) thinkplot.Cdf(rank_cdf) thinkplot.Show(xlabel='percentile rank', ylabel='CDF') يوضَّح الشكل السابق دالة التوزيع التراكمي لرتب المئين لعينة عشوائية من أوزان الولادات، كما يُظهر النتيجة، بحيث تكون دالة التوزيع التراكمي خطًا مستقيمًا أي أنّ التوزيع موحَّد. قد لا يكون هذا الخرج واضحًا إلا أنّه نتج بسبب طريقة تعريف دالة التوزيع التراكمي، حيث يُظهر هذا الشكل أن 10% من العيّنة هي تحت المئين العاشر، و20% من العيّنة تحت رتبة المئين العشرين، وهكذا، كما كنا متوقعين. لذا يكون توزيع رتب المئين موحَّدًا بعض النظر عن شكل دالة التوزيع التراكمي، وتُعَدّ هذه الخاصية مفيدةً لأنها أساس لخوارزمية بسيطة وفعالة تولِّد أعداد عشوائية في حال وجود دالة توزيع تراكمي، وذلك عن طريق اتباع الطريقة التالية: اختر رتبة مئين بصورة موحَّدة من المجال 0-100. استخدِم Cdf.Percentile من أجل إيجاد القيمة الموجودة في التوزيع والتي توافق رتبة المئين التي اخترتها. يزوّدنا Cdf بتنفيذ لهذه الخوارزمية ويدعى Random: # class Cdf: def Random(self): return self.Percentile(random.uniform(0, 100)) كما يزودنا Cdf بـ Sample التي تأخذ عددًا صحيحًا n، وتُعيد قائمةً عدد عناصرها n وقيمها مُختارة عشوائيًا من Cdf. موازنة رتب المئين تفيد رُتب المئين percentile ranks في موازنة المقاييس في مجموعات مختلفة، إذ يُجمع الأشخاص الذين يشاركون في سباقات الركض مثلًا حسب العمر والجنس عادةً، كما يمكنك تحويل وقت السباق إلى رُتب مئين لموازنة الأشخاص في المجموعات العمرية المختلفة. شارك آلان بي داوني Allen B. Downey في سباق جيمس جويس رامبل بطول مسار 10 كيلومتر في ديدهام في ماساتشوستس منذ عدة سنوات، وكان في ترتيب 97 من أصل 1633، أي تغلّب على أو تعادل مع 1633 عدّاء، وهذا يعني أنّ رتبة المئين الخاصة به في الملعب كانت 94%. يمكننا عمومًا حساب رتبة المئين باستخدام الموقع وحجم الملعب: def PositionToPercentile(position, field_size): beat = field_size - position + 1 percentile = 100.0 * beat / field_size return percentile كانت مرتبته 26 من أصل 256 في فئته العمرية التي كان يُشار إليها بـ M4049، والتي تعني ذّكر male بين عمري 40 و49، وبالتالي كانت رتبة المئين الخاصة بي في فئته العمرية هي 90%، وإذا لم يتوقف عن الركض بعد 10 سنوات -وهو يأمل ذلك-، فسيصبح في فئة M5059. لنفترض الآن أنّ رتبة المئين الخاصة به في الفئة العمرية هذه لم تتغير، كم سأكون أبطأ حينها؟ يمكننا الإجابة عن هذا السؤال عن طريق تحويل رتبة المئين في الفئة M4049 إلى موقع في الفئة M5059، وإليك الشيفرة التي تعبر عن ذلك: def PercentileToPosition(percentile, field_size): beat = percentile * field_size / 100.0 position = field_size - beat + 1 return position كان هناك 171 شخصًا في الفئة M5059، لذا يجب أن يكون في المكان السابع عشر أو الثامن عشر ليكون لديه رتبة المئين نفسها، كما أنّ زمن انتهاء العدّاء رقم 17 في الفئة M5059 كان 46:05، أي يجب أن يصل بهذا الوقت لكي يحافظ على رتبة المئين. تمارين يمكنك البدء بـ chap04ex.ipynb من أجل التمارين التالية، علمًا أنّ الحل الخاص بنا موجود في ملف chap04soln.ipynb في مستودع ThinkStats2 على GitHub حيث ستجد كل الشيفرات والملفات المطلوبة. تمرين 1 كم كان وزنك عندما ولدت؟ إذا لم يكن لديك الجواب فبإمكانك الاتصال بوالدتك أو أحد آخر يملك الجواب، وباستخدام بيانات المسح الوطني لنمو الأسرة لكل الولادات الحية؛ احسب توزيع أوزان الولادات، ومن ثم استخدمه لحساب رتبة المئين الخاصة بك. إذا كنت الطفل الأول في عائلتك، فاحسب رتبة المئين الخاصة بك في توزيع الأطفال الأوائل، وإلا فاستخدم توزيع الأطفال الآخرين. وإذا كنت في المئين التسعين أو أكثر، فاتصل بوالدتك واعتذر إليها. تمرين 2 من المفترض أن تكون الأعداد التي تولِّدها random.random موحَّدةً بين 0 و1، أي يجب أن يكون لكل قيمة في المجالالاحتمال نفسه. ولِّد 1000 عدد باستخدام الدالة random.random وارسم دالة الكتلة الاحتمالية ودالة التوزيع التراكمي لها. هل وجدت أنّ التوزيع موحَّد؟ المفاهيم الأساسية رتبة المئين percentile rank: تشير إلى النسبة المئوية للقيم الموجودة في توزيع معيّن والتي تساوي قيمة محدَّدة أو أصغر منها. المئين percentile: القيمة المرتبطة برتبة مئين معطاة. دالة التوزيع التراكمي cumulative distribution function -أو CDF اختصارًا-: دالة تحوِّل القيم إلى احتمالاتها التراكمية، حيث أنّ CDF(x) هو نسبة القيم من العيّنة التي إما تساوي x أو أصغر منها. دالة التوزيع التراكمي العكسية inverse CDF: دالة تحوِّل الاحتمال التراكمي p إلى القيمة الموافقة له. الوسيط median: وهو المئين الخمسون -أي 50th percentile-، وغالبًا ما يُستخدَم على أساس مقياس للنزعة المركزيّة central tendency. الانحراف الربيعي interquartile range: الفرق بين المئين الخامس والسبعين 75th percentile والمئين الخامس والعشرين 25th percentile، ويُستخدَم على أساس مقياس للانتشار. نقطة التجزيء quantile: متسلسلة من القيم التي توافق الترتيبات المئينة التي تبعد عن بعضها مسافات متساوية، فنقاط التجزيء لتوزيع ما هي المئين رقم 25 والمئين رقم 50 والمئين رقم 75. الاستبدال replacement: خاصية من خواص عملية اختيار العيّنات sampling، حيث يشير المصطلح "مع استبدال" إلى أنه يمكن استخدام القيمة نفسها أكثر من مرة، كما يشير مصطلح "بدون استبدال" إلى أنه حالما تُختار القيمة فإنها تُحذَف من المجموعة السكانية. ترجمة -وبتصرف- للفصل Chapter 4 Cumulative distribution functions analysis من كتاب Think Stats: Exploratory Data Analysis in Python. اقرأ أيضًا المقال السابق: دوال الكتلة الاحتمالية في جافاسكريبت دوال الكتلة الاحتمالية في جافاسكريبت التوزيعات الإحصائية في بايثون
  3. يحتوي برنامج الإكسل على مئات الدوال التي نحتاجها في حياتنا في مختلف المجالات، مثل دوال النصوص ودوال الرياضيات ودوال الإحصاء ودوال قواعد البيانات، وكذلك دوال التاريخ والوقت. سنتحدث في هذا المقال عن مجموعة من دوال التاريخ والوقت في الإكسل. ولاستيعاب وفهم الدوال بسهولة، سنطبِّق المثال التالي بالإجابة على مجموعة من الأسئلة: ما هو تاريخ اليوم؟ ما هو تاريخ اليوم مع التوقيت الآن؟ ما هو الشهر الحالي من السنة؟ ما هو تاريخ اليوم من الشهر؟ ما هي السنة الحالية؟ هل يمكن تحديد قِيَم يوم وشهر وسنة، ومن ثم الحصول على قيمة تاريخ كاملًا في خلية واحدة بصيغة تاريخ؟ هل يمكن الحصول على قيمة تاريخ مستقبلي؟ على سبيل المثال: ما هو التاريخ بعد شهرين من اليوم؟ ما هو اليوم من أيام الأسبوع؟ هل يمكن تحديد قِيَم الساعة والدقيقة والثواني والحصول على قيمة الساعة كاملةً مع تحديد هل هي صباحًا أم مساءً؟ هل يمكن حساب عدد أيام عملك عن طريق تحديد تاريخ بدء عملك ونهايته؟ للإجابة على الأسئلة السابقة، سنستخدم دوال التاريخ والوقت. أدرج الجدول التالي في ورقة العمل في الإكسل، بعد ذلك نسِّقها، وابدأ بتطبيق الخطوات لكل دالة للحصول على النتائج. أساسيات التعامل مع دوال التاريخ والوقت بدايةً يجب أن تعلم بعض أساسيات التعامل مع دوال التاريخ والوقت في الإكسل، وهي: التأكد من أن صيغة الخلية التي تحتوي على تاريخ هي صيغة "تاريخ" وليست صيغة "عام"، وذلك من خيارات تنسيق الأرقام في تبويب الصفحة الرئيسية. يمكنك تغيير طريقة عرض التاريخ باختيار "تنسيقات إضافية للأرقام" من خيار "تنسيق الأرقام". ضمن فئة "التاريخ"، تأكد من اختيار التقويم الميلادي من خيار "نوع التقويم"، وبإمكانك تغييره إذا أردت استخدام التقويم الهجري. كما يمكنك اختيار الصيغة المناسبة للتاريخ مثل اختيار تاريخ قصير مشابه لـ 7/19/2021، أو تاريخ طويل مثل 19 يوليو 2021. كذلك بإمكانك اختيار عرض التوقيت أو إخفائه، إلى جانب عرض خيارات الصباح am والمساء pm أو إخفاؤها. يتعامل الإكسل مع أيام الأسبوع من القيمة 1 حتى القيمة 7، حيث يَعُد الإكسل يوم "الأحد" هو اليوم رقم "1" في الأسبوع، بينما يُعَد يوم "السبت" هو اليوم الأخير في الأسبوع وقيمته "7". حيث: يتعامل الإكسل مع الشهور من القيمة 1 حتى القيمة 12ـ أي أن شهر يناير هو شهر 1، وشهر ديسمبر هو شهر 12. يتعامل الإكسل مع أيام الشهر من القيمة 1 حتى القيمة 31. يتعامل الإكسل مع الساعات من القيمة 0 حتى القيمة 23. يتعامل الإكسل مع الدقائق من القيمة 0 حتى القيمة 59. يتعامل الإكسل مع الثواني من القيمة 0 حتى القيمة 59. سنبدأ الآن بتطبيق المثال باستخدام دوال التاريخ والوقت، فللحصول على تاريخ اليوم كاملًا (اليوم والشهر والسنة)، أو جزء منه مثل الحصول على اليوم فقط أو الشهر فقط، سنستخدم دوال التاريخ المختلفة. أدرج دالةً من تبويب إدراج واختر فئة "التاريخ والوقت" للوصول إلى كل دوال التاريخ والوقت. 1. دالة today تُرجِع هذه الدالة التاريخ الحالي بصيغة تاريخ، فهي لا تأخذ أي قيم، بل تكتفي فقط بإرجاع التاريخ الحالي. وهنا اكتب الدالة في خلية النتيجة ولاحظ النتيجة التي ستظهر، فإذا كان التاريخ الذي نطبِّق فيه المثال هو 19 يوليو 2021، فستظهر النتيجة بهذا التاريخ. وأنت ستُظهر النتيجة لديك بالتاريخ الذي تطبِّق فيه المثال. يمكنك تغيير التاريخ من تاريخ قصير إلى تاريخ طويل، وذلك من إعدادات التاريخ، حيث ستحصل على 19 يوليو 2021 بدلًا من 7/19/2021. 2. دالة now تُرجِع هذه الدالة التاريخ والوقت الحاليين بتنسيق الوقت والتاريخ. لاحظ أن هذه الدالة لا تستقبل أي قِيَم، بل تُرجع قيمة التاريخ والوقت فقط. يمكنك تخصيص الناتج للحصول فقط على الوقت وإخفاء التاريخ، كما يمكنك إضافة خيار عرض ص (صباحًا) أو م (مساءً)، وذلك من قائمة تنسيق الأرقام – فئة مخصص. 3. دوال month وday وyear للحصول على تاريخ اليوم من حيث الشهر فقط (أي: يوليو)، أو اليوم فقط (أي: 19)، أو السنة فقط (أي: 2021)؛ استخدِم دالة month للحصول على الشهر ودالة day للحصول على اليوم ودالة year للحصول على السنة. تستقبل هذه الدوال التاريخ، وتُرجع القيمة المطلوبة سواءً كانت يومًا أو شهرًا أو سنة. يمكنك تطبيق هذه الدالة على أي تاريخ في ورقة العمل وليس فقط على تاريخ اليوم، مثل التاريخ 31 يناير 2022، ويمكنك الحصول على السنة فقط بتحديد التاريخ داخل الدالة فتصبح كالتالي: 4. دالة date هذه الدالة تعمل عكس الدوال السابقة، أي أنها تستقبل ثلاث قِيَم (اليوم والشهر والسنة)، وتُرجع قيمة التاريخ بصيغة تاريخ. بينما الدوال الثلاثة السابقة day وmonth وyear تستقبل التاريخ وتُرجع القيمة (اليوم أو الشهر أو السنة). سنعطي هذه الدالة قيمة 19 لليوم، وقيمة 7 للشهر (أي: يوليو)، وقيمة 2021 للسنة. وللحصول على التاريخ كاملًا في خلية واحدة بصيغة تاريخ؛ تأخذ الدالة القيم بالترتيب التالي (إجباريًّا)، ولا يمكن تغيير الترتيب وإلَّا ستحصل على تاريخ خاطئ: القيمة الأولى: السنة القيمة الثانية: الشهر القيمة الثالثة: اليوم 5. دالة edate تُستخدم هذه الدالة للحصول على تاريخ مستقبَلي، وذلك بتحديد عدد الشهور التي تريد إضافتها على التاريخ. وتأخذ هذه الدالة قيمتين أولاهما هي التاريخ المحدد، أما الثانية فهي عدد الشهور التي نريد إضافتها. ستلاحظ أن النتيجة هي تاريخ 19/9/2021 وهو تاريخ اليوم مُضافًا إليه شهران. لقد طبَّقنا في المثال السابق إضافة شهرين على تاريخ اليوم، حيث يمكنك إضافة شهور على تاريخ محدد مسبقًا في ورقة العمل وليس فقط على تاريخ اليوم، كما يمكنك تحويل التاريخ لتاريخ طويل إذا كنت ترغب بعرض اسم الشهر واليوم من أيام الأسبوع. 6. دالة weekday تُستخدم دالة weekday لتحديد رقم اليوم من أيام الأسبوع. وبما أن اليوم الإثنين فإن نتيجة الدالة ستكون الرقم 2 كما ذكرنا مسبقًا، لأن الإكسل يَعُد يوم الأحد هو اليوم الأول من أيام الأسبوع ورقمه 1، وبالتالي فإن يوم الإثنين رقمه 2. يمكننا تحويل الرقم السابق (2) إلى نص (الإثنين) باستخدام الدالة النصية text. إذا كنت تستخدم التاريخ باللغة الإنجليزية، فستحصل على النتيجة التالية Mon: إذا غيَّرت صيغة الدالة، واستبدلت ddd بـ dddd أي أربعة حروف بدلًا من ثلاثة، فستحصل على كلمة Monday كاملة لا مختصرة. 7. دالة time سنحدد القِيَم التالية في صورة أرقام، على النحو: الساعة الثالثة، والدقيقة الثلاثون، وخمسة وخمسون ثانية. يمكننا الحصول على الساعة بالضبط حسب القِيَم السابقة وذلك باستخدام دالة time، حيث تستقبل هذه الدالة ثلاث قِيَم بالترتيب وهي الساعة والدقيقة والثانية، وهنا سنحصل على الساعة 03:30 ص. 8. دالة days تُستخدم هذه الدالة لإرجاع عدد الأيام بين تاريخين، وعليه تستقبل الدالة تاريخين على الترتيب، تاريخ النهاية وتاريخ البداية وتحسب عدد الأيام بين هذين التاريخين. فلنفترض أنك تريد حساب عدد أيام عملك، وكان تاريخ بداية عملك 1 يناير 2021 بينما تاريخ نهاية عملك 31 مارس 2021. طبِّق الدالة على تاريخي البداية والنهاية، حيث ستجد أن عدد أيام عملك 89 يومًا، وهنا غيِّر تاريخ البداية وتاريخ النهاية لقِيَم أخرى ولاحظ الفرق. لقد تعرَّفنا في هذه المقالة على مجموعة من الدوال الخاصة بالتاريخ والوقت في الإكسل، مثل دالة الحصول على التاريخ الحالي today ودالة الحصول على التاريخ والوقت الحاليين now، وكذلك دوال الحصول على اليوم والشهر والسنة وأيام الأسبوع من تاريخ محدد، وهي day وmonth وyear وweekday على التوالي. كما تعلَّمنا كيفية الحصول على التاريخ كاملًا إذا كانت لدينا القِيَم المختلفة لليوم والشهر والسنة وذلك باستخدام دالة date. وتعلَّمنا أيضًا كيفية تحديد تاريخ مستقبَلي موازنةً بتاريخ معين، وبإمكاننا تحويل القيمة العددية لأيام الأسبوع لقيمة نصية عربية أو إنجليزية باستخدام الدالة النصية text، كما تعرَّفنا على دالة days والتي تحسب عدد الأيام بين تاريخين وذلك بتحديد تاريخ البداية وتاريخ النهاية. اقرأ أيضًا دوال التاريخ الأساسية في اكسل دوال الوقت الأساسية في اكسل كيفية تطبيق خاصية التحقق من صحة البيانات على الخلايا في اكسل 7 دوال لتطبيق إجراءات مختلفة على النصوص في اكسل
  4. كوتلن (Kotlin) هي لغة برمجة مخصّصة لمنصّة جافا الافتراضية (Java Virtual Machine أو اختصارًا JVM)، الأندرويد، والمتّصفح. تنتمي للغات statically typed (التي تفحص الأنواع وقت الترجمة). وهي متوافقة مع جافا 100%. // (//)التعليقات على سطر واحد تبدأ بـ/* التعليقات المؤلفة من عدة أسطر تبدو كهذه */ تعمل الكلمة المفتاحية package بنفس طريقة جافا package com.learnxinyminutes.kotlin نقطة الإدخال لبرامج Kotlin هي دالة(تابع) تسمى main ،يمرر التّابع مصفوفة تحتوي على وسطاء arguments لسطر الأوامر. fun main(args: Array<String>) { التّصريح عن القيم يتم باستخدام إما var أو val ،تصريح val لا يمكن إعادة تعيينه، في حين يمكن ذلك في var val fooVal =10 لا يمكننا لاحقًا إعادة تعيين قيمة fooVal لقيمة أخرى. var fooVar =10 fooVar =20 من الممكن إعادة تعيين قيمة fooVar. في أغلب الحالات، يمكن لكوتلن أن تحدّد ما هو نوع المتغير، لذلك لا داعي لتحديده صراحة في كل مرّة. يمكننا أن نصرّح بنوع المتغير بوضوح كالتالي: val foo:Int=7 يمكن تمثيل السلاسل بطريقة مماثلة في java. يتم الهروب باستخدام backslash. val fooString ="My String Is Here!" val barString ="Printing on a new line?\nNo Problem!" val bazString ="Do you want to add a tab?\tNo Problem!" println(fooString) println(barString) println(bazString) تُحدد السلسلة الخام string raw باستخدام triple quote ("""). ويمكن أن تحتوي أسطر جديدة و أية محارف أخرى. val fooRawString =""" fun helloWorld(val name :String){ println("Hello, world!")}""" println(fooRawString) السلاسل ممكن أن تحتوي تعابير القالب template expressions. تبدأ تعابير القالب بالرمز $. val fooTemplateString ="$fooString has ${fooString.length} characters" println(fooTemplateString)// => My String Is Here! has 18 characters من أجل المتغيرات التي تحوي قيمة فارغة null يجب تحديد ذلك صراحة nullable. يمكن تحديد المتغير قابلاً للقيمة null بإلحاق? بنوعه. ويمكننا الوصول إلى المتحولات القابلة لـ null باستخدام مُعامل التشغيل? ،ويمكننا استخدام عامل التشغيل :? لتحديد قيمة بديلة للاستخدام إذا كان المتغير فارغ null. var fooNullable:String?="abc" println(fooNullable?.length)// => 3 println(fooNullable?.length ?:-1)// => 3 fooNullable = null println(fooNullable?.length)// => null println(fooNullable?.length ?:-1)// => -1 يمكن التصريح عن الدوال باستخدام الكلمة المفتاحية fun. وتحدد وسطاء الدالة بين قوسين بعد اسم الدالة. ويمكن لوسطاء الدالة اختياريًا الاحتواء على قيمة افتراضية. ويحدّد نوع إرجاع الدالة، إذا لزم الامر بعد المعطيات. fun hello(name:String="world"):String{return"Hello, $name!"} println(hello("foo"))// => Hello, foo! println(hello(name ="bar"))// => Hello, bar! println(hello())// => Hello, world! يمكن لبارامتر الدالة أن يوسم بالكلمة المفتاحية vararg للسماح بتمرير عدد متغير من المعطيات إلى الدالة. fun varargExample(vararg names:Int){ println("Argument has ${names.size} elements")} varargExample()// => لا يحوي الوسيط أية عناصر varargExample(1)// => يحوي الوسيط عنصر واحد varargExample(1,2,3)// => يحوي الوسيط 3 عناصر عندما تتكوّن الدّالة من تعبير واحد فقط، يمكن حذف الأقواس المنحنية { } ويتم تحديد الجسم(العملية) بعد رمز = fun odd(x:Int):Boolean= x %2==1 println(odd(6))// => false println(odd(7))// => true إذا كان نوع الاسترجاع يمكن استنتاجه فلسنا بحاجة لتحديده. fun even(x:Int)= x %2==0 println(even(6))// => true println(even(7))// => false الدوال يمكن أن تأخذ دوال كوسطاء وترجع دالة. fun not(f:(Int)->Boolean):(Int)->Boolean{return{n ->!f.invoke(n)}} يمكن للدوال المسمّاة أن تحدّد كوسائط باستخدام معامل التشغيل val notOdd = not(::odd) val notEven = not(::even) يمكن تحديد تعابير Lambda كوسائط val notZero = not {n -> n ==0} إذا احتوت lambda على بارامتر واحد فقط يمكن حذف التصريح عنه (جنبًا إلى جنب مع<- )وسيكون اسم البارامتر الوحيد it. val notPositive = not {it >0}for(i in 0..4){ println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")} تستخدم الكلمة المفتاحية class للتّصريح عن الأصناف. class ExampleClass(val x:Int){ fun memberFunction(y:Int):Int{return x + y } infix fun infixMemberFunction(y:Int):Int{return x * y }} لإنشاء حالة instance جديدة نستدعي الباني. ولاحظ أن Kotlin لا تحوي الكلمة المفتاحية new. val fooExampleClass =ExampleClass(7) يمكن استدعاء دوال المستخدم باستخدام التنويت النقطي . println(fooExampleClass.memberFunction(4))// => 11 إذا وُسمت الدالة بالكلمة المفتاحية infix يمكن عندها أن يستدعى باستخدام التنويت الداخلي println(fooExampleClass infixMemberFunction 4)// => 28 أصناف البيانات Data classes هي طريقة مختصرة لإنشاء الأصناف التي تحتوي بيانات فقط وتنشئ دوال hashCode،equals و toString تلقائيًا. data classDataClassExample(val x:Int, val y:Int, val z:Int) val fooData =DataClassExample(1,2,4) println(fooData)// => DataClassExample(x=1, y=2, z=4) أصناف البيانات لديها دالة copy val fooCopy = fooData.copy(y =100) println(fooCopy)// => DataClassExample(x=1, y=100, z=4) يمكن أن تفكَك الكائنات Objects في متغيرات متعددة val (a, b, c)= fooCopy println("$a $b $c")// => 1 100 4 التفكيك باستخدام حلقة for for((a, b, c) in listOf(fooData)){ println("$a $b $c")// => 1 100 4} val mapData = mapOf("a" to 1,"b" to 2) Map.Entry قابل للتفكيك كذلك for((key, value) in mapData){ println("$key -> $value")} الدالة with مشابهة لعبارة with في جافا data classMutableDataClassExample(var x:Int, var y:Int, var z:Int) val fooMutableData =MutableDataClassExample(7,4,9) with (fooMutableData){ x -=2 y +=2 z--} println(fooMutableData)// => MutableDataClassExample(x=5, y=6, z=8) يمكننا إنشاء قائمة (لائحة) باستخدام الدالة listOf. ستكون القائمة غير قابلة للتغيير ولا يمكن إضافة عناصر أو إزالتها. val fooList = listOf("a","b","c") println(fooList.size)// => 3 println(fooList.first())// => a println(fooList.last())// => c// index يمكن الوصول إلى عناصر القائمة من خلال فهرسها println(fooList[1])// => b يمكن إنشاء قائمة قابلة للتعديل باستخدام الدالة mutableListOf val fooMutableList = mutableListOf("a","b","c") fooMutableList.add("d") println(fooMutableList.last())// => d println(fooMutableList.size)// => 4 يمكن إنشاء تعيين set باستخدام الدّالة setOf val fooSet = setOf("a","b","c") println(fooSet.contains("a"))// => true println(fooSet.contains("z"))// => false يمكننا إنشاء خريطة map باستخدام الدّالة mapOf val fooMap = mapOf("a" to 8,"b" to 7,"c" to 9) يمكن الوصول لقيم الـ Map من خلال مفاتيحها println(fooMap["a"])// => 8 تُمثل المتتالية Sequences مجموعات تقييمها مؤجل إلى حين الحاجة lazily-evaluated collections. ويمكننا إنشاء متتالية باستخدام الدّالة generateSequence. val fooSequence = generateSequence(1,{ it +1}) val x = fooSequence.take(10).toList() println(x)// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] مثال لاستخدام متتالية لتوليد أرقام فيبانوتشي fibonacci fun fibonacciSequence():Sequence<Long>{ var a =0L var b =1L fun next():Long{ val result = a + b a = b b = result return a }return generateSequence(::next)} val y = fibonacciSequence().take(10).toList() println(y)// => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] تزوّد Kotlin دوال عالية الرتبة للعمل مع المجموعات collections val z =(1..9).map{it *3}.filter {it <20}.groupBy {it %2==0}.mapKeys {if(it.key)"even"else"odd"} println(z)// => {odd=[3, 9, 15], even=[6, 12, 18]} يمكن استخدام حلقة for مع أي شيء يؤمن التكرار. for(c in "hello"){ println(c)} تعمل حلقات while بشكل مشابه لعملها في اللغات الأخرى var ctr =0while(ctr <5){ println(ctr) ctr++}do{ println(ctr) ctr++}while(ctr <10) يمكن استخدام if كتعبير يرجع القيم. لهذا السبب ليس هناك حاجة لمعامل التشغيل :? في Kotlin. val num =5 val message =if(num %2==0)"even"else"odd" println("$num is $message")// => 5 is odd يمكن استخدام when كبديل لسلاسل if-else if val i =10 when { i <7-> println("first block") fooString.startsWith("hello")-> println("second block")else-> println("else block")} يمكن استخدام when مع المعطيات. when (i){0,21-> println("0 or 21") in 1..20-> println("in the range 1 to 20")else-> println("none of the above")} يمكن استخدام when كدالة ترجع القيم. var result = when (i){0,21->"0 or 21" in 1..20->"in the range 1 to 20"else->"none of the above"} println(result) يمكننا التحقق فيما إذا كان الكائن object من نوع محدّد باستخدام معامل التشغيل is. إذا مرر الكائن فحص النوع type check عندها يمكن استخدام هذا النوع دون الموائمة بشكل صريح. fun smartCastExample(x:Any):Boolean{if(x is Boolean){ X توائم أوتوماتيكيًا إلى Boolean return x }elseif(x is Int){ X توائم أوتوماتيكيًا إلى Int return x >0}elseif(x is String){ X توائم أوتوماتيكيًا إلى String return x.isNotEmpty()}else{returnfalse}} println(smartCastExample("Hello, world!"))// => true println(smartCastExample(""))// => false println(smartCastExample(5))// => true println(smartCastExample(0))// => false println(smartCastExample(true))// => true تعمل الموائمة الذكيّة smartcast أيضًا مع كتلة when fun smartCastWhenExample(x:Any)= when (x){ is Boolean-> x is Int-> x >0 is String-> x.isNotEmpty()else->false} الملحقات Extensions هي طريقة لإضافة وظائف جديدة للصنف class. وهي مشابهة لدوال ملحقات #C fun String.remove(c:Char):String{returnthis.filter {it != c}} println("Hello, world!".remove('l'))// => Hello, word! println(EnumExample.A)// => A println(ObjectExample.hello())// => hello أصناف Enum مشابهة لأنواع enum في جافا. enumclassEnumExample{ A, B, C } يمكن استخدام الكلمة المفتاحية object لإنشاء كائنات وحيدة. لا يمكننا تمثيلها ولكن يمكن أن نشير لحالتها الفريدة من خلال اسمها. وهذا مشابه لكائنات Scala singleton object ObjectExample{ fun hello():String{return"hello"}} fun useObject(){ObjectExample.hello() val someRef:Any=ObjectExample//كماهي تمامًا objects نستخدم أسماء الكائنات } ترجمة -وبتصرّف- للمقال Learn kotlin in Y Minutes
  5. ملاحظة: يفترض هذا الدليل أنك تستخدم مصرّف 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 Guide
  6. جافا هي لغة برمجة حاسوبية للأغراض العامة، المتزامنة، المعتمدة على الصفوف، وغرضية التوجه // يبدأ التعليق المكتوب على خط واحد ب .// /* يبدو التعليق المكتوب على عدة سطور بهذا الشكل. */ /** *تبدو التعليقات في ملفات لغة البرمجة جافا بهذا الشكل *و تستخدم لوصف الصف أو الصفات المختلفة لكائن معين. *:الصفات الرئيسية * *@author- اسم مؤلف الكود. *ويحتوي على معلومات الاتصال كالبريد الالكتروني لمؤلف الكود أو للمؤلفين. *@version- النسخة الحالية من البرنامج. *@since -الوقت الذي تم فيه إضافة هذا الجزء من البرنامج. *@param -من أجل وصف البارامترات المختلفة للمنهج (method). *@return -لوصف القيمة التي يرجعها المنهج. *@deprecated -لإظهار انتهاء صلاحية الكود أو عدم وجوب استخدامه. *@see - روابط إلى جزء آخر من المستندات */ استورد الصف ArrayList بدلاً من استيراد الرزمة java.util كلّها import java.util.ArrayList; استورد جميع الصفوف الموجود داخل الرزمة import java.security.*; يحوي أي ملف جافا على صف عام، على المستوى الخارجي، له نفس اسم الملف public class LearnJava { ليعمل برنامج جافا يجب أن يحوي على تابع رئيسي بمثابة نقطة البدء public static void main(String[] args) { الدخل/الخرج دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الخرج استخدم ()System.out.println لطباعة السطور النصيّة System.out.println("Hello World!"); System.out.println( "Integer: " + 10 + " Double: " + 3.14 + " Boolean: " + true); استخدم ()System.out.print للطباعة بدون سطر جديد System.out.print("Hello "); System.out.print("World"); استخدم ()System.out.printf لتنسيق الطباعة بسهولة System.out.printf("pi = %.5f", Math.PI); // => pi = 3.14159 الدخل استخدم scanner لقراءة الدخل، يجب استيراد الصف ;java.util.Scanner Scanner scanner = new Scanner(System.in); لقراءة السلاسل المحرفية المُدخلة String name = scanner.next(); لقراءة البايتات المُدخلة byte numByte = scanner.nextByte(); لقراءة العدد الصحيح المُدخل int numInt = scanner.nextInt(); لقراءة العدد الحقيقي المٌدخل float numFloat = scanner.nextFloat(); لقراءة العدد الحقيقي مضاعف الدقة المُدخل double numDouble = scanner.nextDouble(); لقراءة القيمة المنطقية المُدخلة boolean bool = scanner.nextBoolean(); المتغيرات التصريح عن المتغيرات يتم التصريح عن متغير باستخدام // <type> <name> int fooInt; يتم التصريح عن مجموعة من المتغيرات من نفس النوع // <type> <name1>, <name2>, <name3> int fooInt1, fooInt2, fooInt3; تهيئة المتغير يتم تهيئة متغير باستخدام // <type> <name> = <val> int barInt = 1; يتم تهيئة مجموعة من المتغيرات من نفس النوع بنفس القيمة باستخدام // <type> <name1>, <name2>, <name3> // <name1> = <name2> = <name3> = <val> int barInt1, barInt2, barInt3; barInt1 = barInt2 = barInt3 = 1; أنواع المتغير البايت (Byte) : وهو 8bit ويستخدم لترميز الأعداد الصحيحة بين -128 و 127 byte fooByte = 100; إذ كنت ترغب بتفسير البايت كعدد صحيح موجب (بدون إشارة). فإن هذه العملية البسيطة من الممكن أن تساعد int unsignedIntLessThan256 = 0xff & fooByte; هذا يناقض عمل cast الذي من الممكن أن يعطي عدد سالب int signedInt = (int) fooByte; القصير (Short): وهو 16bit ويستخدم لترميز الأعداد الصحيحة بين -32,768 و 32,767 short fooShort = 10000; الصحيح (Integer): وهو 32bit ويستخدم لترميز الأعداد الصحيحة بين -2,147,483,648 و 2,147,483,647 int bazInt = 1; الطويل (Long): وهو 64bit ويستخدم لترميز الأعداد الصحيحة بين -9,223,372,036,854,775,808 و 9,223,372,036,854,775,807 long fooLong = 100000L; يستخدم المحرف L للدلالة على أن قيمة المتحول هي من النوع Long. و أي قيمة مُسندة للمتحول بدون استخدام L هي عبارة عن عدد صحيح int بشكل افتراضي. ملاحظة: إن الأنواع byte، short، int، long، هي أنواع ذات إشارة signed. أي من الممكن أن تحوي على قيم موجبة أو قيم سالبة. لا يوجد متغيرات بقيمة موجبة فحسب. ولكن المحارف تعتبر من نوعية unsigned ذات القيمة الموجبة فقط ذات 16bit. العائم (Float): وهو ذو دقة أحادية 32 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 149-^2 و 127^2 - (23-^2-2) float fooFloat = 234.5f; يستخدم المحرف f أو F للدلالة على أن قيمة المتحول هي من النوع Float. و إلا سيعتبر المتغير من النوع الحقيقي المضاعف. المضاعف (Double): وهو ذو دقة مضاعفة 64 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 1074-^2 و 1023^2 - (52-^2-2) double fooDouble = 123.4; القيم المنطقية (Boolean) ذات القيمة: true و false boolean fooBoolean = true; boolean barBoolean = false; Char هو محرف يونيكود 16بت أحادي char fooChar = 'A'; لا يمكن أعادة تهيئة المعطيات من النوع final final int HOURS_I_WORK_PER_WEEK = 9001; ولكن من الممكن تهيئتها بعد عملية التصريح عنها final double E; E = 2.71828; BigInteger -عبارة عن نوع الأعداد الصحيحة الثابتة بمستويات دقّة مختلفة. يستخدم نوع المعطيات BigInteger للسماح للمبرمج بالتعامل مع الأعداد الصحيحة التي هي أكبر من 64-بت. حيث تُخزّن الأعداد الصحيحة في مصفوفة من البايتات، التي يمكن التلاعب بها باستخدام التوابع المبنيّة في الصف BigInteger . يمكن تهيئة المتغير من النوع BigInteger بمصفوفة من البايتات أو بمصفوفة من السلاسل المحرفية. BigInteger fooBigInteger = new BigInteger(fooByteArray); BigDecimal عبارة عن نوع الأعداد الحقيقية الثابتة بمستويات دقة مختلفة. ويأخذ المتغير من النوع BigDecimal معاملين: عدد صحيح ذو حجم غير مقّيّد، يمثل مستوى الدقّة، وعدد صحيح أخر بحجم 32 بت. يسمح النوع BigDecimal بالتحكم بتقريب الأعداد الحقيقية. عندما يكون مطلوب مستوى دقة محدد للعدد الحقيقي يُنصح باستخدام BigDecimal . من الممكن تهيئة المتغير من النوع BigDecimal بأحد الأنواع التالية: int ،long ،double ،String ،BigInteger BigDecimal fooBigDecimal = new BigDecimal(fooBigInteger, fooInt); كُن حذرًا عند استخدامك للأنواع float ,double لأن عدم الدقّة في تحديد النوع سوف تُنسخ في الـ BigDecimal ويُفضل استخدام سلسلة محرفية ثابتة عندما تحتاج إلى قيمة دقيقة. BigDecimal tenCents = new BigDecimal("0.1"); String -السلاسل المحرفية String fooString = "My String Is Here!"; الحرف الخاص n\ يُعرف بحرف السطر الجديد الذي يحرك مؤشر الكتابة إلى بداية السطر التالي. String barString = "Printing on a new line?\nNo Problem!"; الحرف الخاص t\ يُعرف بالمسافة الأفقية، يقوم بتحريك مؤشر الكتابة مسافة معينة إلى النقطة التالية في السطر. String bazString = "Do you want to add a tab?\tNo Problem!"; System.out.println(fooString); System.out.println(barString); System.out.println(bazString); بناء السلاسل المحرفية: باستخدام عامل الجمع (+) تُعتبر الطريقة الأساسية (الأمثل) للقيام ببناء السلاسل المحرفية. String plusConcatenated = "Strings can " + "be concatenated " + “via + operator.”; System.out.println(plusConcatenated); // Output: Strings can be concatenated via + operator. باستخدام الصف StringBuilder لا تشكل هذه الطريقة أية سلاسل محرفية وسيطة، فقط تقوم بتخزين قطع السلاسل المحرفية و ربطها مع بعضها عندما يتم استدعاء التابع ()toString. تلميح: لا يعتبر الصف StringBuilder إجرائية آمنة. يوجد صف بديل آمن StringBuffer (مع تأثير بسيط على الأداء). StringBuilder builderConcatenated = new StringBuilder(); builderConcatenated.append("You "); builderConcatenated.append("can use "); builderConcatenated.append("the StringBuilder class."); System.out.println(builderConcatenated.toString()); // فقط الآن تمّ بناء السلسة المحرفية // Output: You can use the StringBuilder class. تكون StringBuilder فعاله عندما السلسلة المحرفية الكاملة المبنيّة مطلوبة في نهاية عملية ما. StringBuilder stringBuilder = new StringBuilder(); String inefficientString = ""; for (int i = 0 ; i < 10; i++) { stringBuilder.append(i).append(" "); inefficientString += i + " "; } System.out.println(inefficientString); System.out.println(stringBuilder.toString()); تتطلب inefficientString عمل أكثر، حيث أنها تنتج سلسلة محرفية عند كل دورة للحلقة. يتم ترجمة تجميع السلاسل المحرفية البسيطة بالعامل (+) إلى: toString() و StringBuilder تجنب استخدام تجميع السلاسل المحرفية داخل الحلقات. لاستخدام منسق السلاسل المحرفية طريقة أخرى بديلة لتوليد السلاسل المحرفية سريعة و قابلة للقراءة. String.format("%s may prefer %s.", “Or you”, “String.format()”); // Output: Or you may prefer String.format(). المصفوفات يجب تحديد حجم المصفوفة بشكل فوري بمجرد التصريح عنها. تستخدم الصيغة التالية للتصريح عن مصفوفة. // <datatype>[] <var name> = new <datatype>[<array size>]; // <datatype> <var name>[] = new <datatype>[<array size>]; int[] intArray = new int[10]; String[] stringArray = new String[1]; boolean boolArray[] = new boolean[100]; طريقة أخرى للتصريح عن مصفوفة و تهيئتها int[] y = {9000, 1000, 1337}; String names[] = {"Bob", "John", "Fred", "Juan Pedro"}; boolean bools[] = {true, false, false}; فهرسة المصفوفة - الوصول إلى عنصر فيها System.out.println("intArray @ 0: " + intArray[0]); تبدأ المصفوفات بالفهرس (0)، و هي قابله للتغيير . intArray[1] = 1; System.out.println("intArray @ 1: " + intArray[1]) ArrayLists; // => 1 مجموعة من أنواع المعطيات الأخرى، التي تستحق التدقيق فيها: ArrayLists : مشابهة للمصفوفات، إلا أنها تحوي على وظائف إضافية، وحجمها قابل للتعديل. LinkedLists : عبارة عن تنفيذ لقائمة مترابطة بشكل مضاعف، التي تنفذ جميع العمليات التي من المتوقع لقائمة مترابطة بشكل مضاعف أن تنفذها. Maps : عبارة عن وصل كائنات المفتاح إلى كائنات القيمة. وهي عبارة عن واجهة، و بالتالي لا يمكن أن يتم تشكيل كائنات منها. يجب أن يتم تحديد نوع المفاتيح و القيم بناءً على الكائن المُشكل من الصف. من الممكن ان يُربط كل مفتاح إلى قيمة واحدة فقط، ومن الممكن أن يظهر كل مفتاح مرة واحدة فقط. (لا يوجد نسخ) لتنفيذ الخريطة/الواجهة Map hashtable : يُستخدم هذا الصف الجدول ، HashMaps وينفذ الخريطة Map ، هذا يسمح بتثبيت زمن العمليات الأساسية، مثل الحصول على عنصر أو إدخال عنصر، حتى في المجموعات الكبيرة. TreeMap: عبارة عن Map ، مصنفة حسب مفاتيحها. يُحافظ كل تعديل على ترتيبه، إما باستخدام المقارن المزود عند عملية توليد الكائن، أو باستخدام المقارنات لكل كائن، اذا كان ينفذ واجهة قابلة للمقارنة. سوف يؤدي الفشل المدمج في تنفيذ واجهة قابلة للمقارنة، مع الفشل في تزويد مقارن إلى رمي ClassCastExceptions تأخذ عمليات إدخال و حذف عناصر زمن من الدرجة ((O(log(n ، لذلك تجنب استخدام بنى المعطيات هذه، إلا أذا كنت ترغب من الاستفادة من ميزة الترتيب. العمليات System.out.println("\n->Operators"); int i1 = 1, i2 = 2; // الطريقة المختصرة للتصريح عن عدة متغيرات في نفس الوقت العمليات الحسابية بسيطة System.out.println("1+2 = " + (i1 + i2)); // => 3 System.out.println("2-1 = " + (i2 - i1)); // => 1 System.out.println("2*1 = " + (i2 * i1)); // => 2 System.out.println("1/2 = " + (i1 / i2)); // => 0 (int يعيد int/int) System.out.println("1/2.0 = " + (i1 / (double)i2)); // => 0.5 باقي القسمة System.out.println("11%3 = "+(11 % 3)); // => 2 عمليات المقارنة System.out.println("3 == 2? " + (3 == 2)); // => false System.out.println("3 != 2? " + (3 != 2)); // => true System.out.println("3 > 2? " + (3 > 2)); // => true System.out.println("3 < 2? " + (3 < 2)); // => false System.out.println("2 <= 2? " + (2 <= 2)); // => true System.out.println("2 >= 2? " + (2 >= 2)); // => true العمليات المنطقية System.out.println("3 > 2 && 2 > 3? " + ((3 > 2) && (2 > 3))); // => false System.out.println("3 > 2 || 2 > 3? " + ((3 > 2) || (2 > 3))); // => true System.out.println("!(3 == 2)? " + (!(3 == 2))); // => true العمليات على مستوى البت ~ عامل إيجاد المتمم على مستولى البتات. << عامل الإزاحة الحسابية نحو اليسار (مع الأخذ بعين الاعتبار بت الإشارة). >> عامل الإزاحة الحسابية نحو اليمين (مع الأخذ بعين الاعتبار بت الإشارة). >>> عامل الإزاحة المنطقية نحو اليمين (بدون الأخذ بعين الاعتبار بت الإشارة). & على مستوى البتّات and العملية المنطقية ^ على مستوى البتّات xor العملية المنطقية | على مستوى البتّات or العملية المنطقية عمليات الزيادة int i = 0; System.out.println("\n->Inc/Dec-rementation"); العامل ++ يقوم بالزيادة بمقدار واحد. العامل – يقوم بالإنقاص بمقدار واحد. إذا تم وضع هذين العاملين قبل المتغير، فإنهن يقومان بالزيادة أو الإنقاص، ومن ثم يعيدان قيمة المتحول الجديدة. أما إذا تم وضع هذين العاملين بعد المتغير، فإنهما يقومان بإعادة قيمة المتحول، ومن ثم يقومان بالزيادة أو الإنقاص. System.out.println(i++); // i = 1, يطبع 0 (زياده لاحقة). System.out.println(++i); // i = 2, يطبع 2 (زيادة سابقة). System.out.println(i--); // i = 1, يطبع 2 (إنقاص لاحق) System.out.println(--i); // i = 0, يطبع 0 (إنقاص سابق) بنى التحكم System.out.println("\n->Control Structures"); عبارة if مشابهة لتلك الموجودة في لغة البرمجة C int j = 10; if (j == 10) { System.out.println("I get printed"); } else if (j > 10) { System.out.println("I don't"); } else { System.out.println("I also don't"); } حلقة While int fooWhile = 0; while(fooWhile < 100) { System.out.println(fooWhile); //زيادة العدّاد //(في المجال (0،1،..99 foowhile مكررة 100 مرة ، حيث أن قيم المتغير fooWhile++; } System.out.println("fooWhile Value: " + fooWhile); حلقة Do While int fooDoWhile = 0; do { System.out.println(fooDoWhile); //زيادة العدّاد //في المجال 0-99 foowhile مكررة 99 مرة ، حيث أن قيم المتغير fooDoWhile++; } while(fooDoWhile < 100); System.out.println("fooDoWhile Value: " + fooDoWhile); حلقة For : هيكلية الحلقة على الشكل التالي: for(<start_statement>; <conditional>; <step>) مثال for (int fooFor = 0; fooFor < 10; fooFor++) { System.out.println(fooFor); // fooFor 0->9 مكررة 10 مرات، حيث } System.out.println("fooFor Value: " + fooFor); //outerيتم الخروج من الحلقة الداخلية باستخدام جملة الهروب المعنونّة بـ outer: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (i == 5 && j ==5) { break outer; //يخرج من الحلقة الخارجية أيضا، بدلاً من الخروج فقط من الحلقة الداخلية } } } حلقة For Each : قادرة على العمل مع المصفوفات و الكائنات for إن حلقة التي تنفذ الواجهات القابلة للتكرار. int[] fooList = {1, 2, 3, 4, 5, 6, 7, 8, 9}; هيكلية الحلقة على الشكل التالي: for (<object> : <iterable>) تقرأ هكذا: لكل عنصر في ال iterable ملاحظة: يجب أن يتطابق نوع الكائن مع نوع عناصر ال iterable for (int bar : fooList) { System.out.println(bar); //سوف تتكرر 9 مرات و يطبع 1-9 على سطور جديدة. } Switch تعمل Switch مع أنواع المعطيات: int، char، short ،byte وأيضًا من الممكن أن تعمل مع النوع Enum و String و مجموعة من الصفوف الخاصة التي تجمع الأنواع البدائية: Character, Byte, Short, Integer. بدءًا من جافا 7، من الممكن استخدام النوع String. ملاحظة: تذكر عدم استخدام التعليمة "break" في نهاية حالة جزئية إذا كان هناك يوجد حالة أخرى تستوفي الشرط أيضًا. int month = 3; String monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; default: monthString = "Some other month"; break; } System.out.println("Switch Case Result: " + monthString); عبارة (Try-with-resources) في Java الإصدار +7 تُستخدم في جافا عبارات Try-catch-finally، و لكن في جافا +7 أيضا ً مُتاح استخدام try-with-resources إن عبارات try-with-resources تُبسط عمل عبارات try-catch-finally عن طريق إغلاق المصادر بشكل أتوماتيكي. لاستخدام try-with-resources ضمّن كائن من صف في تعليمة try. try (BufferedReader br = new BufferedReader(new FileReader("foo.txt"))) { // يمكنك أن تجرّب شيئًا يحتاج إلى استثناء System.out.println(br.readLine()); // في جافا 7 المصادر مغلقة دومًا حتى لو كان هناك استثناء } catch (Exception ex) { //catch سوف يتم إغلاق المصدر قبل أن تنفذ عبارة System.out.println("readLine() failed."); } في هذه الحالة، لا يوجد حاجة إلى عبارة finally حيث أن BufferedReader تم إغلاقه للتو. يتم استخدام هذا لتجنب الحالات الحرجة، عندما عبارة finally لا يمكن استدعاءها. الاختزال الشرطي يمكنك استخدام العامل "?" لسرعة الأسناد . تُقرأ كما يلي: "إذا كانت العبارة صحيحة ، استخدم <أول قيمة> و إلا استخدم <القيمة الثانية> int foo = 5; String bar = (foo < 10) ? "A" : "B"; System.out.println("bar : " + bar); // "bar : A"تطبع // صحيحة (foo < 10) لأن العبارة //أو بشكل مبسط: System.out.println("bar : " + (foo < 10 ? "A" : "B")); التحويل بين أنواع المعطيات تحويل المعطيات تحويل السلاسل المحرفية إلى عدد صحيح Integer.parseInt("123");//يعيد نسخة العدد الصحيح من السلسة المحرفية "123" تحويل العدد الصحيح إلى سلسلة محرفية Integer.toString(123);//يعيد النسخة المحرفية من العدد الصحيح 123 راجع تحويل المعطيات للأنواع التالية من الصفوف: Double Long String الصفوف و التوابع للتصريح عن كائن من الصف Bicycle استخدم الكلمة المفتاحية new Bicycle trek = new Bicycle(); استدعاء توابع الكائن trek.speedUp(3); // setter , getter يجب عليك استخدام توابع الواضع و الآخذ trek.setCadence(100); يُعيد التابع ()toString التمثيل النصي للكائن. System.out.println("trek info: " + trek.toString()); التهيئة مزدوجة الأقواس لا تملك جافا تراكيب لتشكيل مجموعات ساكنة من المعطيات بطريقة سهلة عادةً يتم استخدام الطريقة التالية: private static final Set<String> COUNTRIES = new HashSet<String>(); static { COUNTRIES.add("DENMARK"); COUNTRIES.add("SWEDEN"); COUNTRIES.add("FINLAND"); } ولكن يوجد طريقة أنيقة لإنجاز الشيء نفسه بشكل أسهل، تُدعى هذه الطريقة مزدوجة الأقواس private static final Set<String> COUNTRIES = new HashSet<String>() {{ add("DENMARK"); add("SWEDEN"); add("FINLAND"); }} يولد القوس الاول { } صف داخلي بدون اسم AnonymousInnerClass ويعرّف القوس الثاني{ } كتلة مهيئ الحالة instance. يتم استدعاء هذه الكتلة، عندما يتم توليد الصف الداخلي لا يعمل هذا فقط للمجموعات، إنما يعمل أيضًا لكل الصفوف غير النهائي non-final يمكنك تضمين صفوف أخرى خارجية غير عامة في ملف جافا ، ولكنها ليست بممارسة جيدة. بدلاً من ذلك يفضّل فصل الصفوف في ملفات جافا منفصلة صيغة التصريح عن صف: // <public/private/protected> class <class name> { // } حقول البيانات, أنماط الباني, الوظائف كلها في الداخل تستدعى الوظائف كالدوال في جافا class Bicycle { حقول و متغيرات الصف Bicycle Public: يمكن الوصول إليه من أي مكان public int cadence; Private: يمكن الوصول إليه فقط من داخل الصف private int speed; Protected: يمكن الوصول إليه فقط من داخل الصف و الصفوف الفرعية protected int gear; default: يمكن الوصول إليه فقط من داخل الرزمة. String name; Static متغير صف ساكن static String className; البلوك الساكن Static لا تملك جافا تنفيذ للبواني الساكنة، ولكن جافا تملك كتل برمجية لتعريف متغيرات الصف (المتغيرات لساكنة) سيتم استدعاء هذا البلوك عند تحميل الصف. static { className = "Bicycle"; } الباني هي الطريقة التي يتم فيها توليد الصفوف. هذا هو الباني public Bicycle() { //يمكنك أيضا استدعاء باني آخر: // this(1, 50, 5, "Bontrager"); gear = 1; cadence = 50; speed = 5; name = "Bontrager"; } هذا عبارة عن باني ذو معاملات public Bicycle(int startCadence, int startSpeed, int startGear, String name) { this.gear = startGear; this.cadence = startCadence; this.speed = startSpeed; this.name = name; } صيغة الدالة // <public/private/protected> <return type> <function name>(<args>) أصناف الجافا غالبًا ما تنفّذ getters وsetters في حقولها صيغة التصريح عن الدالة // <access modifier> <return type> <method name>(<args>) public int getCadence() { return cadence; } دوال void لا تحتاج تصريح عودة public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void speedUp(int increment) { speed += increment; } public void slowDown(int decrement) { speed -= decrement; } public void setName(String newName) { name = newName; } public String getName() { return name; } دالة لتوليد قيم الصفات لهذا الكائن @Override // موروثة من الصف لهذا الكائن. public String toString() { return "gear: " + gear + " cadence: " + cadence + " speed: " + speed + " name: " + name; } الصف PennyFarthing عبارة عن صنف فرعي من Bicycle class PennyFarthing extends Bicycle { -Penny Farthings //هي عباره عن نوع من الدراجات بعجلات أمامية كبيرة. public PennyFarthing(int startCadence, int startSpeed) { // super استدعي الباني الأب باستخدام الكلمة المفتاحية super(startCadence, startSpeed, 0, "PennyFarthing"); } يجب عليك تعليم المنهج الذي تُعيد كتابته بـ annotation@. @Override public void setGear(int gear) { this.gear = 0; } Object casting بما أن الصف PennyFarthing يرث الصف Bicycle : فمن الممكن القول أن: Bicycle هو PennyFarthing، و يمكن أن نكتب: // Bicycle bicycle = new PennyFarthing(); هذا يُدعى"object casting" حيث يتم تشكيل كائن من كائن آخر. الواجهات صيغة التصريح عن واجهة // <access-level> interface <interface-name> extends <super-interfaces> { // // Constants // // Method declarations // } مثال - الطعام: public interface Edible { public void eat(); //يجب على أي صف ينفذ هذه الواجهة // ان ينفذ هذه الدالة } public interface Digestible { public void digest(); // منذ جافا 8، من الممكن أن تملك الواجهات دالة افتراضية. public default void defaultMethod() { System.out.println("Hi from default method ..."); } } الآن يمكنك تشكيل صف يُنفذ كلا من هاتين الواجهتين. public class Fruit implements Edible, Digestible { @Override public void eat() { // ... } @Override public void digest() { // ... } } في جافا، يمكنك أن تمدد(ترث) صف واحد فقط، ولكن يمكنك أن تنفذ عدّة واجهات . على سبيل المثال: public class ExampleClass extends ExampleClassParent implements InterfaceOne, InterfaceTwo { @Override public void InterfaceOneMethod() { } @Override public void InterfaceTwoMethod() { } } الصفوف التجريدية صيغة التصريح عن صف تجريدي: // <access-level> abstract class <abstract-class-name> extends // <super-abstract-classes> { // // Constants and variables // // Method declarations // } لا يمكن تشكيل كائنات من الصفوف المجردة (تمثيل الصفوف المجردة) يمكن للصفوف المجردة أن تعرف عن دوال مجرّدة. المناهج المجردة لا تحوي على جسم ويجب تعليمها بالكلمة المفتاحية abstract . يجب على الصفوف الأبناء غير المجردة أن تعيد كتابة Override@ جميع الدوال المجردة في صفوفها العليا. من الممكن أن تكون الصفوف المجرّدة مفيدة عند الجمع بين منطق التكرار و السلوك المخصص، ولكن بما أن الصف المجرد يتطلب الوراثة، فإن الصفوف المجردة تنتهك منطق "التركيب عبر الوراثة". لذلك خُذ بعين الاعتبار الطرق الأخرى التي تستخدم هذا التركيب public abstract class Animal { private int age; public abstract void makeSound(); من الممكن أن يحوي المنهج جسم public void eat() { System.out.println("I am an animal and I am Eating."); //(private)ملاحظة: من الممكن هنا الوصول للمتغيرات الخاصة age = 30; } public void printAge() { System.out.println(age); } يمكن أن تحوي الصفوف المجردة على دالة رئيسية public static void main(String[] args) { System.out.println("I am abstract"); } } class Dog extends Animal { لاحظ بأنه لا يزال هناك حاجة لإعادة كتابة الدوال التجريدية في الصف التجريدي // @Override public void makeSound() { System.out.println("Bark"); // age = 30; ==> ERROR! // Animal خاص بالنسبة للصف age خطأ- و ذلك لأن المتغير } ملاحظة: من الممكن أن تحصل على خطأ إذا قمت هنا باستخدام Override@ لأن جافا لا تسمح لك بإعادة كتابة الدوال الساكنة. ما يحدث هنا يُدعى إخفاء الدالة METHOD HIDING public static void main(String[] args) { Dog pluto = new Dog(); pluto.makeSound(); pluto.eat(); pluto.printAge(); } الصفوف النهائية final class صيغة التصريح عن الصفوف النهائية // <access-level> final <final-class-name> { // // Constants and variables // // Method declarations // } الصفوف النهائية هي عبارة عن الصفوف التي لا يمكن وراثتها، (أي هي الولد النهائي). بطريقة ما، تُعد الصفوف النهائية معاكس الصفوف المجردة. لأنه يجب توسيع (وراثة) الصفوف المجردة، أما الصفوف النهائية لا يمكن توسيعها. public final class SaberToothedCat extends Animal { لاحظ بأنه ما يزال يوجد حاجة لإعادة كتابة الدوال المجردة في الصف المجرد.// @Override public void makeSound() { System.out.println("Roar"); } } الدوال النهائية public abstract class Mammal() { // صيغة الدالة النهائية: // <access modifier> final <return type> <function name>(<args>) // لا يمكن إعادة كتابة الدوال النهائية بواسطة صف ابن، // وبالتالي الدالة النهائية هو أخر تنفيذ للدالة . public final boolean isWarmBlooded() { return true; } } النوع Enum еnum هي طريقة تنظيمية للثوابت (Constants) في الكود بحيث تجمع الثوابت التي لها علاقة ببعضها تحت فئة واحدة بطريقة تنظم الوصول إليها .بما أن هذه القيم هي عبارة عن ثوابت، لذلك فإن أسماء الحقول من النوع enum يجب أن تكتب بحروف كبيرة. في لغة جافا يتم تعريف هذا النوع باستخدام الكلمة المفتاحية enum. لتعريف متحول عن يوم من أيام الأسبوع، باستخدام النوع enum : public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } من الممكن استخدام متغيرنا Day من النوع enum كما يلي: public class EnumTest { //Enumالمتغير من النوع Day day; public EnumTest(Day day) { this.day = day; } public void tellItLikeItIs() { switch (day) { case MONDAY: System.out.println("Mondays are bad."); break; case FRIDAY: System.out.println("Fridays are better."); break; case SATURDAY: case SUNDAY: System.out.println("Weekends are best."); break; default: System.out.println("Midweek days are so-so."); break; } } public static void main(String[] args) { EnumTest firstDay = new EnumTest(Day.MONDAY); firstDay.tellItLikeItIs(); // => Mondays are bad. EnumTest thirdDay = new EnumTest(Day.WEDNESDAY); thirdDay.tellItLikeItIs(); // => Midweek days are so-so. } } الأنواع enum هي فعالة بشكل أكبر مما تم توضيحه فوق. من الممكن أن يحتوي جسم المتغير enum على دوال و حقول أخرى. ترجمة -وبتصرّف- للمقال Learn Java in Y Minutes
×
×
  • أضف...