لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 10/13/21 في كل الموقع
-
لدي ال plot التالي: import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 1000) y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, np.sin(x), '--b', label ='Sine', color="r",ls='solid') plt.title('Matplotlib xticklabels Example') plt.show() والخرج: لكن أريد تغيير حجم التسميات (labels) للمحور الأفقي وتدويرها، كيف يمكن القيام بذلك؟2 نقاط
-
يُعتبر DevOps بمثابة زواج بين إدارتين متنافستين منذ الأزل: التطوير "Development" وإدارة العمليات/التنفيذ "Operations". فمنذ عُرفت الإدارة بشكلها الحديث، عانت فرق التطوير وفرق التنفيذ من ضعف التواصل والتعاون، لما؟ لأن لدى كل منها هدف يتعارض مع هدف الأخرى: ففي حين ترغب فرق إدارة العمليات برؤية المنتج مكتملاً بأعلى جودة وبأقل قدر ممكن من الموارد، تُصرّ فرق التطوير على التأكد من إنجاز العمل بالكامل وبجودة عالية، بغض النظر عن الموارد اللازمة لتحقيق النجاح. تكمن المفارقة في كون هذا التعارض لا يؤدي لتخصيص الموارد بشكل مناسب ولا لإنجاز العمل بالجودة المطلوبة! وهنا يأتي دور DevOps. تُستخدَم كلمة DevOps (اختصارٌ للكلمتين Development و Operations) لوصف مجموعةٍ من أنشطة التكنولوجيا الحديثة التي تسعى إلى تقريب مطوري البرمجيات وموظفي العمليات من بعضهم بعضًا بشكلٍ أوثق بغية العمل بصورة أكثر تعاونية على نفس المشروع. تهدف DevOps لخلق بيئة تطوير تدعم التكامل المتواصل والتسليم المستمر. وذلك من خلال أربعة مبادئ رئيسية تقوم عليها DevOps: التكامل المتواصل (CI: Continuous integration): هو مصطلح خاص بهندسة البرمجيات يصف أسلوبًا في تطوير البرمجيات يتسم بتواصل عملية إعادة بناء تطبيق ما مصحوبة باختباره بصفة متكررة التسليم المتواصل (CD: Continuous delivery): هو منهج يعتمده مهندسو البرمجيات في تطوير البرامج ضمن دورات قصيرة، ويهدف إلى تسريع بناء واختبار وإطلاق البرامج الاختبار المتواصل (Continuous Testing): هي عملية تنفيذ الاختبارات الآلية كجزء من خط أنابيب تسليم البرامج للحصول على تعليقات فورية حول مخاطر العمل المرتبطة بإصدار البرنامج المراقبة المتواصلة (Continuous Monitoring): وهي العملية المسؤولة عن مراقبة البرنامج أو الميزة الجديدة بحثًا عن الأخطاء والأمان والتوافق. بإمكانك الإطلاع على هذه المقالات للتوسع أكثر في مجال ال DevOps و أخذ فكرة شاملة عنه: ما المقصود بـ DevOps؟ ما هي الغاية من DevOps؟ ثلاث خطوات لتأمين DevOps مفتوح المصدر أفضل 5 لغات برمجة لـ DevOps رحلة المطور عبر DevOps لماذا تعد DevOps الاستراتيجية التقنية الأكثر أهمية و هناك عدة مقالات أخرى في المجال يُمكنك الوصول لها من القائمة العُلوية تحت قسم : دروس و مقالات > DevOps.2 نقاط
-
يستعمل الـ CROSS JOIN عادة لإنشاء ,إنطلاقا من جدولين, توليفة مزدوجة من كل صف من الجدول الأول مع كل صف من الجدول الثاني . أي إن كان الجدول الأول يحوي المعلومات : +++++++++++ |STUDENTS | |---------| |AHMED | |OMAR | |YOUNESS | +++++++++++ و الجدول الثاني المعلومات : +++++++++++ |CARS | |---------| |BMW | |MERCEDES | |HONDA | +++++++++++ فإن إستعمال الـ CROSS JOIN الذي سيقوم بتشكيل : سطر لأحمد بمحاذاة سطر BMW . // // // // MERCEDES . // // // // HONDA . و أيضا : سطر لعمر بمحاذاة سطر BMW . // // // // MERCEDES . // // // // HONDA . و أيضا : سطر ليونس بمحاذاة سطر BMW . // // // // MERCEDES . // // // // HONDA . بمعنى أنه سيتبع منطقا كالتالي : / BMW / |OMAR ---- MERCEDES \ \ HONDA / BMW / |AHMED --- MERCEDES \ \ HONDA / BMW / |YOUNESS --- MERCEDES \ \ HONDA لينتج لنا مجموعة النتائج التالية : ++++++++++++++++++++++ |STUDENTS | CARS | |---------|----------| |AHMED |BMW | |AHMED |MERCEDES | |AHMED |HONDA | | | | |OMAR |BMW | |OMAR |MERCEDES | |OMAR |HONDA | | | | |YOUNESS |BMW | |YOUNESS |MERCEDES | |YOUNESS |HONDA | ++++++++++++++++++++++ يشبه هذا جداء مجموع في الجبر , إذ يتم إستعمال النشر المزدوج كطريقة لتحليل عبارة جبرية تتضمن جداء مجموع , مثال : (a + c )( b + d ) = ab + ad + cb + cd سوى أن النتائج في الـ CROSS JOIN يتم نشرها كصفوف بنتيجة الإستعلام . السياق العام لها هو ما كالتالي : SELECT ColumnName_1, ColumnName_2, ColumnName_N FROM [Table_1] CROSS JOIN [Table_2] أو يكفي : SELECT ColumnName_1, ColumnName_2, ColumnName_N FROM [Table_1],[Table_2] و يتم استخدامه بغرض إنشاء مجموعة من كافة مجموعات العناصر بجدول ما بقاعدة البيانات ، مثل تشكيل مجموعات منازل ذات ألوان و أحجام معينة , هذا مع إمكانية التحكم في عناصر هاته المجموعات لونا أو حجما : select size, color from sizes CROSS JOIN colors ++++++++++++++++++++++ |SIZE | COLOR | |---------|----------| |XS |RED | |XS |BLUE | |XS |GREY | | | | |XM |RED | |XM |BLUE | |XM |GREY | | | | |XL |RED | |XL |GREY | |XL |BLUE | ++++++++++++++++++++++ فلو أردنا مثلا إستثناء المنازل الحمراء , فسنتثني اللون الأحمر فقط ليتم إستثناء كل التوليفات المشكلة من حجم X و لون أحمر .. : و هكذا . أو ربما تريد جدولاً يحتوي على صف لكل دقيقة في اليوم ، وتريد استخدامه للتحقق من تنفيذ إجراء ما كل دقيقة ، لذلك يمكنك تجاوز ثلاثة جداول : select hour, minute from hours CROSS JOIN minutes ++++++++++++++++++++++ |HOUR | MINUTE | |---------|----------| |13:00 |00 | |13:00 |01 | |13:00 |02 | |13:00 |03 | . . . . . . |21:00 |55 | |21:00 |56 | |21:00 |57 | |21:00 |58 | . . . . . . ++++++++++++++++++++++ فهو يخلق مجموعة إحتمالات غير مكررة ,بإعتبار أن الأعمدة تحمل قيما فريدة, إبتداءا من مجموعات محدودة من العناصر . يمكن أن يكون هذا عمليا جدا في عمليات مثل إختبار التطبيق أو بذر قواعد البيانات ببيانات تجريبية .2 نقاط
-
ملاحظات: عندما نريد 16 بت للعدد نضع أصغر افي الجزء الأيسر للتمثيل الثنائي للعدد لنضمن عدد الخانات عملية تحويل العدد للنظام الثانئي تتمثل باستخراج باقي القسمة لهذا العدد على العدد 2 وتكرار الخطوة لناتج القسمة على 2 لنفس العدد المتمم الأحادي هو مقلوب تمثيل العدد الثنائي أي نبدل كل 0 ب 1 والعكس 1 ب 0 المتمم الثاني هو 1 + المتمم الأحادي تحويل عدد عشري لثائي: بداية الحلقة نأخذ باق القسمة على 2 للعدد ونضع القيم من اليمين لليسار نقسم العدد على 2 نكرر الحلقة مثال لتحويل الرقم 64 من عشري لثنائي من 16 خانة: 64 = 0000000001000100 المتمم الأحادي، كل بت نقلب قيمته: 64 = 0000000001000100 64 متمم = 1111111110111011 المتمم الثنائي ينتج عن المتمم الأحادي بجمع 1 64 متمم أولي = 1111111110111011 + 0000000000000001 64 متمم ثانوي = 1111111110111111 حاولي نفس الخطوات2 نقاط
-
حاولت فهم وتجميع معلومات عن مجال DevOps ولكن لم أنجح بذلك، أريد تبسيط للمفهوم مع عرض تعداد للمجالات التي يفيد تطبيقه فيها1 نقطة
-
1 نقطة
-
لدي صورة وأريد رسم مستطيل على ال regions of interest في الصورة الخاصة بي، كيف يمكنني القيام بذلك؟1 نقطة
-
هل يمكن لأحد ما أن يوضح لي ما يقوم به التابع cvWaitKey فهو غير واضح أبداً في المرجع؟1 نقطة
-
تحيه طيبه للجميع اقوم بجلب صور متعدده من للاستوديو وعرضها للمستخدم الان يمكن للمستخدم جلب عدد لا محدود من الصور انا احتاج الى ان اعمل قيد او حد لعدد الصور الذي يمكن للمستخدم جلبها وختيارها مثلا اربع صور فقط لا يمكن ادراج صورة اكثر انا استعمل المكتبه التاليه : image_picker // Pick multiple images final List<XFile>? images = await _picker.pickMultiImage(); الكود كامل : import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Image Picker Demo', home: MyHomePage(title: 'Image Picker Example'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { List<XFile> _imageFileList; set _imageFile(XFile value) { _imageFileList = value == null ? null : [value]; } dynamic _pickImageError; bool isVideo = false; final ImagePicker _picker = ImagePicker(); void _onImageButtonPressed(ImageSource source, {BuildContext context, bool isMultiImage = false}) async { if (isMultiImage) { try { final pickedFileList = await _picker.pickMultiImage( maxWidth: 66, maxHeight: 66, imageQuality: 66, ); if (_imageFileList == null) { if (pickedFileList == null) return; setState(() { _imageFileList = pickedFileList; }); } else { if (pickedFileList != null) { setState(() { _imageFileList = [..._imageFileList, ...pickedFileList].toSet().toList(); }); } // لا تغيير } } catch (e) { setState(() { _pickImageError = e; }); } } } Widget _previewImages() { if (_imageFileList != null) { return Semantics( child: ListView.builder( key: UniqueKey(), itemBuilder: (context, index) { return Semantics( label: 'image_picker_example_picked_image', child: kIsWeb ? Image.network(_imageFileList[index].path) : Image.file(File(_imageFileList[index].path)), ); }, itemCount: _imageFileList.length, ), label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', textAlign: TextAlign.center, ); } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); } } Future<void> retrieveLostData() async { final LostDataResponse response = await _picker.retrieveLostData(); if (response.isEmpty) { return; } if (response.file != null) { if (response.type == RetrieveType.video) { isVideo = true; } else { isVideo = false; setState(() { _imageFile = response.file; _imageFileList = response.files; }); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android ? FutureBuilder<void>( future: retrieveLostData(), builder: (BuildContext context, AsyncSnapshot<void> snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: case ConnectionState.waiting: return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); case ConnectionState.done: return _previewImages(); default: if (snapshot.hasError) { return Text( 'Pick image/video error: ${snapshot.error}}', textAlign: TextAlign.center, ); } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); } } }, ) : _previewImages(), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Padding( padding: const EdgeInsets.only(top: 16.0), child: FloatingActionButton( onPressed: () { isVideo = false; _onImageButtonPressed( ImageSource.gallery, context: context, isMultiImage: true, ); }, heroTag: 'image1', tooltip: 'Pick Multiple Image from gallery', child: const Icon(Icons.photo_library), ), ), ], ), ); } } ياليت اذا احد سبق وحصلت معه نفس هذا المشكله يفيدنا1 نقطة
-
من المفترض ان يقوم مطور الحزمة باضافة هذا الميزة ولا الحزمة ستعاني من مشكله + نقص لخصائص اساسيه بها كل الشكر لك اخي الكريم واعتذر على الاطاله1 نقطة
-
_imageFileList = _imageFileList != null ? (_imageFileList.length > 4 ? _imageFileList.take(4)) : _imageFileList,1 نقطة
-
يمكنك القيام بذلك من خلال invert_xaxis كالتالي: x = [1, 6, 9, 5,7] y = [3,8,4,15,9] plt.scatter(x,y) # عكس المحور plt.gca().invert_xaxis() plt.title('Matplotlib Reverse Example') plt.show() الخرج: وبشكل مشابه بالنسبة للمحور العمودي يمكنك استخدام invert_yaxis: plt.gca().invert_yaxis() أو من خلال set_xlim لضبط الحدود بالشكل الذي نريده كالتالي: x = [1, 6, 9, 5,7] y = [3,8,4,15,9] plt.scatter(x,y) ax = plt.gca() ax.set_xlim(ax.get_xlim()[::-1]) plt.title('Matplotlib Reverse Example') plt.show() حيث نمرر لها get_xlim للحصول على الامتداد الحالي للمحور ثم نقوم بعكسه من خلال [::-1]. أما بالنسبة للمحور العمودي ففقط نبدل إلى set_ylim و get_ylim.1 نقطة
-
itemCount: _imageFileList !=null ? _imageFileList.length.clamp(0, 4) : _imageFileList.length.clamp(0, _imageFileList.length),1 نقطة
-
لتغيير حجمها يمكنك استخدام tick_params كالتالي: import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 1000) y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, np.sin(x), '--b', label ='Sine', color="r",ls='solid') # x تغيير حجم الخط للمحور الأفقي ax.tick_params(axis='x', which='major', labelsize=20) plt.title('Matplotlib xticklabels Example') plt.show() والخرج: حيث أن axis تمثل المحاور التي نريد تطبيق التغيرات عليها ويأخذ القيم {'x', 'y', 'both'}. أما which فكما نعلم قد يكون هناك ticks أساسية وثانوية فهنا من خلال هذا الوسيط أنت تحدد فيما إذا كنت تريد تطبيق التغيرات على العلامات الريسية أم الثانوية أم كلاهما {'major', 'minor', 'both'}. والأهم labelsize الذي يحدد الحجم. أما بالنسبة لتدوير ال tick_params فيمكنك استخدام plt.setp التي تمنحك التحكم الكامل بعملية التدوير وبالدرجة التي تريدها من خلال الوسيط rotation كالتالي: import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 1000) y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, np.sin(x), '--b', label ='Sine', color="r",ls='solid') # x تغيير حجم الخط للمحور الأفقي ax.tick_params(axis='x', which='major', labelsize=20) # عملية التدوير plt.setp(ax.xaxis.get_majorticklabels(), rotation=270) # تدوير 270 درجة plt.title('Matplotlib xticklabels Example') plt.show() الخرج: حيث نقوم بتمرير درجة التدوير التي نحتاجها مثلاً قمنا بتدويرها 270 درجة لجعلها عمودية، وأيضاً يجب أن نمرر ax.xaxis.get_majorticklabels كوسيط أول للدالة والتي تمثل كل ال ticklabels للمحور الأفقي. أما إذا أردنا تطبيق ذلك على المحور y ففقط نبدل المحور الذي سيتم تطبيق التدوير عليه أي تصبح القيمة الممررة لأول وسيط في الدالة هي ax.yaxis.get_majorticklabels. أي: import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 1000) y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, np.sin(x), '--b', label ='Sine', color="r",ls='solid') # x تغيير حجم الخط للمحور الأفقي ax.tick_params(axis='x', which='major', labelsize=20) # عملية التدوير plt.setp(ax.yaxis.get_majorticklabels(), rotation=270) # تدوير 270 درجة plt.title('Matplotlib xticklabels Example') plt.show() وأخيراً إذا كان لديك ticklabels ثانوية نمرر للوسيط الأول القيمة ax.youraxix.get_minorticklabels. أي: import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 1000) y = np.sin(x) fig, ax = plt.subplots() ax.plot(x, np.sin(x), '--b', label ='Sine', color="r",ls='solid') # x تغيير حجم الخط للمحور الأفقي ax.tick_params(axis='x', which='major', labelsize=20) # عملية التدوير plt.setp(ax.yaxis.get_minorticklabels(), rotation=270) # تدوير 270 درجة plt.title('Matplotlib xticklabels Example') plt.show() وكذلك يمكنك تدويرها من خلال set_rotation كالتالي: for tick in ax.get_xticklabels(): tick.set_rotation(270) كذلك بالنسبة للحجم: for tick in ax.xaxis.get_major_ticks(): tick.label.set_fontsize(20)1 نقطة
-
أريد رفع قاعدة البيانات لدي من نوع PostgreSQL إلى الاستضافة ماهي الخطوات التي يجب أن أقوم بها؟1 نقطة
-
خطوات رفع قاعدة بيانات من نوع PostgreSQL إلى الإستضافة كالتالي: تصدير قاعدة البيانات: يمكنك تصدير قاعدة بيانات PostgreSQL إلى ملف باستخدام برنامج سطر الأوامر pg_dump ، أو يمكنك استخدام phpPgAdmin: الطريقة الأولى: إستخدام برنامج pg_dump: لتصدير قاعدة بيانات PostgreSQL باستخدام برنامج pg_dump ، اتبع الخطوات التالية: قم بالوصول إلى سطر الأوامر على الحاسوب حيث يتم تخزين قاعدة البيانات. على سبيل المثال ، إذا كانت قاعدة البيانات موجودة على حساب استضافة ويب آخر أو مع مزود استضافة ويب آخر ، فقم بتسجيل الدخول إلى الحساب باستخدام SSH. إذا كان لديك وصول مادي إلى الكمبيوتر ، فيمكنك فتح DOS أو نافذة طرفية للوصول إلى سطر الأوامر. اكتب الأمر التالي ، ثم اضغط على Enter. استبدل username باسم المستخدم الخاص بك ، واستبدل dbname باسم قاعدة البيانات التي تريد تصديرها: pg_dump -U username dbname > dbexport.pgsql سيُطلب منك كلمة المرور قم بإدخالها ثم إضغط Enter و بعد ذلك سيتم تصدير قاعدة البيانات إلى الملف dbexport.pgsql الطريقة الثانية: إستخدام phpPgAdmin بعد فتح البرنامج قم بالضغط على إسم قاعدة البيانات التي تريد تصديرها. من القائمة العُلوية إضغط على export تحت Format إضغط على Structure and data. من الخيارات إضغط على قائمة الإختيار و حدد SQL إضغط على تحميل ثم حدد مسار الحفظ ثم إضغط على زر save. إنشاء قاعدة بيانات PostgreSQL على إستضافتك و إسناد مستخدم لها. سجل دخولك إلى حسابك على الإستضافة من قسم Databases إضغط على PostgreSQL Databases من قسم إنشاء قاعدة بيانات ادخل الإسم الذي تريده في حقل الإسم إضغط على زر الإنشاء بعد ذلك إضغط على زر go back تحت قسم Add User to Database من قائمة المستخدمين حدد المستخدم الذي تريد ربطه بقاعدة البيانات التي أنشأتها. من قائمة قواعد البيانات حدد قاعدة البيانات التي أنشأتها. إضغط على زر submit إستيراد قاعدة البيانات يُمكنك إستيراد قاعدة البيانات إلى إستضافتك بإستخدام احدى الطريقتين إما عن طريق إستخدام سطر الأوامر بإستخدام الأمر psql أو إستخدام برنامج phpPgAdmin بعد تسجيل الدخول إلى حسابك على الإستضافة عن طريق بروتوكول SSH يُمكنك تنفيذ الأمر: psql -U username dbname < dbexport.pgsql حيث نقوم بتعويض username بإسم المستخدم و dbname بإسم قاعدة البيانات التي نريد الإستيراد فيها و dbexport.pgsql بإسم الملف الموجود الذي صدرناه سابقاً. الطريقة الثانية هي عن طريق إستخدام برنامج phpPgAdmin المثبت على الإستضافة بعد تسجيل الدخول إلى حسابك إفتح البرنامج، حدد قاعدة البيانات التي تريد الإستيراد فيها ثم من القائمة العلوية حدد SQL ثم choose file إضغط عليه لتحديد الملف من حاسوبك في الأخير إضغط على execute بعدها سيتم إستيراد البيانات.1 نقطة
-
أنت تعيد 0، وهذا يعدل المتغير itemCount إلى 0 لذلك يسمح للمستخدم بإعادة التحديد ماذا تفعل الطريقة clamp؟ جرب: لست متأكداً itemCount: _imageFileList !=null ? _imageFileList.length.clamp(0, 4) : _imageFileList.length, أو استخدم الدالة take لأخذ أول x عنصر من القائمة _imageFileList = _imageFileList != null ? (_imageFileList.length > 4 ? _imageFileList.take(4) : _imageFileList) : _imageFileList, itemCount: _imageFileList.length,1 نقطة
-
اهلا بك اخي انا قمت بتنفيذ ما ذكرته انت وذهب الخطاء فعلا المشكله فقط هيا كالتالي: لو قمت بذهاب الى الاستوديو واختيار 6 صور على سبيل المثال سوف يتم عرض للمستخدم لدي اربع صور وذلك بسبب السطر هذا : itemCount: _imageFileList !=null ? _imageFileList.length.clamp(0, 4) : 0, ولكن المشكله ان المستخدم لو قام بضغط من جديد على زر الاضافة يستطيع الذهاب الى الاستوديو بدون ان يعمل الشرط الذي تم وضعه لا اعلم ما هو سبب حدوث ذلك قمت باختبار ثاني وقمت فقط باختيار العدد المطلوب وهو 4 صور في هذا الحالة الكود يعمل والمستخدم لا يستطيع الضغط من جديد على الزر المشكله فقط لو قام المستخدم باختيار صور اكثر من اربع في اول دخول له الى الاستوديو لا اعلم اذا كان شرحي للمشكله واضح الكود كامل اصبح بشكل التالي: import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Image Picker Demo', home: MyHomePage(title: 'Image Picker Example'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { List<XFile> _imageFileList; set _imageFile(XFile value) { _imageFileList = value == null ? null : [value]; } bool isVideo = false; final ImagePicker _picker = ImagePicker(); static const int MAX_PHOTO_UPLOAD = 4; void _onImageButtonPressed(ImageSource source, {BuildContext context, bool isMultiImage = false}) async { if (isMultiImage) { try { final pickedFileList = await _picker.pickMultiImage( // maxWidth: 120, // maxHeight: 120, imageQuality: 100, ); if (_imageFileList == null ) { if (pickedFileList == null) return; setState(() { _imageFileList = pickedFileList; }); } else { if (pickedFileList != null) { setState(() { _imageFileList = [..._imageFileList, ...pickedFileList].toSet().toList(); }); } } } catch (e) { setState(() { }); } } } Future<void> retrieveLostData() async { final LostDataResponse response = await _picker.retrieveLostData(); if (response.isEmpty) { return; } if (response.file != null) { if (response.type == RetrieveType.video) { isVideo = true; } else { isVideo = false; setState(() { _imageFile = response.file; _imageFileList = response.files; }); } } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android ? FutureBuilder<void>( future: retrieveLostData(), builder: (BuildContext context, AsyncSnapshot<void> snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: case ConnectionState.waiting: return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); case ConnectionState.done: return Column( children: [ Expanded( child: ListView.builder( scrollDirection: Axis.horizontal, key: UniqueKey(), itemBuilder: (context, index) { return Semantics( label: 'image_picker_example_picked_image', child: kIsWeb ? Image.network(_imageFileList[index].path,fit: BoxFit.cover,) : Image.file(File(_imageFileList[index].path),fit: BoxFit.cover,) ); }, itemCount: _imageFileList !=null ? _imageFileList.length.clamp(0, 4) : 0, ), ), SizedBox( height: 5, ), Padding( padding: const EdgeInsets.only( left: 3, right: 3), child: Container( height: 50, width: 100, child: Material( color: Colors.deepOrange, child: InkWell( borderRadius: BorderRadius .circular( 8), child: Icon( Icons .add_a_photo, color: Theme.of( context) .floatingActionButtonTheme .foregroundColor, ), onTap: () async { if (_imageFileList != null && _imageFileList.length == MAX_PHOTO_UPLOAD){ showDialog( context: context, builder: (ctx) => AlertDialog( content: Text( "No more can be added" ), actions: < Widget>[ TextButton( onPressed: () { Navigator.of(ctx).pop(); }, child: Text("Closes"), ), ], ), ); return; } _onImageButtonPressed( ImageSource.gallery, context: context, isMultiImage: true, ); }), )), ) ], ); default: if (snapshot.hasError) { return Text( 'Pick image/video error: ${snapshot.error}}', textAlign: TextAlign.center, ); } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); } } }, ) : ListView.builder( scrollDirection: Axis.horizontal, key: UniqueKey(), itemBuilder: (context, index) { return Semantics( label: 'image_picker_example_picked_image', child: kIsWeb ? Image.network(_imageFileList[index].path,fit: BoxFit.cover, width: 100, height: 50,) : Image.file(File(_imageFileList[index].path),fit: BoxFit.cover, width: 100, height: 50,), ); }, itemCount: _imageFileList.length, ), ), ); } }1 نقطة
-
الخطأ: Unhandled Exception: NoSuchMethodError: The getter 'length' was called on null. ^^^^^^^^^^^^^^^^^^^^ ^^^^ القائمة الابتدائية تكون فارغة null لذلك لايمكن استدعاء دوال عليها ، الكائن لم يتم بناءه الحل: if (_imageFileList != null && _imageFileList.length == MAX_PHOTO_UPLOAD) { ^^^^^^^^^^^^^^^^^^^^^^ نختبر القائمة إن كانت موجودة. للتخلص من مشكلة null علينا استخدام الدالة البانية في أول الصفحة List<XFile> _imageFileList = List<XFile>();1 نقطة
-
مرحبا اخي الكريم @Wael Aljamal قمت بتجربه كثير بشكل التالي ولكن دائما احصل على نتيجة خطاء كالتالي: الكود المستعمل بشكل منفصل: static const int MAX_PHOTO_UPLOAD = 3; Padding( padding: const EdgeInsets.only( left: 3, right: 3), child: Container( height: 50, width: 100, child: Material( color: Colors.deepOrange, child: InkWell( borderRadius: BorderRadius .circular( 8), child: Icon( Icons .add_a_photo, color: Theme.of( context) .floatingActionButtonTheme .foregroundColor, ), onTap: () async { if (_imageFileList.length == MAX_PHOTO_UPLOAD) { showDialog( context: context, builder: (ctx) => AlertDialog( content: Text( "No more can be added" ), actions: < Widget>[ TextButton( onPressed: () { Navigator.of(ctx).pop(); }, child: Text("Closes"), ), ], ), ); return; } _onImageButtonPressed( ImageSource.gallery, context: context, isMultiImage: true, ); }), )), ) النتيجة: E/flutter (22452): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: NoSuchMethodError: The getter 'length' was called on null. E/flutter (22452): Receiver: null E/flutter (22452): Tried calling: length E/flutter (22452): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5) E/flutter (22452): #1 _MyHomePageState.build.<anonymous closure>.<anonymous closure> (package:flutter_application_1/testtwo.dart:179:56) E/flutter (22452): #2 _MyHomePageState.build.<anonymous closure>.<anonymous closure> (package:flutter_application_1/testtwo.dart:178:42) E/flutter (22452): #3 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:991:20) E/flutter (22452): #4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24) E/flutter (22452): #5 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11) E/flutter (22452): #6 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5) E/flutter (22452): #7 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7) E/flutter (22452): #8 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:475:9) E/flutter (22452): #9 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:93:12) E/flutter (22452): #10 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:138:9) E/flutter (22452): #11 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:397:8) E/flutter (22452): #12 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:136:18) E/flutter (22452): #13 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:122:7) E/flutter (22452): #14 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:381:19) E/flutter (22452): #15 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:361:22) E/flutter (22452): #16 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:278:11) E/flutter (22452): #17 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:316:7) E/flutter (22452): #18 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:280:5) E/flutter (22452): #19 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:238:7) E/flutter (22452): #20 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:221:7) E/flutter (22452): #21 _rootRunUnary (dart:async/zone.dart:1370:13) E/flutter (22452): #22 _CustomZone.runUnary (dart:async/zone.dart:1265:19) E/flutter (22452): #23 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7) E/flutter (22452): #24 _invoke1 (dart:ui/hooks.dart:180:10) E/flutter (22452): #25 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:276:7) E/flutter (22452): #26 _dispatchPointerDataPacket (dart:ui/hooks.dart:96:31) E/flutter (22452): الصفحة كامله import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Image Picker Demo', home: MyHomePage(title: 'Image Picker Example'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { List<XFile> _imageFileList; set _imageFile(XFile value) { _imageFileList = value == null ? null : [value]; } bool isVideo = false; final ImagePicker _picker = ImagePicker(); static const int MAX_PHOTO_UPLOAD = 3; void _onImageButtonPressed(ImageSource source, {BuildContext context, bool isMultiImage = false}) async { if (isMultiImage) { try { final pickedFileList = await _picker.pickMultiImage( // maxWidth: 120, // maxHeight: 120, imageQuality: 100, ); if (_imageFileList == null) { if (pickedFileList == null) return; setState(() { _imageFileList = pickedFileList; }); } else { if (pickedFileList != null) { setState(() { _imageFileList = [..._imageFileList, ...pickedFileList].toSet().toList(); }); } } } catch (e) { setState(() { }); } } } Future<void> retrieveLostData() async { final LostDataResponse response = await _picker.retrieveLostData(); if (response.isEmpty) { return; } if (response.file != null) { if (response.type == RetrieveType.video) { isVideo = true; } else { isVideo = false; setState(() { _imageFile = response.file; _imageFileList = response.files; }); } } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android ? FutureBuilder<void>( future: retrieveLostData(), builder: (BuildContext context, AsyncSnapshot<void> snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: case ConnectionState.waiting: return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); case ConnectionState.done: return Column( children: [ Expanded( child: ListView.builder( scrollDirection: Axis.horizontal, key: UniqueKey(), itemBuilder: (context, index) { return Semantics( label: 'image_picker_example_picked_image', child: kIsWeb ? Image.network(_imageFileList[index].path,fit: BoxFit.cover,) : Image.file(File(_imageFileList[index].path),fit: BoxFit.cover,) ); }, itemCount: _imageFileList !=null ? _imageFileList.length : 0, ), ), SizedBox( height: 5, ), Padding( padding: const EdgeInsets.only( left: 3, right: 3), child: Container( height: 50, width: 100, child: Material( color: Colors.deepOrange, child: InkWell( borderRadius: BorderRadius .circular( 8), child: Icon( Icons .add_a_photo, color: Theme.of( context) .floatingActionButtonTheme .foregroundColor, ), onTap: () async { if (_imageFileList.length == MAX_PHOTO_UPLOAD) { showDialog( context: context, builder: (ctx) => AlertDialog( content: Text( "No more can be added" ), actions: < Widget>[ TextButton( onPressed: () { Navigator.of(ctx).pop(); }, child: Text("Closes"), ), ], ), ); return; } _onImageButtonPressed( ImageSource.gallery, context: context, isMultiImage: true, ); }), )), ) ], ); default: if (snapshot.hasError) { return Text( 'Pick image/video error: ${snapshot.error}}', textAlign: TextAlign.center, ); } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); } } }, ) : ListView.builder( scrollDirection: Axis.horizontal, key: UniqueKey(), itemBuilder: (context, index) { return Semantics( label: 'image_picker_example_picked_image', child: kIsWeb ? Image.network(_imageFileList[index].path,fit: BoxFit.cover, width: 100, height: 50,) : Image.file(File(_imageFileList[index].path),fit: BoxFit.cover, width: 100, height: 50,), ); }, itemCount: _imageFileList.length, ), ), ); } }1 نقطة
-
اهلا بك اخي الكريم تم اخي سوف اقوم بمشاركة تجربتي باذن الله ليله1 نقطة
-
ما رأيك أن تجرب كتابة الشيفرة ثم نعمل على تعديلها، انت منذ فترة تتعلم فلاتر، ولكن يجب معرفة كيفية عمل مخطط منطقي لسير البرنامج وعمل بعض البحث. شاركنا تجربتك.1 نقطة
-
يمكنك عرض تنبيه للمستخدم مثلا على شكل Toast إشعار تنبيهات، كما يمكن وضع تنويه ضمن الزر اكتب حد اقصى 4 او Max (4) .. Select imgs up to 4... وبعد انتهاء المستخدم من التحديد، إن كان حجم القائمة أكبر من 4 اعرض له تنبيه جديد بخبره أن اول 4 صور سيتم اختيارها. واقتطع القائمة محتفظاً بأربع عناصر.1 نقطة
-
حلو جدا ، ايضا يمكنك السماح فقط لأربعة صور بالتخرين و الحفظ داخل قاعدة البيانات .1 نقطة
-
اهلا بك اخي الكريم الحل جميل وممكن تطبيقه من خلال طول القائمه ولكن المشكله انني سوف احتاج الى اضافة هذا الصور الى قاعدة البيانات مما يعني ان القائمه كلها سوف يتم ادراجها وهذا الامر لا ينفع انا احتاج فقط الى 4 صور لكل موضوع1 نقطة
-
أريد جعل موقعي مؤمن من الاختراق ويستخدم بروتوكول HTTPS فكيف أستطيع ذلك ومن أين أحصل على شهادة SSL وكيف أثبتها؟1 نقطة
-
لقد استعملت هذه الحزمة كثيرا و لم أجد ما تطلبه بها و لكن هناك حل ما و هو عندما يقوم المستخدم باختيار الصور , تقوم أنت فقط بعرض 4 صور من الصور المختارة و لا تعرض أكثر من ذلك حسب ما تريد.1 نقطة
-
حسب ترتيب خطوات تنفيذ الاستعلام في SQL سيتم تنفيذ جزء ON قبل جزء WHERE لذلك يمكننا نقل الشرط لتتم الفلترة بناءً عليه بخطوة قبل وهذا يسرع الأداء، حيث لا نحتاج WHERE بعدها SELECT * FROM Table1 LEFT JOIN Table2 ON Table1.id = Table2.user_id AND Table2.role='Admin' ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^ لاحظ نقل شرط WHERE ودمجه مع شرط الربط. WHERE تعمل على الجدول الناتج من عملية الربط، أي سيكون هنالك بيانات أكثر لاختبارها، لذلك نضع الشرط مع جزء ON ضع دائمًا شروط الدمج في جملة ON إذا كنت تقوم بإجراء INNER JOIN. أي لا تضف أي شروط من WHERE إلى جملة ON ، بل ضعها في جملة WHERE إذا كنت تقوم بتنفيذ LEFT JOIN ، فقم بإضافة أي شروط WHERE إلى جملة ON للجدول الموجود في الجانب الأيمن من الدمج. هذا أمر لا بد منه ، لأن إضافة جملة WHERE تشير إلى الجانب الأيمن من الدمج ستحوله إلى INNER JOIN. الاستثناء هو عندما تبحث عن السجلات غير الموجودة في جدول معين. يمكنك إضافة المرجع إلى معرّف فريد (ليس فارغًا أبدًا) في جدول RIGHT JOIN إلى جملة WHERE بهذه الطريقة: WHERE t2.idfield IS NULL. لذا ، فإن المرة الوحيدة التي يجب أن تشير فيها إلى جدول على الجانب الأيمن من الصلة هي العثور على تلك السجلات غير الموجودة في الجدول.1 نقطة
-
لنفترض أنه لدينا استعلام فيه العديد من الكلمات المفتاحية سيكون ترتيب التنفيذ كالتالي: تحديد الجداول وخاصة الجدول اليساري FROM LEFT TABLE تحديد علاقة الربط ON التي تخص حقول الربط تحديد نمط الربط والجدول الثانوي (يميني - يساري - كامل LEFT - RIGHT - FULL ..) تطبيق الفلترة باستخدام الشرط الخاص بعبارة WHERE عملية التجميع التي تحددها GROUP BY عملية الفلترة على المجموعات الجزئية أي تطبيق شرط HAVING تحديد الأعمدة المراد جلب قيمها وهنا ينفذ جزء SELECT تحديد الفلترة الخاصة ب DISTINCT عمل الترتيب بواسطة ORDER BY تحديد عدد الحقول المعادة مثل LIMIT - OFFSET - TOP (خطوة 7) SELECT (خطوة 8) DISTINCT (خطوة 10) <top_specification> <select_list> (خطوة 1) FROM left_table (خطوة 3) join_type JOIN right_table (خطوة 2) ON join_condition (خطوة 4) WHERE where_condition (خطوة 5) GROUP BY group_by_list (خطوة 6) HAVING having_clause (خطوة 9) ORDER BY order_by_list1 نقطة
-
لنحاول التعديل: try { final pickedFileList = await _picker.pickMultiImage( maxWidth: 66, maxHeight: 66, imageQuality: 66, ); if (_imageFileList == null) { if ( pickedFileList == null) return; setState(() { _imageFileList =pickedFileList; }); } else { if (pickedFileList != null) { setState(() { _imageFileList = [..._imageFileList, ...pickedFileList].toSet().toList(); }); } // لا تغيير } } catch (e) { setState(() { _pickImageError = e; }); }1 نقطة
-
إن الفترة 6 أشهر، هي المدة التي يضمن فيها الطالب استرداد أمواله في حال لم يجد عمل بعد اجنياز الامتحان. الأعمال التي تؤهلك لها الدورة: بالإضافة لسهولة تعلم أي مجال برمجي آخر أو تخصصي. استرداد الأموال: بالإضافة لمشاركات مفيدة:1 نقطة
-
استبدل النص المرسل للمستخدم في دالة send: res.send('Hello World!') ^^^^^^^^^^^^ // Thank you for your request res.send('Thank you for your request')1 نقطة
-
لقد طرحت سؤالاً متقدماً أكثر ويتعامل مع قواعد البيانات، فأعتقد أنك تستطيع حل هذه المشكلة.. const express = require('express') // تضمين المخدم const app = express() // بناء الغرض // تحديد المنفذ const port = 4000 // استقبال الطلبيات في المسار الجذر app.get('/', (req, res) => { // إرسال رد للمستخدم res.send('Hello World!') }) // تشغيل المخدم app.listen(port, () => { // ^^^^ تحديد المنذ من المتغير console.log(`Example app listening at http://localhost:${port}`) }) // التشغيل ضمن cmd node app.js1 نقطة
-
اولا يطلب البرنامج من المستخدم إدخال نص، ويكتب له > مثل إشارة لمربع النص. وبعد استقبال الدخل من المستخدم، يفصل الكلامات عن طريق الفراغ، أي يحول السلسلة النصية إلى مصفوقة كل عنصر منها كلمة باستخدام التابع split. وثم يقوم بالمرور على الكلمات و يستبدل الكلمة و برمز إيموجي. عليك ان تجرب البرنامج، مثلاً أدخلت رسالة ترحيب ستظهر: Hello wael :) => Hello wael 😄 massage = input("> ") # قراءة سلسلة نصية words = massage.split(" ") # تقسم السلسلة لكلمات حسب الفراغات وتصبح مصفوفة # تهيئة قاموس يقابل كل كلمة بالتي نريد تبديلها بها emojes = { ":(" : "😞", ":)" : "😄" } output = "" # تهيئة السلسلة الناتجة for word in words : # المرور على المصفوفة # إضافة ناتج استبدال الكلمة الحالية حسب مقابلها في القاموس، وفي حال لايوجد مقابل نعيد نفس الكلمة output += emojes.get(word , word) + " " print(output) # إضافة ناتج استبدال الكلمة الحالية حسب مقابلها في القاموس، وفي حال لايوجد مقابل نعيد نفس الكلمة التابع get يأخذ مفتاح للبحث عنه ضمن القاموس، ولدينا : ( لها مقابل، فيعي الرمز التعبيري ايموجي بدالها ، وإلا يعيد نفس الكلمة1 نقطة
-
لرسم هيستوغرام يمكنك استخدام الدالة hist في Matplotlib: matplotlib.pyplot.hist(x, bins=None, range=None, density=False, histtype='bar',align='mid', orientation='vertical', color=None, label=None, stacked=False, data=None, **kwargs) ولإنشاء هيستوغرام (مدرج تكراري)، فإن الخطوة الأولى هي إنشاء حاوية للنطاقات أو المجالات " bin of the ranges "، ثم توزيع النطاق الكامل للقيم في سلسلة من الفواصل الزمنية، وإحصاء القيم التي تقع في كل من الفواصل الزمنية. أي بمعنى أوضح مثلاً في حالة لديك صورة رمادية فإن السويات اللونية والتي تمثل المحور الأفقي تكون من 0 ل255 وتريد معرفة عدد مرات تكرار كل سوية لونية في الصورة (كم بكسل في الصورة يحمل هذه القيمة) ولسبب ما تريد أن تقسم هذا السويات إلى نطاقات (مجالات) مثلاً تريد تقسيمها إلى 64 مجال بحيث كل مجال يحوي 4 سويات لونية. وبالتالي نكون هنا قد حددنا الحاويات وتبقى لنا أن نقوم بإحصاء البكسلات التي تنتمي إلى كل حاوية من هذه الحاويات. الآن بالعودة للوسطاء فإن أول وسيط يمثل المصفوفة التي نريد حساب هستوغرامها (قد تمثل صورة أو أياً كان)، والوسيط الثاني bins هو عدد صحيح يمثل عدد الحاويات، أما range فيحدد القيمة الدنيا والعليا لل bins. أما density فهي قيمة منطقية، ويؤدي تفعيلها لرسم كثافة احتمالية وسيتم إرجاعها من الدالة، وهنا ستعرض كل حاوية العدد الأولي للحاوية مقسوماً على العدد الإجمالي للأعداد وعرض الحاوية: (density = counts / (sum(counts) * np.diff(bins))) بحيث تتكامل المنطقة الموجودة أسفل المدرج التكراري مع 1: (np.sum(density * np.diff(bins)) == 1) وإذا كان stacked مفعلاً أيضاً، فسيتم تسوية (normalized) مجموع الرسوم البيانية إلى 1. أما الوسيط histtype فهو يحدد نوع الرسم البياني المراد رسمه {'bar', 'barstacked', 'step', 'stepfilled'} وافتراضياً يكون bar، وهو الرسم بياني التقليدي (أشرطة أو أعمدة). بحيث يتم ترتيب الأشرطة جنباً إلى جنب. والنوع الثاني barstacked وهو مدرج تكراري من نوع شريط bar حيث يتم تكديس العديد من البيانات فوق بعضها البعض.أما النوع step مخطط خطي غير معبأ بشكل افتراضي. وأخيراً stepfilled يولد مخطط خطي معبأ بشكل افتراضي. أما الوسيط align فيحدد المحاذاة الأفقية لأشرطة الرسم البياني. "left": تتركز الأشرطة على حواف الحاوية اليسرى. "mid": تتمركز الأشرطة بين حواف الحاوية. "right": تتركز الأشرطة على حواف الحاوية اليمنى. أما orientation فقيمه هي {'vertical', 'horizontal'}، فإذا كان "أفقياً" ، فسيتم استخدام barh -أي bar لكن معكوس- للمخططات البيانية من النوع الشريطي وسيكون kwarg السفلي هو الحواف اليسرى. أما ال color فهو لتحديد اللون. وأخيراً ففي حالة التغعيل True فسيتم تكديس بيانات فوق بعضها البعض. أما في حال عدم التفعيل False فيتم ترتيب البيانات المتعددة جنباً إلى جنب إذا كان نوع هيست هو "bar" أو فوق بعضها البعض إذا كان النوع هو "step". مثال 1: سنقوم بتوليد بيانات عشوائية ثم سنقوم بعرض هستوغرامها: import matplotlib.pyplot as plt import numpy as np x = np.random.normal(170, 10, 250) plt.hist(x) plt.show() الخرج: مثال2: import matplotlib.pyplot as plt import numpy as np from matplotlib import colors from matplotlib.ticker import PercentFormatter # إنشاء بيانات عشوائية N_points = 984 n_bins = 15 # إنشاء توزيع x = np.random.randn(N_points) y = .8 ** x + np.random.randn(984) + 25 legend = ['distribution'] # إنشاء الهيستوغرام fig, axs = plt.subplots(1, 1, figsize =(10, 7), tight_layout = True) #ticks حذف العلامات axs.xaxis.set_ticks_position('none') axs.yaxis.set_ticks_position('none') # إضافة شبكة إلى الخلفية axs.grid(b = True, color ='grey', linestyle ='-.', linewidth = 0.5, alpha = 0.6) # إضافة نص للشكل fig.text(0.9, 0.15, 'Ali Haidar Ahmad', fontsize = 13,color ='red',ha ='right', va ='top',alpha = 0.7) # plot حذف خطوط حاويةال for s in ['top', 'bottom', 'left', 'right']: axs.spines[s].set_visible(False) #وضع مسافة بين التسميات وبين المحاور axs.xaxis.set_tick_params(pad = 4) axs.yaxis.set_tick_params(pad = 8) # إنشاء الهستوغرام N, bins, patches = axs.hist(x, bins = n_bins) # تحديد الألوان fracs = ((N**(9)) / N.max()) norm = colors.Normalize(fracs.min(), fracs.max()) for thisfrac, thispatch in zip(fracs, patches): color = plt.cm.viridis(norm(thisfrac)) thispatch.set_facecolor(color) # تسمية للمحاور plt.xlabel("X-axis") plt.ylabel("y-axis") plt.legend(legend) # عنوان plt.title('Hsoub') # عرض plt.show() الخرج: مثال 3: import matplotlib.pyplot as plt import cv2 # قراءة صورة im = cv2.imread('/content/closedeye1.jpg') # نقوم بتسطيح الصورة لتصبح أحادية الأبعاد vals = im.flatten() # هنا كونك تريد أن تقوم برسم هيستوغرام صورة فيجب أن تضع عدد الحاويات على 255 إلا إذا كنت تريد شيئاً آخر # 255 = bins plt.hist(vals, 255) plt.xlim([0,255]) plt.show() الخرج:1 نقطة
-
إن أكاديمية حسوب لاتوفر دورات خاصة ببرمجة الألعاب, حتى تستطيع برمجة الألعاب يجب أن يتوفر لديك مهارات التفكير المنطقي و الخوارزميات وأساسيات البرمجة، فإن كنت مبتدئ أنصح بالتعلم بشكل صحيح من البداية ليكون لديك معرفة واسعة. وبشكل متقدم، برمجة الألعاب تحتاج لمهارات في الرياضيات.. إن قمت بتسجيل دورة علوم الحاسب المقدمة من أكاديمية حسوب سوف تستطيع تعلم لغة برمجة سكراتس وإنشاء ألعاب تفاعلية مع تحريك الأجسام وتغيير الخلفيات والقفز و إطلاق الرصاص من الدبابة وكلها أساسيات عالم الألعاب وغيرها من الأمور الأساسية لكي تصبح قادراً على تعلم لغة برمجة حقيقية ثم بعدها برمجة الألعاب. أنصح بهذه الدورة. مدخل إلى علوم الحاسوب أساسيات البرمجة أنظمة التشغيل ونظام لينكس قواعد البيانات إلى عالم الويب البرمجة كائنية التوجه الخوارزميات وبنى المعطيات أنماط التصميم هذه أمثلة تدرس في الدورة1 نقطة
-
في OpenCV لتطبيق انزياح على الصورة (نقل) نحتاج إلى تحديد مقدار الانزياح على المحور الأفقي والرأسي للصورة (tx ، ty) لإنشاء مصفوفة التحويل: حيث يشير tx إلى التحول على طول المحور x و ty يشير إلى التحول على طول المحور y ، أي عدد البكسلات التي نحتاجها للتحول بها في هذا الاتجاه. ثم نستخدم الدالة cv2.wrapAffine لتطبيق هذا التحويل، وهذه الدالة تحتاج إلى مصفوفة 2×3 كما أنها تتطلب مصفوفة بقيم np. float32 لذا يجب تحويل مصفوفتك إلى هذا النمط قبل تمريرها إلى الدالة. في المثال التالي سنقوم بتطبيق إزاحة للصورة التالية بمقدار معين: import cv2 from google.colab.patches import cv2_imshow import numpy as np # قراءة الصورة img = cv2.imread("/content/closedeye1.jpg") # تحديد عدد أسطر وأعمدة الصورة rows,cols,_ = img.shape # مصفوفة التحويل # سنقوم بتطبيق تحويل بمقدار 60 على المحور الأفقي و 20 للعمودي M = np.float32([[1,0,90],[0,1,60]]) # 3*2 مصفوفة dst = cv2.warpAffine(img,M,(cols,rows)) # عرض الصورة cv2_imshow(dst) والصورة الناتجة بعد التحويل:1 نقطة
-
يمكنك استخدام التابع cv2.resize لإنجاز ما أنت بحاجةٍ إليه، حيث يقوم بتقليصها أو توسيع نطاقها لتلبية متطلبات الحجم. كما بوفر العديد من طرق الاستيفاء (التوليد) لتغيير حجم الصورة، وأهمها: - cv2.INTER_AREA: يستخدم عندما نحتاج إلى تقليص الصورة. أو cv2.INTER_CUBIC لكنه أبطأ لكن أكثر كفاءة. - cv2.INTER_LINEAR: يستخدم عندما نحتاج تكبير الصورة (الحالة الافتراضية عندما نطلب زيادة حجم الصورة). وبشكل أساسي يأخذ هذا التابع 3 وسطاء: cv2.resize(src, size,interpolation) الوسيط الأول هو الصورة المطلوب تعديلها، أما الثاني فهو الحجم المطلوب تعديل حجم الصورة إليه. أما الثالث فهو طريقة الاستيفاء المطلوبة. مثال: import cv2 from google.colab.patches import cv2_imshow # قراءة الصورة img = cv2.imread("/content/closedeye1.jpg", cv2.IMREAD_UNCHANGED) # طباعة حجم الصورة print('Original Dimensions : ',img.shape) # تحديد أبعاد الحجم الجديد dim = (100, 120) # resize تطبيق التابع # resize image resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) # عرض حجم الصورة بعد التعديل print('Resized Dimensions : ',resized.shape) # عرض الصورة cv2_imshow(resized) ######################### الخرج ##################### # Original Dimensions : (186, 271, 3) # Resized Dimensions : (120, 100, 3) والصورة الناتجة: 3*100*120 مثال لزيادة حجم الصورة: import cv2 from google.colab.patches import cv2_imshow # قراءة الصورة img = cv2.imread("/content/closedeye1.jpg", cv2.IMREAD_UNCHANGED) # طباعة حجم الصورة print('Original Dimensions : ',img.shape) # تحديد أبعاد الحجم الجديد dim = (400, 400) # resize تطبيق التابع # resize image resized = cv2.resize(img, dim, interpolation = cv2.INTER_LINEAR) # عرض حجم الصورة بعد التعديل print('Resized Dimensions : ',resized.shape) # عرض الصورة cv2_imshow(resized) ######################### الخرج ##################### # Original Dimensions : (186, 271, 3) # Resized Dimensions : (400, 400, 3) والصورة الناتجة: 3*400*4001 نقطة
-
نعم يمكن الاستعلام من قاعدتي بيانات مختلفتين، الفرق هو وضع اسم قاعدة البيانات التي ينتمي لها الجدول قبل اسمه الشكل العام: SELECT table_1.*, table_2.* FROM [Database_1].[Table_Schema].[Table_Name_1] table_1 JOIN [Database_2].[Table_Schema].[Table_Name_2] table_2 ON table_1.id = table_2.id مثال تقريبي: SELECT t1.*, t2.name -- تحديد الحقول FROM DataBase_A.table1 t1 -- تحديد الجدول الأول من قاعدة البيانات الأولى JOIN DataBase_B.table2 t2 -- تحديد الجدول الثاني من قاعدة البيانات الثانية ON t2.column2 = t1.column1; -- عمل ربط معين لاحظ أن استخدام الاسم المستعار يسهل الوصول للجداول يكون اسم قاعدة البيانات التي ينتمي لها الجدول بادئة prefix للمجال الذي ينتمي منه الجدول مثال فعلي: لنفرض لدينا قاعدة بيانات أولى فيها جدول المستخدمين، وقاعدة بيانات ثانية فيها جدول الرسائل / المحادثات فيمكنا عمل ربط وجلب رسائل أحد المستخدمين بعمل الربط بشكل عادي كالتالي: select * from DataBase_A.dbo.Users u join DataBase_B.dbo.Messages m on u.UserId = m.UserId1 نقطة
-
يوجد عدة طرق لتحقيق المطلوب وهذا يعتمد على نوع مخدم قاعدة البيانات وما يدعمه. في SQL Server يوجد الكلمتين المفتاحيتين: OFFSET وتعني تطبيق إزاحة skip وتجاهل عدد من السجلات (نبدأ جلب السجلات التي رقمها من بعد قيمتها) FETCH NEXT تفيد في تحديد عدد الأسطر التالية المطلوبة SELECT col1, col2, -- تحديد الأعمدة FROM ... -- تحديد الجدول WHERE ... -- كتابة شرط الفلترة ORDER BY -- يفضل وبشكل ضروري ترتيب البيانات حسب حقل معين ليتم جلبهم بطريقة صحيحة -- الجزء التالي يحدد الإزاحة وعدد السجلات الذي سيعيده الاستعلام OFFSET 10 ROWS -- تخطي عشر سجلات 10 rows FETCH NEXT 10 ROWS ONLY; -- جلب العشر سجلات التالية 10 rows يمكن تعريف متغيرات وتمرير قيمة لها ثم استخدامها في الاستعلام DECLARE @Skip TINYINT = 10 , @Fetch TINYINT = 10; كما يمكن تطبيقها بواسطة ROW_NUMBER التي تعيد رقم السطر ضمن النتيجة وبهذا نقوم بفلترة النتائج select * -- جلب أعمدة from ( select *, ROW_NUMBER() OVER (ORDER BY columnid) AS ROW_NUM -- جلب ترقيم النتائج بترتيب حسب المعرف from table_name ) table_x WHERE -- تحديد شرط الفلترة RowNum >= 10 AND RowNum <= 20 ORDER BY RowNum -- إعادة ترتريب حسب رقم السطر وفي MySQL يمكن استخدام LIMIT + OFFSET LIMIT + OFFSET => SELECT column FROM table LIMIT 10 OFFSET 10 LIMIT 2 params => SELECT * FROM table LIMIT 10, 50 -- النتيجة هي الأسطر 11-60 skip 10, take 50 تجاهل أول 10 أسطر وجلب 50 سطر التالي لجلب كل القيم بعد النتيجة 10، نضع الوسيط الثاني قيمة كبيرة جدا SELECT * FROM tbl LIMIT 10,18446744073709551615; وباستخدام المتغيرات SET @skip=1; SET @numrows=5; PREPARE STMT FROM 'SELECT * FROM tbl LIMIT ?, ?'; EXECUTE STMT USING @skip, @numrows;1 نقطة
-
لماذا إعادة تقديم؟ لسوء سُمعة JavaScript حيثُ أنَّها أكثر لُغة برمجة يُساء فهمها في العالم. على الرَّغم من أنَّه في كثيرٍ من الأحيان يتمّ وصفها لُعبة على سبيل السخريّة، إلَّا أنّهُ أسفل بساطتها الخادعة تقعُ بعض ميزات لغةٍ قويّة، أحدُها أنَّها الآن تُستَخدم من قِبَل عددٍ لا يُصدّق من التطبيقاتِ عاليةِ المستوى، ويُبيِّن ذلك أنَّ معرفةً أعمق بهذه التكنولوجيا إنّما هي مهارة مُهمّة لأي مُطوّر لشبكة الإنترنت أو المحمول. من المُفيد أن نبدأ بفكرةٍ عامَّة عن تاريخ هذه اللُّغة. تم إنشاء JavaScript في عام 1995 من قبل Brendan Eich، وهو مهندس في Netscape، وصدرت أولًا مع Netscape 2 في عام 1996. في الأصل كان مُتّفقٌ تسميتها LiveScript، ولكن تمَّ تغيير اسمها في قرارٍ تسويقيٍّ مشؤوم كمُحاولة للاستفادة من شعبية لُغة Java - التابعة لـ Sun Microsystems- على الرغم من أنَّ هُناك عدد قليل جدًا من القواسم المُشتركة بين اللغتين. لقد كان هذا مصدرًا للارتباك منذ ذلك الحين. بعد ثلاثةِ أشهرٍ أطلقت Microsoft نُسخة مُعظمُها مُتوافق مع اللُّغة أسمتها JScript مع IE . قدّمت Netscape اللُّغة إلى Ecma International، وهي مُنظّمةُ معاييرٍ أوروبيّة، وأسفرَ ذلك عن الطبعةِ الأولى من ECMAScript القياسيّة في عام 1997. تلقّت اللُغة عملية تحديث كبيرة كما في ECMAScript Edition 3 في عام 1999، وبَقيتْ مُستقرّة إلى حدٍ كبير منذ ذلك الحين. تمّ التخلي عن الطبعة الرابعة بسبب الخلافات السياسيّة بشأن تعقيد اللُّغة. شكَّلَتْ أجزاءٌ كثيرة من الطبعة الرابعة أساسَ الطبعة ECMAScript 5، التي نُشِرَت في ديسمبر من عام 2009 والطبعة الكبرى 6 والتي سيتمّ نشرها في عام 2015. من أجل الاعتياد، سوف أستخدمُ مُصطلحَ JavaScript طوال الوقت. خلافًا لمُعظم لغاتِ البرمجة، ليس لدى لغة JavaScript مفهوم الإدخال أو الإخراج. إنّما هي مُصمَّمةٌ لتعمل كلغةِ برمجةٍ نصيّة في بيئة استضافة، والأمرُ متروكٌ للبيئة المضيفة لتوفير آليات للتواصل مع العالم الخارجي. بيئةُ الاستضافةِ الأكثرِ شيوعًا هي المُتصفِّح، ولكن بالإمكان أيضًا العثور على مُفسّر JavaScript في Adobe Acrobat, Photoshop, SVG images, Yahoo!'s Widget engine، فضلًا عن البيئاتِ المُتاحةِ من جانب الخادوم مثل node.js. إلا أن قائمة المناطق حيث يتم استخدام JavaScript فقط تبدأ هُنا. تشتملُ اللُّغة أيضًا على قواعد بيانات NoSQL، مثل Apache CouchDB مفتوح المصدر، أجهزة الحاسوب المضمّنة أو بيئات سطح المكتب كاملة، مثل GNOME (واحدة من واجهات المستخدم الرسوميّة الأكثر شعبيّة لأنظمة التشغيل جنو / لينكس). نظرة عامةJavaScript هي لغة ديناميكيّة كائنيّة المنحنى. لديها أنواع types ومُشغِّلين operators، كائنات objects قياسيّة مُدمجة ووظائف methods. يأتي بناءُ الجُملةِ في JavaScript من لُغَتَي Java و C، وتنطبقُ العديدُ من الهياكل في تلك اللغات في JavaScript كذلك. إلَّا أنَّ أحد الفروق الرئيسيَّة هو أنَّه لا توجد فئات Classes في JavaScript. بدلًا من ذلك، يتم إنجاز وظائف الفئة عن طريق نماذج الكائن object prototypes. الفرقُ الرئيسي الآخر هو أن الدوال functions هي كائنات، تُعطى الدوال القدرة على الاحتفاظ بتعليماتٍ برمجيَّة قابلة للتنفيذ وتمريرها مثلها مثل أي كائن آخر. دعونا نبدأ من خلال النَّظر داخل لبنة بناء أي لغة: الأنواع. تقوم برامج JavaScript بمُعالجة القيم، وتنتمي كل تلك القيم إلى نوع. أنواع JavaScript هي: عدد Numberسلسلة Stringمنطقيَّة Booleanدالّة Functionكائن Objectرمز Symbol (جديد في الإصدار 6)كذلك undefined وnull، ويُعتبر هذين النوعين غريبين بعض الشيء. وهُناكَ أيضًا المصفوفة Array، والتي هي نوع خاص من الكائن. والتاريخ Date والمُنشئ RegExp، وهي كائناتٌ تحصلُ عليها مجانًا. ولكي نكون دقيقين من الناحية الفنيّة، الدوال ما هي إلَّا نوعٌ خاصٌّ من الكائن. ولذلك فإن الرسم البياني للنوع يبدو أكثر مثل ما يلي: عدد Numberسلسلة Stringمنطقيَّة Booleanرمز Symbol (جديد في الإصدار 6)كائن Objectدالّة Functionمصفوفة Arrayتاريخ Dateمُنشئ RegExpلا شيء Nullغير مُحدَّد Undefinedكذلك هُناك بعض أنواع الخطأ Error المُدمجة. لكن على كلِّ حال الأمور أسهل كثيرًا إذا اعتمدنا على الرسم البياني الأوَّل. الأرقامالأرقام في JavaScript هي "قِيَم IEEE 754 الدقيقة المُزدوجة في شكل 64-بِت"، وفقًا للمواصفات. لهذا المعنى بعض العواقب المُثيرة للاهتمام. ليس هُناكَ شيءٌ في JavaScript يُشبه العدد الصحيح integer، لذلك عليك أن تكون حذرًا قليلًا مع عمليّاتك الحسابيّة إذا كنتَ معتادًا على الرياضيات في C أو Java. احترس من أشياء مثل: 0.1 + 0.2 == 0.30000000000000004في المُمَارسة العمليّة، يتمّ التعامل مع القيم كما أعداد صحيحة من 32 بِت bit (ويتم تخزينهم بتلك الطريقة في بعض تطبيقات المُتصفِّح)، والتي يمكن أن تكون هامّة للعمليّات التي تستخدم نوع بِت. العوامل الحسابيّة القياسيّة مدعومة، بما في ذلك الجمع والطرح ومُعامل الحساب modulus أو (remainder)، وهكذا دواليك. هناك أيضًا كائن مُدمَج نسيتُ ذِكرَهُ سلفًا يُسمّى Math والذي يُمكنكَ استخدامَهُ إذا كنت ترغب في أداءِ دوالٍّ وثوابت رياضيّة أكثر تقدمًا: Math.sin(3.5); var d = Math.PI * r * r;يُمكنكَ تحويل سلسلة إلى عدد صحيح باستخدام الدالّة المُدمجة ()parseInt. هذه الدالّة تأخذ قاعدة للتحويل كمُعامِل argument ثاني اختياري، والتي ينبغي عليكَ توفيرها دائمًا: parseInt("123", 10); // 123 parseInt("010", 10); // 10رُبَّما تحصُل على نتائج مُذهلة في المُتصفِّحات القديمة (قبل عام 2013) إذا لم توفِّر قاعدة التحويل: parseInt("010"); // 8حَدَثَ ذلك لأن دالّة ()parseInt قرّرت مُعامَلة السلسلة كثُمانيّة octal نظرًا لأن 0 هي الرَّائدة. إذا كُنتَ ترغب في تحويل رقم ثنائيّ binary إلى عدد صحيح integer، فقط يتمّ تغيير القاعدة: parseInt("11", 2); // 3وبالمثل، يمكن تحليل أرقام الفاصِلة العائمة floating point numbers باستخدام الدالّة المُدمجة ()parseFloat والتي تَستَخدِم base 10 دائمًا خلافًا لرفيقتها ()parseInt. يُمكنكَ أيضًا استخدام عامل التشغيل الأحاديّ + لتحويل القيم إلى أرقام: + "42"; // 42يتمّ إرجاع قيمة خاصّة تُسمّى NaN (اختصار لـ”Not a Number”) إذا كانت السلسلة غير رقميّة: parseInt("hello", 10); // NaNNaN سامّة: إذا قُمتَ بتقديمها كمدخل input إلى أي عمليَّة حسابيَّة فإن النتيجة ستكون أيضًا NaN: NaN + 5; // NaNيُمكنكَ اختبار NaN باستخدام الدالّة المُدمجة ()isNaN: isNaN(NaN); // trueلدى JavaScript القيم الخاصّة Infinity و-Infinity: 1 / 0; // Infinity -1 / 0; // -Infinityيُمكنكَ اختبار قيم Infinity و–Infinity وNaN باستخدام الدالّة المُدمجة ()isFinite: isFinite(1/0); // false isFinite(-Infinity); // false isFinite(NaN); // falseمُلاحظة: تقوم دالَّتَي ()parseInt و()parseFloat بتحليل سلسلة حتى تصلا إلى حرفٍ غير صالح لشكل الرَّقم المُحدَّد، بعد ذلك يتمّ إرجاع الرقم الذي تم تحليله إلى تلك النقطة. رغم ذلك فإن المُعامِل "+" يقوم ببساطة بتحويل السلسلة إلى NaN إذا كان هُناك أيّ حرفٍ غير صالح في السلسلة. جرّب محاولة تحليل السلسلة "10.2abc" باستخدام كل طريقة مع نفسك في وحدة التَّحكُّم console، وسوف تفهم الاختلافات بشكلٍ أفضل. السلاسلالسلاسل في JavaScript هي مُتتابعات من الأحرف. ولتعريفٍ أكثر دقة، هي مُتتابعات من أحرف Unicode، يتمّ تمثيل كُل حرف في عدد 16 بِت. لا بُدَّ أنَّ هذا نبأ سار لأي شخص قد تعامل مع نظام التدويل. إذا كُنتَ ترغب في تمثيل حرف واحد، عليكَ استخدام سلسلة من طول 1 فقط. للعثور على طول سلسلة، قٌم باستدعاء خاصيّتها length: "hello".length; // 5هذه أول فُرشاة لدينا مع كائنات JavaScript! هل ذكرتُ لكَ أنَّ بامكانكَ استخدام السلاسل مثل الكائنات أيضًا؟ لديهم وظائف تسمحُ لكَ بمُعالجة السلسلة والوصول إلى معلومات عنها: "hello".charAt(0); // "h" "hello, world".replace("hello", "goodbye"); // "goodbye, world" "hello".toUpperCase(); // "HELLO"أنواع أخرىتُميّز JavaScript ما بين null، وهي القيمة التي تدل على لا شيء (ويمكن الوصول إليها من خلال طريقة واحدة ألا وهي استخدام الكلمة المحجوزة null)، وundefined، وهي قيمة من نوع 'غير معروف' التي تُشير إلى قيمة غير مُهيَّأة - أي قيمة لم يتمّ تعيينها بعد. سوف نتحدَّث عن المُتغيّرات في وقتٍ لاحق، ولكن يُمكن تعريف مُتغيّر في JavaScript دون تحديد قيمة له. إذا قمتَ بذلك، فإنَّ نوع المُتغيّر يُصبِح undefined. في الواقع نوع undefined ثابت. لدى JavaScript نوع Boolean مع القيم الممكنة true وfalse (وكلاهما كلمات محجوزة). يمكن تحويل أيّ قيمة إلى قيمة منطقيّة وفقًا للقواعد التالية: 1- تُصبِح كل من (false، 0، سلسلة فارغة (" ")، NaN، null وundefined) false. 2- تُصبِح كل القيم الأخرى true. يُمكنكَ إجراء هذا التحويل صراحة باستخدام دالَّة ()Boolean: Boolean(""); // false Boolean(234); // trueولكن نادرًا ما يكون هذا ضروريًّا، حيث أن JavaScript ستؤدّي بصمتٍ هذا التحويل عندما تتوقَّع قيمة منطقيَّة، مثل في عبارة if (انظر أدناه). لهذا السبب، ببساطة نذكُر أحيانًا "القيم الحقيقيّة" و"القيم الزائفة" قاصدين القيم التي تُصبح true وfalse على التوالي عند تحويلها إلى القيم المنطقيّة. بدلًا من ذلك يُمكن تسمية هذه القيم "truthy" و"falsy" على التوالي. العمليّات المنطقيّة مثل &&، logical and) || (logical or و! (logical not) مُعتمَدة. انظر أدناه. المُتغيّراتيتمّ تعريف المُتغيّرات الجديدة في JavaScript باستخدام var: var a; var name = "simon";إذا قمتً بتعريف مُتغيّر دون تعيين أيّ قيمة له، فإن نوعه يُصبِح undefined. هناك فارق هام عن لغات أخرى مثل Java ألا وهو أنّ الكُتَل blocks في JavaScript لا تملك نطاق scope؛ فقط الدوال هي التي لها نطاق. لذلك إذا تم تعريف مُتغيّر باستخدام var في جملة مُجمّعة (على سبيل المثال داخل if control structure)، سيكون المُتغيِّر مرئي من قِبَل الدالّة كاملة. إلَّا أنَّه بدءًا من ECMAScript الطبعة 6، تَسمحُ لك تعريفات let وconst امكانيَّة إنشاء مُتغيّرات block-scoped. المُعامِلاتمُعامِلات JavaScript الرقميّة هي +، -، *، / و٪ - وهو عامل ما تبقى. يتمّ تعيين القيم باستخدام = وهناك أيضًا جُمل تعيين مُركبّة مهمّة مثل += و-=. هذه تمتد إلى x = x مُعامِل y. x += 5 x = x + 5يُمكنكَ استخدام ++ و-- للزيادة والإنقاص على التوالي. كذلك بإمكانك أن تَستَخدِم هذه المُعامِلات كمُعامِلات prefix أو postfix. يقومُ المُعامِل + بربط السلسلة: "hello" + " world"; // "hello world"إذا قمتَ بإضافة سلسلة لعدد (أو قيمة أخرى) يتمّ تحويل كل شيء إلى سلسلة أولًا. يُمكن لما يلي أن يوضِّح المقصود: "3" + 4 + 5; // "345" 3 + 4 + "5"; // "75"إضافة سلسلة فارغة إلى شيء هي وسيلة مفيدة لتحويله إلى سلسلة. يُمكن إجراء المُقارنات في JavaScript باستخدام <، >، <= و =>. تَعمَلُ هذه المُقارنات مع السلاسل والأرقام على حدٍّ سواء. تبدو المُساواة أوضح قليلًا عن نظيراتها. تقوم المُساواة المزدوجة == بإجبار تغيير النَّوع إذا قُمتَ باعطائها أنواع مختلفة، وتُعطي نتائج مُثيرة للاهتمام في بعض الأحيان: "dog" == "dog"; // true 1 == true; // trueلتجنُّب إجبار النَّوع، استخدم المساواة الثلاثيَّة: 1 === true; // false true === true; // trueهُناكَ أيضًا != و!==. لدى JavaScript أيضًا مُعامِلات أُحاديّة. إذا كنت ترغب في استخدامها. هياكل التَّحكُّملدى JavaScript مجموعة مُماثِلة لهياكل التَّحكُّم الموجودة في لغات أخرى مثل عائلة C. العبارات الشرطيَّة مدعومة بواسطة if وelse؛ يُمكنكَ سَلسَلَتهم معًا إذا أردت: var name = "kittens"; if (name == "puppies") { name += "!"; } else if (name == "kittens") { name += "!!"; } else { name = "!" + name; } name == "kittens!!"لدى JavaScript حلقات while وحلقات do-while. الأولى جيِّدة لعمليات الحلقات البسيطة؛ والثانية مع تلك الحلقات إذا كنت ترغب في التأكُّد من أن جسد الحلقة يتمّ تنفيذه مرة واحدة على الأقل: while (true) { // an infinite loop! } var input; do { input = get_input(); } while (inputIsNotValid(input))For loop في JavaScript هي نفسها التي في C وJava: فهي تُتيح لكَ توفير معلومات التَّحكُّم للحلقة الخاصّة بك في سطرِ واحد. for (var i = 0; i < 5; i++) { // Will execute 5 times }يَستخدم المُعامِلين && و|| دائرة منطقيَّة قصيرة، وهو ما يعني أنَّها تعتمد على مُعامِلها الأوّل في إذا ما كانت ستُنفِّذ مُعامِلها الثاني. وهذا مُفيد لفحص الكائنات الفارغة قبل محاولة الوصول إلى سماتها: var name = o && o.getName();أو لوضع قيم افتراضيّة: var name = otherName || "default";لدى JavaScript مُعامِل ثُلاثي للتعبيرات الشرطيَّة: var allowed = (age > 18) ? "yes" : "no";يُمكن استخدام جُملة switch مع الفروع المتعدِّدة استنادًا إلى عددٍ أو سلسلة: switch(action) { case 'draw': drawIt(); break; case 'eat': eatIt(); break; default: doNothing(); }إذا لم تقم بإضافة break سوف يتوقّف التنفيذ عن المستوى التالي. وهذا نادرًا جدًا ما تُريد أن يحدث - في واقع الأمر هي تستحقّ وصفها على وجه التحديد fallthrough في تعليق إذا كُنتَ حقَا من المفترض أن تساعد في التصحيح: switch(a) { case 1: // fallthrough case 2: eatIt(); break; default: doNothing(); }يُعتبر شرط default اختياري. يُمكنكَ الحااق تعبيرات في كل من جزء switch وcase إذا أردت ذلك. تُجرَي المُقارنات بين اثنين باستخدام ===: switch(1 + 3) { case 2 + 2: yay(); break; default: neverhappens(); }الكائناتيُمكن اعتبار كائنات JavaScript كمجموعات بسيطة من أزواج name-value. على هذا النحو يمكن تشبيهها بـ: * القواميس Dictionaries في لغة Python * التجزئات Hashes في لغتي Perl وRuby * جداول التجزئة Hash Tables في لغتي C و C++ * HashMaps في لغة Java * مصفوفات الترابط Associative arrays في لغة PHP حقيقة أنّ هياكل البيانات تُستخدم في نطاق واسع ما هو إلَّا دليلٌ على تنوُّعها. وحيثُ أنَّ كل شيء (أنواع الشريط الأساسيَّة bar core types) في JavaScript هو كائن، فإنَّ أيّ برنامج JavaScript يحتوي بطبيعة الحال على قدرٍ كبيرٍ من عمليَّات البحث في جدول التجزئة. وهذا شيءٌ جيِّد والعمليَّات سريعة جدًا! جزء الـ"اسم" هو سلسلة JavaScript، في حين أنَّ القيمة يُمكن أن تكون أي قيمة لـ JavaScript - بما في ذلك المزيد من الكائنات. يَسمحُ هذا لك بامكانيَّة بناء هياكل بيانات من نوع التعقيد التعسُّفي. هُناكَ طريقتان أساسيَّتان لإنشاء كائن فارغ: var obj = new Object();و: var obj = {};و: function Person(name, age) { this.name = name; this.age = age; } // Define an object var You = new Person("You", 24); // We are creating a new person named "You" // (that was the first parameter, and the age..)هذه الطُرق مُتعادِلة لغويًّا؛ يُطلق على الثانية جُملة الكائن الحرفيّة، وهي الأكثر ملاءمة. بناء الجملة هذه هو أيضًا جوهر شكل JSON لذلك ينبغي تفضيل استخدامها في جميع الأوقات. يُمكن الوصول إلى سِمَات الكائن بمُجرّد إنشائه بطريقة من اثنتين: obj.name = "Simon"; var name = obj.name;و: obj["name"] = "Simon"; var name = obj["name"];هاتين الطريقتين مُتعادِلتين لُغويًا أيضًا. للطريقة الثانية ميزة ألا وهي أنَّ اسم الصفة المُميّزة property يتم توفيره كسلسلة، مما يعني أنَّه يُمكن حسابها في وقت التشغيل. رغم ذلك استخدام هذا الأسلوب يمنع بعض تحسينات مُحرِّك ومُصغِّر حجم JavaScript من التطبيق. أيضًا لتحديد set والحصول على get يُمكِنُكَ استخدام الخصائص ذات الأسماء المُتعارَف على أنَّها كلمات محجوزة: obj.for = "Simon"; // Syntax error, because 'for' is a reserved word obj["for"] = "Simon"; // works fineمُلاحظة: ابتداءً من EcmaScript 5، يُمكن استخدام الكلمات المحجوزة كأسماء خاصيّة الكائن المُميّزة object property مُجرّدة، وهذا يُعني أنَّها لا تحتاج إلى أن يتمّ وضعها بين اقتباسات عند تحديد حرفيَّات الكائن. راجع ES5 في Spec. يُمكن استخدام جُملة الكائن الحرفيّة لتهيئة كائن في مُجمَله: var obj = { name: "Carrot", "for": "Max", details: { color: "orange", size: 12 } }يُمكن سَلسَلة الوصول إلى الخاصيّة المُميّزة معًا: obj.details.color; // orange obj["details"]["size"]; // 12المصفوفاتفي الواقع المصفوفات في JavaScript هي نوعٌ خاصٌّ من الكائن. تعملُ المصفوفات كثيرًا مثل الأشياء العاديّة (يُمكن الوصول إلى الخصائص العدديّة باستخدام صياغة [] فقط) ولكن لدى المصفوفات خاصيَّة سحريَّة تُسمَّى ‘length’. هذه الخاصيَّة دائمًا ما تَزِيد بواحد عن أعلى مؤشِّر في المصفوفة. هُناك طريقة واحدة لخلق المصفوفات وهي على النحو التالي: var a = new Array(); a[0] = "dog"; a[1] = "cat"; a[2] = "hen"; a.length; // 3هُناك تأشير أكثر ملاءمة ألا وهو استخدام المصفوفات الحرفيَّة: var a = ["dog", "cat", "hen"]; a.length; // 3لاحظ أن array.length ليس بالضرورة عدد العناصر في المصفوفة. مع مراعاة ما يلي: var a = ["dog", "cat", "hen"]; a[100] = "fox"; a.length; // 101تذكَّر - طول المصفوفة يزيد بواحد عن أعلى مؤشِّر بها. إذا استعلمتَ عن مؤشِّر مصفوفة غير موجود، تحصل على نوع غير مُعرَّف undefined: typeof a[90]; // undefinedإذا أخذتَ ما سبق في الاعتبار، يمكنك التكرار عبر مصفوفة باستخدام ما يلي: for (var i = 0; i < a.length; i++) { // Do something with a[i] }هذه الطريقة غير فعّالة بعض الشيء حيثُ أنّكَ تقوم بالبحث عن الخاصيَّة length مرّة واحدة كل حلقة. تحسينٌ لهذا ما يلي: for (var i = 0, len = a.length; i < len; i++) { // Do something with a[i] }هذا أجمل في المظهر ولكن ذو لغة محدودة: for (var i = 0, item; item = a[i++];) { // Do something with item }نقوم هُنا بإعداد مُتغيّرين. يتم اختبار التعيين أيضًا في الجزء الأوسط من الحلقة للتأكُّد من المصداقيّة - إذا نَجَحَت، ستستمرّ الحلقة في العمل. وحيثُ أن i يتزايد في كل مرّة، سيتمّ تعيين العناصر من المصفوفة إلى عناصر في ترتيب تسلسلي. تتوقّف الحلقة عندما يتمّ العثور على قيم "falsy" (مثل نوع undefined). يجبُ عليكَ استخدام هذه الحيلة فقط مع المصفوفات التي تَعْلَمُ أنَّها لا تحتوي على قِيَم "falsy" (على سبيل المثال مصفوفات من كائنات أو نموذج كائن المُستند DOM). إذا كُنتَ تقوم بالتكرار خلال بيانات رقميَّة يُمكن أن تشمل 0 أو بيانات سلسلة يُمكن أن تشمل سلسلة فارغة يجبُ عليكَ بدلًا من ذلك استخدام تعبير i, len. هُناك طريقة أخرى للتكرار وهي استخدام حلقة for…in. لاحظ أنَّهُ إذا قامَ شخصٌ بإضافة خصائص جديدة لـ Array.prototype فإنَّه سيتم تكرارهم من قِبَل هذه الحلقة أيضًا: for (var i in a) { // Do something with a[i] }إذا كُنتَ ترغب في إلحاق عنصر إلى مصفوفة، ببساطة قُم بذلك كما يلي: a.push(item);تأتي المصفوفات مع عدد من الوظائف. انظر أيضًا وثائق كاملة عن أساليب المصفوفة. الوصفاسم الدالَّةتقوم بإرجاع سلسلة مع toString() من كل عنصر مفصولة بفواصل.a.toString()تقوم بإرجاع سلسلة مع toLocaleString() من كل عنصر مفصولة بفواصل.a.toLocaleString()تقوم بإرجاع مصفوفة جديدة مع عناصر مضافة إليها.a.concat(item1[, item2[, ...[, itemN]]])تقوم بتحويل المصفوفة إلى سلسلة - قيم محدَّدة من قبل العامل المُتغيِّر sepa.join(sep)تقوم بإزلة وإرجاع العنصر الأخير.a.pop()تقوم push بإضافة عنصر واحد أو أكثر في النهاية.a.push(item1, ..., itemN)تقوم بعكس المصفوفة.a.reverse()تقوم بإزالة وإعادة العنصر الأول.a.shift()تقوم بإرجاع مصفوفة فرعيَّة (Sub-array).a.slice(start, end)تقوم بأخذ دالَّة مقارنة اختياريَّة.a.sort([cmpfn])تتيح لك تعديل مصفوفة عن طريق حذف قسم واستبداله بمزيد من العناصر.a.splice(start, delcount[, item1[, ...[, itemN]]])تقوم بإلحاق عناصر إلى بداية المصفوفة.a.unshift([item]) الدوالّالدوالّ هي العنصر الأساسيّ في فهم JavaScript جنبًا إلى جنبٍ مع الكائنات. لا يُمكن للدالّة المُجرّدة أن تكون أسهل من التالية: function add(x, y) { var total = x + y; return total; }تدلُّ التعليمات البرمجيَّة هذه على كل شيء يُمكن معرفته عن الدوال الأساسية. يُمكن لدالّة JavaScript أن تستقبل 0 أو أكثر مما تُدعى مُتغيّرات. يُمكن لجسم الدالّة أن يحتوي على عدد ما تُريد من الجُمل statements، ويُمكن أن تُعرّف مُتغيّراتُها الخاصّة والتي تكون محليّة بالنسبة لتلك الدالّة. يُمكن استخدام جُملة return لإرجاع قيمة في أي وقت ثُمَّ إنهاء الدالّة. إذا لم يتمّ استخدام أيّ جُملة إرجاع (أو تمّ استخدامها وكانت فارغة بدون قيمة) فإن JavaScript تقوم بإرجاع نوع 'غير مُعرَّف'. تُعتبر المُتغيّرات المُسمّاه كمبادئ توجيهيّة أكثر من أي شيء آخر. يُمكنكَ استدعاء دالّة دون تمرير المُتغيّر الذي تتوقّعه الدالّة، في هذه الحالة سيتمّ تعيين المُتغيّر كنوع غير مُعرَّف. add(); // NaN // You can't perform addition on undefinedيُمكنكَ أيضًا تمرير عدد من المُتغيّرات أكثر من العدد الذي تتوقّعه الدالّة: add(2, 3, 4); // 5 // added the first two; 4 was ignoredقد يبدو هذا سخيفًا بعض الشيء ولكن لدى الدوال خاصيَّة الوصول إلى مُتغيّرات إضافيّة داخل أجسامهم تُسمّى arguments، والتي هي كائن مثل المصفوفة تحتوي على كافّة القيم التي تمَّ تمريرُها إلى الدالّة. دعونا نُعيد كتابة دالّة الجمع لتأخذ عدد ما نُريد من القيم: function add() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[i]; } return sum; } add(2, 3, 4, 5); // 14رغم ذلك فإنَّ هذا في الحقيقة ليس له أيّ فائدة على مُجرَّد كتابة 2 + 3 + 4 + 5. دعونا نقوم بإنشاء دالّة المتوسِّط: function avg() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[i]; } return sum / arguments.length; } avg(2, 3, 4, 5); // 3.5هذا مفيدٌ جدًا ولكنَّه يُظهِر مُشكلة جديدة. تأخذ دالّة ()avg قائمة من المُتغيّرات مفصولٌ بينَ كلٍّ منها بفاصِلة - ولكن ماذا لو كُنتَ تُريد أن تجد مُتوسّط مصفوفة؟ يُمكنك كتابة الدالّة على النحو التالي: function avgArray(arr) { var sum = 0; for (var i = 0, j = arr.length; i < j; i++) { sum += arr[i]; } return sum / arr.length; } avgArray([2, 3, 4, 5]); // 3.5سيكونُ من الجميل أن يُصبح بمقدورِنا إعادة استخدام الدالَّة التي قُمنا بإنشائِها مُسبقًا. لحُسن الحظّ، تُمكنُّكَ Javascript من استدعاء دالّة مع مصفوفة من قيم المُتغيّرات العشوائيّة، وذلكَ باستخدام وظيفة apply() من أي كائن دالّة. avg.apply(null, [2, 3, 4, 5]); // 3.5قيمة المُتغيّر الثانية لـ ()apply هي المصفوفة المُستخدمة كقيم المُتغيّرات؛ سيتم نقاش المُتغيّر الأول في وقت لاحق. هذا يؤكد حقيقة أن الدوالّ هي كائنات أيضًا. تُتيح لكَ Javascript إنشاء وظائف مجهولة. var avg = function() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[i]; } return sum / arguments.length; }هذا هو ما يُعادل لُغويًا نموذج دالّة ()avg. إنَّها قويَّة للغاية حيثُ أنها تُتيحُ لكَ وضع تعريف دالَّة كاملة في أي مكان يُمكنكَ عادة وضع تعبير expression به. يُتيحُ هذا كل أنواع الحِيَل. إليكَ طريقة لـ "اخفاء" بعض المُتغيّرات المحليّة – مثل نطاق الكتلة block scope في لغة C: var a = 1; var b = 2; (function() { var b = 3; a += b; })(); a; // 4 b; // 2تسمحُ لكَ JavaScript استدعاء دوال بشكلٍ متكرِّر. هذا الأمرُ مفيدٌ بشكلِ خاصّ في التعامل مع هياكل الشجرة tree structures، مثل ما تحصُلُ عليه في نموذج كائن المُستند DOM بالمُتصفّح. function countChars(elm) { if (elm.nodeType == 3) { // TEXT_NODE return elm.nodeValue.length; } var count = 0; for (var i = 0, child; child = elm.childNodes[i]; i++) { count += countChars(child); } return count; }هذا يُسلّط الضَوْء على مشكلةٍ مُحتملةٍ مع الدوال المجهولة: كيف يُمكن استدعاؤها بشكلِ متكرر إذا لم يكُن لديها اسم؟ تُتيحُ لكَ JavaScript تسمية تعبيرات دالَّة لهذا الغرض. يُمكنكَ استخدام تعبيرات تنفيذ الدالَّة مُباشرةً (IIFEs - Immediately Invoked Function Expressions) المُسمّاة على النحو التالي: var charsInBody = (function counter(elm) { if (elm.nodeType == 3) { // TEXT_NODE return elm.nodeValue.length; } var count = 0; for (var i = 0, child; child = elm.childNodes[i]; i++) { count += counter(child); } return count; })(document.body);الاسم المُقدّم إلى تعبير دالّة على النحو الوارد أعلاه إنَّما هو متاحٌ فقط لنطاقِ الدالّة نفسها. يَسمحُ هذا بمزيدِ من التحسينات تُنفَّذ من قِبَل المُحرِّك وتعليمات برمجيَّة أكثرُ قابليَّة للقراءة على حدٍّ سواء. يظهر الاسم أيضًا في المُصَحِّح debugger وبعض stack trace التي يمكن أن تُوفِّر لكَ الوقت. لاحظ أن دوالّ JavaScript هي نفسها كائنات ويُمكنكَ إضافة أو تغيير خصائص بها تمامًا مثل الكائنات الّتي تطرَّقنا إليها في جُزء الكائنات. الكائنات المُخصَّصَةمُلاحظة: للاطّلاع على نقاش أكثر تفصيلًا عن البرمجة الشيئيَّة في JavaScript، انظر مُقدِّمة إلى JavaScript الشيئيّة. في البرمجة الشيئيّة الكلاسيكيّة، الكائنات هي مجموعات من بيانات ووظائف تعملُ على تلك البيانات. JavaScript هي لغة النموذج القائم والتي لا تحتوي على فئات مثل تِلكَ التي توجد في C++ أو Java. (هذا الامرُ مُربِك أحيانًا للمبرمجين مِمَّن اعتادوا على لغات تحتوي على فئات.) بدلًا من ذلك تَستَخدِم JavaScript الدوال كفئات. لنعتبر أن هُناك كائن عبارة عن شخص Person لديه حقلي الاسم الأول والاسم الأخير. هُناك نوعان من الطرق التي من خلالها قد يتمّ عرض الاسم: كما "الأوَّل الأخير" أو "الأخير، الأوَّل". هذه طريقة لفعل ذلك باستخدام الدوال والكائنات التي ناقشناها سابقًا: function makePerson(first, last) { return { first: first, last: last }; } function personFullName(person) { return person.first + ' ' + person.last; } function personFullNameReversed(person) { return person.last + ', ' + person.first; } s = makePerson("Simon", "Willison"); personFullName(s); // "Simon Willison" personFullNameReversed(s); "Willison, Simon"هذه الطريقة تعمل ولكنَّها سيِّئة جدًا. ينتهي بكَ الأمر مع العشرات من الدوال في مساحتك العامَّة global namespace. ما نحتاجه حقًا هو وسيلة لإرفاق دالَّة بكائن. حيثُ أنَّ الدوال هي كائنات، هذا أمر سهل: function makePerson(first, last) { return { first: first, last: last, fullName: function() { return this.first + ' ' + this.last; }, fullNameReversed: function() { return this.last + ', ' + this.first; } }; } s = makePerson("Simon", "Willison") s.fullName(); // "Simon Willison" s.fullNameReversed(); // "Willison, Simon"هُناك شيء في هذه الجُمل التعليميَّة لم نَرَ من قبل: الكلمة المحجوزة this. تُستَخدَم this داخل الدالَّة وتُشير إلى الكائن الحالي. ما يُعنيه ذلك بالتحديد أنه يتمّ التعيين نِتاجًا عن الطريقة التي قُمتَ باستدعاء الدالَّة بها. إذا قمت باستدعائها باستخدام تأشير النقطة أو تأشير القوس في كائن، يُصبِح هذا الكائن this. إذا لم يتمّ استخدام تأشير النقطة dot notation للاستدعاء، تُشير this إلى الكائن العام global object. لاحظ أن this هي سبب مُتكرِّر للأخطاء. على سبيل المثال: s = makePerson("Simon", "Willison"); var fullName = s.fullName; fullName(); // undefined undefinedعندما نستدعي ()fullName وحدها، من دون استخدام ()s.fullName، فإنَّ هذا مُقيَّد بالنطاق العام global scope. وبما أنَّه لا توجد مُتغيِّرات عامَّة global variables تُسمَّى first أو last فإنَّنا نحصل على نوع undefined لكلِّ واحد منهما. يُمكننا الاستفادة من الكلمة المحجوزة this لتحسين دالَّة makePerson: function Person(first, last) { this.first = first; this.last = last; this.fullName = function() { return this.first + ' ' + this.last; }; this.fullNameReversed = function() { return this.last + ', ' + this.first; }; } var s = new Person("Simon", "Willison");قدَّمنا كلمة محجوزة أُخرى: new. ترتبط new ارتباطًا وثيقًا بـ this. ما تفعله هو أنَّها تخلُق كائن فارغ جديد تمامًا، ثم تدعو الدالَّة المُحدَّدة ويتمّ تعيين this لهذا الكائن الجديد. لاحظ أنَّ الدالَّة المُحدَّدة بواسطة this لا تُرجِع قيمة ولكنَّها فقط تقوم بتعديل كائن this. تُرجِع new كائن this إلى موقع الاستدعاء. يُطلَق على الدوال المُصمَّمة ليتمّ استدعاؤها بواسطة new دوال المُنشِئ. مُمارسة شائعة هي كتابة تلك الدوال بأحرف كبيرة كتذكير لاستدعائهم بواسطة new. لا يزال لدى الدالَّة المُحسَّنة نفس المأزق مع استدعاء ()fullName وحدها. بدأت كائنات Person تُصبِح أفضل ولكن لا تزال هُناك بعض الحوافّ السيِّئة لديها. في كل مرة نقوم بإنشاء كائن Person جديد فنحن نقوم بخلق كائني دالَّة جديدين به كذلك - ألن يكون أفضل لو كانت هذه التعليمات البرمجيَّة مُشتَرَكة؟ function personFullName() { return this.first + ' ' + this.last; } function personFullNameReversed() { return this.last + ', ' + this.first; } function Person(first, last) { this.first = first; this.last = last; this.fullName = personFullName; this.fullNameReversed = personFullNameReversed; }هذا أفضل: نقومُ بخلق دوال الوظيفة مرَّة واحدة فقط، ونُعيِّنُ مراجع لهم داخل المُنشئ. هل بإمكاننا فعل ما هو أفضل من ذلك؟ الجواب هو نعم: function Person(first, last) { this.first = first; this.last = last; } Person.prototype.fullName = function() { return this.first + ' ' + this.last; }; Person.prototype.fullNameReversed = function() { return this.last + ', ' + this.first; };Person.prototype هو كائن مُشتَرَك من قِبَل كافَّة نماذج Person. يُشكِّلُ هذا الكائن جُزءًا من سلسلة بحث (لها اسم خاص وهو "سلسلة النموذج" prototype chain): متى حاولتَ الوصول إلى خاصيَّة غير مُعيَّنة في Person، سوف تتحقَّق JavaScript من Person.prototype لترى ما إذا كانت هذه الخاصّيّة موجودة هناك بدلًا من ذلك. نتيجة لذلك، أي شيء مخصَّص لـ Person.prototype يُصبِح متاحًا لجميع النماذج من هذا المُنشئ عبر كائن this. هذه أداة قويَّة بشكلٍ لا يُصدَّق حيثُ أنَّ JavaScript تُتيحُ لكَ تعديل نموذج شيء في أيّ وقت في بَرنامجك، ممَّا يُعني أنَّه يُمكنكَ زيادة وظائف إضافيَّة لكائنات موجودة في وقت التَّشغيل: s = new Person("Simon", "Willison"); s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function Person.prototype.firstNameCaps = function() { return this.first.toUpperCase() }; s.firstNameCaps(); // "SIMON"من المُثير للاهتمام أنَّهُ يُمكنكَ أيضًا إضافة أشياء إلى النموذج الخاصّ بالكائنات المُدمجة في JavaScript. دعونا نُضيف وظيفة إلى سلسلة تقوم بإرجاع هذه السلسلة معكوسة: var s = "Simon"; s.reversed(); // TypeError on line 1: s.reversed is not a function String.prototype.reversed = function() { var r = ""; for (var i = this.length - 1; i >= 0; i--) { r += this[i]; } return r; }; s.reversed(); // nomiSتعمل الطَّريقة الجديدة حتَّى مع حرفيَّات السلسلة string literals "This can now be reversed".reversed(); // desrever eb won nac sihTكما ذكرتُ سالِفًا، يُشكِّل النموذج جزءًا من سلسلة. جذر تلك السلسلة هو Object.prototype والذي من وظائفه ()toString - تلك هي الوظيفة التي يتمّ استدعاؤها عندما تُحاول تمثيل كائن كسلسلة. يُعتبَر هذا مفيدًا من أجل تصحيح كائنات Person: var s = new Person("Simon", "Willison"); s; // [object Object] Person.prototype.toString = function() { return '<Person: ' + this.fullName() + '>'; } s; // "<Person: Simon Willison>"أتذكُرُ كيف احتَوَت ()avg.apply على مُعامِل أوَّل فارغ null؟ يُمكننا إعادة النَّظر في ذلك الآن. المُعامِل الأول في ()apply هو الكائن الذي يجب أن يُعامَل على أنَّهُ 'this'. هذا تنفيذٌ تافه لـ new على سبيل المثال: function trivialNew(constructor, ...args) { var o = {}; // Create an object constructor.apply(o, args); return o; }لا يُعتَبرُ هذا نُسخة طبق الأصل من new لأنها لم تقم بإعداد سلسلة النَّموذج (سيكون من الصَّعب التَّوضيح). هذا ليس شيئًا تستخدمه في كثيرٍ من الأحيان ولكن من المفيد أن تعرف عنه. يُطلَق على مُقتطف ...args (بما في ذلك علامات الحذف) "المُعامِلات المُتبقّية" rest arguments - كما يُوحِي اسمها فهي تحتوي على بقيَّة المُعامِلات. استدعاء var bill = trivialNew(Person, "William", "Orange");يُعادِل تقريبًا var bill = new Person("William", "Orange");لدى ()apply دالَّة أخت اسمها call، والتي أيضًا تُتيحُ لكَ تعيين this ولكنَّها تأخذ قائمة موسَّعة من المُعامِلات بدلًا من مصفوفة. function lastNameCaps() { return this.last.toUpperCase(); } var s = new Person("Simon", "Willison"); lastNameCaps.call(s); // Is the same as: s.lastNameCaps = lastNameCaps; s.lastNameCaps();الدوالّ الداخليّةتعريفات دالَّة JavaScript مسموحٌ بها داخل دوال أخُرى. لقد رأينا هذا من قبل مع دالَّة ()makePerson. هُناك تفصيل مهمّ في الدوال المُتداخلة في JavaScript وهو أنَّه يُمكِنُها الوصول إلى مُتغيِّرات في نِطَاق الدالَّة الأم: function betterExampleNeeded() { var a = 1; function oneMoreThanA() { return a + 1; } return oneMoreThanA(); }يوفِّر هذا قدرًا كبيرًا من الفائدة في كتابة تعليمات برمجيَّة أكثر صيانة. إذا اعتَمَدَتْ دالَّة على واحدة أو اثنتين من الدوال الأُخرى غير المُفيدة لأي جُزء من تعليماتك البرمجيَّة، يُمكنكَ إدخال تلك الدوال ذات المنفعة في الدالَّة التي سيتمّ استدعاؤها من مكان آخر. يُحافظ هذا على عدد من الدوال التي هي في النطاق العام، وهذا دائمًا شيءٌ جيد. يُعتبر هذا عدَّاد كبيرة لإغراء المُتغيِّرات العامَّة global variables. عند كتابة تعليمات برمجيَّة معقَّدة غالبًا ما يكون مُغريًا استخدام مُتغيِّرات عامَّة لتبادُل القيم بين دوالّ مُتعدِّدة - الأمر الذي يؤدِّي إلى تعليمات برمجيِّة من الصعب صيانتها. يُمكنُ للدوالّ المُتداخلة مُشاركة المُتغيِّرات الموجودة في الدالَّة الأم، لذلك يُمكنكَ استخدام هذه الآليَّة لتجميع دالَّتين معًا متى كان هذا منطقيَّا دون تلويث مساحتُكَ العامَّة – ‘محليّات عامَّة’ إذا أردتَ أن تُطلِق عليها ذلك. ينبغي استخدام هذا الأسلوب مع الحذر، إلَّا أنَّ حوذته قُدرة مُفيدة. الإغلاقيقودنا هذا إلى واحدة من التجريدات الأكثر قوّة التي بإمكان Javascript تقديمها - ولكن أيضًا الأكثر تعريضًا للارتباك. ماذا تفعل هذه التعليمات البرمجيَّة؟ function makeAdder(a) { return function(b) { return a + b; }; } var x = makeAdder(5); var y = makeAdder(20); x(6); // ? y(7); // ?يجب أن يكون اسم دالَّة makeAdder قد أوضَح كل شيء: يقوم بخلق دوال 'adder' جديدة، تلك الدوال التي عند استدعائها بواسطة مُعامِل تقوم باضافته إلى المُعامِل الذي تم إنشائهم بواسطته. ما يحدث هُنا هو إلى حدٍّ كبير نفس ما كان يحدث مع الدوال الداخليَّة في وقتٍ سابق: للدالَّة المُعرَّفة داخل دالَّة أخرى حقّ الوصول إلى مُتغيِّرات الدالَّة الخارجيَّة. الفرقُ الوحيدُ هُنا هو أن الدالَّة الخارجيَّة قد عادت، وبالتالي الحس السليم يهدي إلى أن مُتغيِّراتها المحليَّة لم تعد موجودة. لكنها لا تزال موجودة - لولاها دوال adder ما كانت قادرة على العمل. ما هو أكثر من ذلك، هناك نوعان من "نسخ" مختلفة من المُتغيِّرات المحليّة الخاصَّة بـ makeAdder - نُسخة بها a هو 5 ونُسخة بها a هو 20. وبُناءً على ذلك فإن نتيجة استدعاءَات الدالَّة تلك هي على النحو التالي: x(6); // returns 11 y(7); // returns 27إليكَ ما يحدُث فعليًّا. عندما تقوم Javascript بتنفيذ دالَّة يتمّ إنشاء كائن 'نطاق' للاحتفاظ بالمُتغيِّرات المحليَّة التي تم إنشاؤها داخل تلك الدالَّة. يتمّ تهيئتها مع أيّ مُتغيَّر يتمّ ارساله كعامِل دالَّة مُتغيِّر. يُشبه هذا الكائن العام الذي يحتوي على جميع المُتغيِّرات والدوال العامَّة، ولكن هُناك مع بعض الاختلافات الهامَّة: أوَّلُها، يتمَّ إنشاء كائن نطاق جديد تمامًا في كل مرة تبدأ دالَّة بالتنفيذ. ثانيها، لا يُمكن الوصول المُباشِر إلى كائنات النطاق تلك من خلال جُمل Javascript البرمجيَّة الخاصَّة بك، على عكس الكائن العام (الذي يُمكن الوصول إليه كـ this ويُمكن الوصول إليه في المُتصفِّحات كـwindow). على سبيل المثال ليس هُناك آليَّة لتكرار خصائص كائن النِطاق الحالي. لذلك عندما يتم استدعاء makeAdder، يتمّ إنشاء كائن نطاق مع خاصيَّة واحدة: a، والذي هو المُعامِل الذي تمّ تمريره إلى دالَّة makeAdder. تُعيد بعد ذلك makeAdder دالَّة تمّ إنشاؤها حديثًا. في هذه المرحلة عادةً يقوم جامع القُمامة في JavaScript بتنظيف كائن النطاق الذي تمّ خلقه لـ makeAdder، ولكن الدالًّة المُعادة تُحافظ على مرجع إلى كائن النطاق هذا. نتيجة لذلك، لن يكون كائن النطاق القمامة مُجمَّعة إلى أن لا يكون هُناك أيَّة مراجع إلى كائن الدالَّة التي أعادتها makeAdder. تُكوِّن كائنات النطاق سلسلة تُسمَّى سلسلة نطاق scope chain، على غرار سلسلة النَّموذج المُستخدمة من قبل نظام كائن JavaScript. الإغلاق هو مزيجٌ من دالَّة وكائن النِّطاق الذي بِهِ تمّ إنشاء تلك الدالَّة. يُمكِّنُكَ الإغلاق من حفظ الحالة - كما أنَّه كثيرًا ما يمكن استخدامه بدلًا من الكائنات. يُمكِن العثور على عدَّة مُقدِّمات ممتازة للإغلاق هُنا. تسريبات الذَّاكرةأحد الآثار الجانبيّة المؤسفة للإغلاق هو أنَّه يجعل من السَّهل تسرب الذاكرة في Internet Explorer. Javascript هي لغة تجميع البيانات المُهملة - تُخصَّص ذاكرة للكائنات عند إنشائها ويتم استعادة تلك الذَّاكرة من قِبَل المُتصفِّح عندما لا تتبقَّى مراجع إلى كائن. يتم التعامل مع الكائنات التي توفّرها البيئة المضيفة عن طريق تلك البيئة. تحتاج مُستضيفات المُتصفِّح إلى إدارة عدد كبير من الكائنات التي تمثل صفحة HTML المُقدَّمة ألا وهي كائنات الـ DOM. الأمر متروك للمُتصفِّح لإدارة تخصيص واسترداد هذه الكائنات. يستخدم Internet Explorer لهذا آلية جمع القمامة الخاصَّة به، بعيدًا عن الآليَّة المستخدمة لـ JavaScript. إنَّ التفاعل بين الآليَّتين هو ما قد يتسبِّب في تسرُّب الذَّاكرة. يحدث تسرُّب الذاكرة في IE أي وقت يتمّ تشكيل مرجع دائري بين كائن JavaScript وكائن أصلي. تأمَّل ما يلي: function leakMemory() { var el = document.getElementById('el'); var o = { 'el': el }; el.o = o; }تخلُق المراجع الدائريَّة المُشكَّلة بالأعلى تسرُّب للذَّاكرة. لن يقوم IE بتحرير الذَّاكرة المُستخدمة من قِبَل el وo حتَّى يتمّ إعادة تشغيل المُتصفِّح تمامًا. من المُرجَّح ألّا تُلاحَظ الحالة المذكورة أعلاه. يُصبح تسرُّب الذَّاكرة مصدر قلق حقيقي فقط في التطبيقات طويلة التشغيل أو التطبيقات التي تُسرِّب قدرًا كبيرًا من الذَّاكرة بسبب هياكل البيانات الكبيرة أو أنماط التسرُّب داخل الحلقات. نادرًا ما تكون التسريبات واضحة بهذا الشَّكل - في كثيرٍ من الأحيان يمكن أن يكون لهيكل البيانات المسرَّبة عدة طبقات من المراجع تقومُ بالعتيم على مرجع مُعاد. يجعلُ الإغلاق من السَّهل خلق تسرُّب ذاكرة دون قصد. انظر إلى التعليمات البرمجيَّة هذه: function addHandler() { var el = document.getElementById('el'); el.onclick = function() { el.style.backgroundColor = 'red'; }; }تقوم التعليمات البرمجيَّة أعلاه بتحويل العنصر إلى أحمر عند النقر عليه. كما أنَّها أيضًا تخلُق تسرُّب ذاكرة. لماذا؟ لأن الإشارة إلى el قُبِضَت دون قصد في الإغلاق الذي تمَّ إنشاؤه للدالَّة الداخليَّة المجهولة. يخلُق هذا مرجعًا دائريًّا بين كائن JavaScript (الدالَّة) وكائن أصلي (el). هُناك عدد من الحلول لهذه المشكلة. أبسطُها هو عدم استخدام مٌتغيِّر el: function addHandler(){ document.getElementById('el').onclick = function(){ this.style.backgroundColor = 'red'; }; }المُثير للدَّهشة أن هُناك خدعة لكسر المراجعِ الدائريَّة تُقدَّم بواسطة الإغلاق، هذه الخُدعة هي إضافةُ إغلاقٍ آخر: function addHandler() { var clickHandler = function() { this.style.backgroundColor = 'red'; }; (function() { var el = document.getElementById('el'); el.onclick = clickHandler; })(); }يتمُّ تنفيذ الدالّة الداخليّة على الفور وتُخفي محتوياتها عن الإغلاق الذي تمَّ انشاؤه باستخدام clickHandler. خدعة أُخرى جيّدة لتجنب الإغلاق هي كسر المراجعِ الدائريَّة خلال حدث window.onunload. سوف تقوم العديد من مكتبات الحدث بتنفيذِ ذلك بدلًا عنك. لاحظ عمل بذلك يقوم بتعطيل ذاكرة التخزين المؤقت back-forward في فيرفُكس، لذلك يجبُ عليكَ أن لا تُسجِّل مُستمع unload في فَيرفُكس، إلا إذا كان لديك أسباب أخرى للقيام بذلك.1 نقطة