لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 03/18/22 في كل الموقع
-
عندما حاولت مقارنة نصين مختلفين في PHP بإستخدام عامل المساواة == وجد أنه يُعيد True: <?php echo '1e3' == '1000'; // True لماذا يحدث هذا الأمر؟ هنا نقارن بين نصين مختلفين تمامًا، لكن رغم ذلك يتم إعادة True ، أعمل أن 1000 تساوي 1e3 رياضيًا، لكن هنا "1000" (نص String) وليس رقم! لماذا لا يتم إعادة False في هذه الحالة؟3 نقاط
-
تستخدم حلقة for لعمل عدد محدد من التكرارات، مثل التكرار على مجال من الأرقام مثل 1 إلى 10 أي من نقطة بداية إلى نقطة نهاية محددة، أو بالمرور على مصفوفات لأن لها طول ثابت. تنقسم حلقة for ل 3 أجزاء رئيسية: أول جزء يتم تنفيذه مرة واحدة وفيه يتم تعريف متغير الحلقة ثاني جزء نضع فيه الشرط الذي نختبره لكل تكرار للتحقق هل مازال الشرط محقق فتستمر الحلقة لتكرار جديد ويتم اختبار الشرط قبل تنفيذ ما بداخل الحلقة الجزء الثالث يتم تنفيذه بعد إنتهاء تنفيذ جسم الحلقة لكل تكرار وفيه نزيد عداد الحلقة أي نجعله يتغير وينتقل على المجال بين البداية و النهاية، وفي حال التعامل مع مصفوفة سينقل للعنصر التالي أول تعليمة برمجية نكتبها بعد حلقة for يرتبط تنفيذها بتكرار الحلقة (لكل تكرار سيتم تنفيذ أبناء الحلقة) وفي حال كان لدينا أكثر من تعليمة برمجة تنفيذهم مرتبط بالحلقة نضعهم ضمن أقواس المجموعة { } لاحظ في الشيفرة، 1 تم فيه تعريف متغير الحلقة اسمه i مع قيمة ابتدائية 1 (start) الجزء 2 هو شرط تكرار الحلقة ويضمن تحرك (تغير قيمة i) ضمن المجال أصغر أو يساوي 5 الجزء 3 يضمن زيادة عداد الحلقة بقيمة 1 بعد الانتهاء من تنفيذ جسم الحلقة (في مثالنا تعليمة الطباعة) for (let i = 1; i <= 5; i++){ // 1 2 3 // تعليمة برمجة يرتبط تنفيذها بتكرار الحلقة console.log(i) } يمكن توسيع الحلقة لتبسيطها بالشكل التالي: let i = start loop: // بداية جسم الحلقة if (i <= 5) { // شرط الاستمرار console.log(i) // اطبع قيمة العداد الحالية i = i + 1 } else break // توقف end loop: نهاية جسم الحلقة يمكنك قراءة: for في JavaScript من موسوعة حسوب2 نقاط
-
Redis في الأصل اختصار لعبارة "خادم قاموس عن بعد REmote DIctionary Server" التي تشير الى مضمونه مباشرة. وهو بمفهومه البسيط مخزن للبيانات يستعمل نمط المفتاح - القيمة. في الذاكرة بشكل in-memory بشكل يجعل الاستعلام منها سريعا. وهذا ما جعله يستعمل بشكل أولي كخادم تخزين مؤقت cache للبيانات. فهو يسمح للمُستخدِمين بتخزين كميات ضخمة من البيانات بدون التقيد المفروض بقواعد البيانات العلائقيّة (relational database). استعمال خادم تخزين مؤقت يزيد من سرعة وآداء الموقع فالموقع لن يضطر الى الاستعلام من قواعد البيانات كل مرة يقوم فيها مستخدم ما بطلب مورد ما. صحيح أن العملية قد لا تكون ملحوظة عند وجود اعداد قليلة من المستخدمين ولكن تصبح مزعجة عند زيادة عدد الزوار، خصوصا وان كنت تستخدم احد خوادم الاستضافة المشتركة. بمعنى ان الحاجة الى التخزين المؤقت بمفهومه العام تزداد كلما زاد نشاط الموقع. يمكن تخيل كيفية عمل التخزين المؤقت بالشكل التالي: بدل أن يقوم المتصفح باستهداف الخادم ثم قواعد البيانات تختصر العملية بشكل اسرع في : يستهدف المتصفح الخادم ثم خادم التخزين المؤقت، وبما أن البيانات تخزن بشكل in-memory فاستحضارها سيكون بشكل سريع جدا. من بين طرق التخزين المؤقت، توجد redis. وفيما يلي أهم مميزاته: كونه يستعمل نمط تخزين لا علائقي NoSQL في الذاكرة الرئيسية In-memory. وهما الأمران المعروفان بالسرعة والآداء العاليين. خوادم Redis دائما متوفرة ونادرا ما نسمع عن توقف. تستخدم Redis توزيعا جغرافيا ممتازا لتقديم البيانات، فعند طلبها يتم عرضها من اقرب Node النسخ الاحتياطي Backup دعمهم اكثر من رائع في لارافيل، من بين كامل طرق التخزين المؤقت استعمل redis منذ زمن وانصح به.2 نقاط
-
أنا مبتدء في لارافيل Laravel وأحاول أن أقوم بعمنل مشروع صغير، ولكن في كل مرة أقوم بعمل controller من خلال الأمر php artisan أضطر أن أقوم بعمل model وملف request هذا عوضًا عن كتابة كل التوابع الموجودة في المتحكم controller، وأنا أجد أن هذه العملية روتينية للغاية. هل توجد طريقة لجعل هذا الأمر (إنشاء كل هذه الملفات وربطها معًا) يتم بشكل تلقائي؟1 نقطة
-
أنا اعمل على مشروع بإستخدام webpack اخر اصدار وعند التعديل على ملف sass أثناء تشغيل السيرفر يتم عمل تحديث للصفحة ولكن الصور لا تظهر وعند عمل تعديل فى اى ملف اخر مثل ملف index.html مثلا تعود للعمل مرة أخرى فهل هذه مشكلة بالإصدارات وما هى الإصدارات المتوافقه بين CSS و SASS و URL1 نقطة
-
هل تظهر لديك أخطاء معينة عند تشغيل السيرفر ، في حال كان يظهر لديك أخطاء هل يمكنك إرفاقها ، حتى نستطيع مساعدك وفهم المشكلة بشكل جيد .1 نقطة
-
لو سمحت لقد انتهيت من دورة بناء صفحة تشبه يوتيوب ,الدوره فهمتها جيدا وطبقتها بشكل جيد لكلن هناك امور انساها لان الدورة بها معلومات كثيرة هل اراجعها مرة اخري ام ابدا في الدورة التي بعدها1 نقطة
-
هل يمكنك إضافة كود HTML كذلك لأن هناك بعض العناصر لا يتضح معنها من مجرد قراءة id الخاص بها فقط1 نقطة
-
<html> <head> <title>PHP Functions</title> </head> <body> <?php function successfulCompaines($names_array, $rate) { $new_names = []; $index = null; $min = null; foreach ($names_array as $i => $name) { $random_rate = rand(0,100); if ($min == null) { $index = 0; $min = $random_rate; } if ($random_rate <= $rate) array_push($new_names, $name); if ($random_rate < $min) { $index = $i; $min = $random_rate; } } return count($new_names) == 0 ? [$names_array[$index]]:$new_names; } $compaines = array("Lenovo","Huawei","Apple","Amazon","Microsoft","Abb","Netflex","Siemens","Samsung","Adidas","Uber","Dell","Hp","Walmart","Tesla","Google","Volvo","Toyota","Ibm","Shell"); $successRate = 0.5; while(true) { $compaines = successfulCompaines($compaines, $successRate); if (count($compaines) == 1) { echo 'Congrats for '. $compaines[0]; break; } $successRate /=2; } وصلت لهده المرحلة اريد ان يطبع البرنامج على الاقل شركتين او اكثر لانه يطبع لي شركة واجدة فقط1 نقطة
-
المشكلة لديك هي تحديد قيمة صغيرة للغاية للمتغير successRateحيث أن القيمة 0.5 ستكون أصغر من أي قيمة للمتغير random_rate (ماعدا القيمة 0 بالطبع)، لذلك عليك أن تقوم بتغير قيمة المتغير successRate لتكون قيمة أكبر مثل 50 على سبيل المثال: أيضًا عليك أن تقوم بنقل جملة طباعة الجملة Congrats for خارج جملة if لكي يتم طباعة الجملة في كل دورة وليس في آخر دورة فقط، بالشكل التالي: <?php function successfulCompaines($names_array, $rate) { $new_names = []; $index = null; $min = null; foreach ($names_array as $i => $name) { $random_rate = rand(0, 100); if ($min == null) { $index = 0; $min = $random_rate; } if ($random_rate <= $rate) { array_push($new_names, $name); } if ($random_rate < $min) { $index = $i; $min = $random_rate; } } return count($new_names) == 0 ? [$names_array[$index]] : $new_names; } $compaines = array("Lenovo", "Huawei", "Apple", "Amazon", "Microsoft", "Abb", "Netflex", "Siemens", "Samsung", "Adidas", "Uber", "Dell", "Hp", "Walmart", "Tesla", "Google", "Volvo", "Toyota", "Ibm", "Shell"); // نرفع قيمة المتغير التالي $successRate = 50; while (true) { $compaines = successfulCompaines($compaines, $successRate); // ننقل جملة الطباعة إلى هنا echo 'Congrats for ' . $compaines[0]; echo "<br>"; if (count($compaines) == 1) { break; } $successRate /= 2; }1 نقطة
-
يدخل في حلقة لا نهائية #include <stdio.h> void main () { int n; int amount; float Service_charge; double Total_amount; printf("Enter any number to continue or press 0 to stop:>"); scanf("%d",&n); while(n !=0 ){ if(n==0) break; printf("\nEnter the amount:"); scanf("%d",&amount); if(amount < 50){ Service_charge=1; printf("Service charge $ %.2f",Service_charge); printf("\n-------------------------------"); Total_amount=amount + Service_charge; printf("\nTotal amount is $ %.2f",Total_amount); printf("\n-------------------------------");} else { Service_charge = amount * 15/100; printf("\nService charge $ %.2f",Service_charge); printf("\n-------------------------------"); Total_amount=amount + Service_charge; printf("\nTotal amount is $ %.2f",Total_amount); printf("\n-------------------------------"); } } printf("Program end……….") ; }1 نقطة
-
لا يجب أن تستخدم المعامل == عند المقارنة في PHP خصوصًا عند مقارنة النصوص حيث ستحصل على نتيجة مختلفة عما تتوقعه، فعلى سبيل المثال كل العمليات التالية سوف تُعيد True: <?php echo "hi" == 0; // True echo 1 == '1'; // True echo '80000' == '8e4'; // True وذلك لأن PHP تقوم بتحويل أنواع القيم، ومن الأفضل إستخدام المعامل === بدلًا من == عند المقارنة بين قيمتين: <?php echo "hi" === 0; // False echo 1 === '1'; // False echo '80000' === '8e4'; // False1 نقطة
-
لمقارنة السلاسل النصية في PHP بدون تقييم ما تحله السلسلة من تعابير رياضية يمكن أن نسخدم دالة المقارنة strcmp التي تعيد 0 عند تطابق السلسلتين، و قيمة سالبة إن كانت السلسلة الأولى أصغر و قيمة موجبة إن كانت الأولى أكبر <?php $str1 = "Hello"; $str2 = "Hello"; echo strcmp($str1, $str2); // Outputs: 0 $str1 = "Hello"; $str2 = "Hello World"; echo strcmp($str1, $str2); // Outputs: -6 ?> وإن أردت المقارنة بدون حساسية لحالة الأحرف أي A = a نستخدم الدالة strcasecmp حيث أنه بالرفم من اختلاف حالة الأحرف Oo Hh ستعيد لنا الدالة أن السلسلتين متطابقتين. <?php echo strcasecmp("Hello world!","HELLO WORLD!"); // output 0 ?>1 نقطة
-
أعتقد أن هذا تدوين علمي يستخدم للأرقام 1000 = 10 ^ 3 = 1 * 10 ^ 3 = 1 e3 وبعد بعض التجارب والخطأ أعتقد أن هذا صحيح جرب هذا 100 == 1e2 300 == 3e2 5000 == 5e3 فإن الأرقام بعد "e" تعني عدد الموضع في الأعداد العشرية الفاصلة العشرية (تلك "النقطة" في الرقم) التي تم نقلها ... ... يعني e3 أنك تضرب الجزء قبل "e" و 1000 (ثلاثة الأصفار ~ ثلاثة مواضع في رقم عشري)1 نقطة
-
1 نقطة
-
تمام..هبعتها حالا let counter1html = document.getElementById('counter1'); let add1 = document.getElementById('add1'); let add2 = document.getElementById('add2'); let add3 = document.getElementById('add3'); let counterTitle1 = document.getElementById('counterTitle1'); let counterTitle2 = document.getElementById('counterTitle2'); let skip = document.getElementById('skip'); let create = document.getElementById('create'); let timerhtml = document.getElementById('mainTimer'); videoImg.onclick = function () { videoImg.style.display = 'none'; mainVideo.play(); content.style.display = 'none'; } mainVideo.onplay = function () { function counter1 () { let mainTimer = document.getElementById('mainTimer').innerText -= 1; let counter1 = document.getElementById('counter1').innerText -=1; if(mainTimer == 190){ counter1html.style.display = 'block'; counter1html.innerHTML = 10; counterTitle1.style.display = 'block'; } if(mainTimer == 180){ counterTitle1.style.display = 'none'; counterTitle2.style.display = 'block'; counterTitle2.style.zIndex = '20'; counter1html.style.display = 'block'; counter1html.style.zIndex = '20'; counter1html.innerHTML = 10; mainVideo.pause(); add1.style.display = 'block'; add1.play(); } if(mainTimer == 170){ add1.pause(); counterTitle2.style.display = 'none'; counter1html.style.display = 'none'; add1.style.display = 'none'; mainVideo.play(); } if(mainTimer == 150){ counter1html.style.display = 'block'; counterTitle1.style.display = 'block'; counterTitle1.style.zIndex = '20'; counter1html.innerHTML = 10; } if(mainTimer == 140){ counterTitle2.style.display = 'block'; counterTitle1.style.display = 'none'; counter1html.innerHTML = 61; add2.style.display = 'block'; add2.play(); mainVideo.pause(); } if(mainTimer == 130 && counter1html.innerHTML == 51){ skip.style.display = 'block'; } if(mainTimer == 79){ add2.style.display = 'none'; add2.pause(); mainVideo.play(); counterTitle2.style.display = 'none'; counter1html.style.display = 'none'; skip.style.display = 'none' } if(mainTimer == 50){ counterTitle1.style.display = 'block'; counter1html.style.display = 'block'; counter1html.innerHTML = 10; } if(mainTimer == 40){ counterTitle1.style.display = 'none'; counterTitle2.style.display = 'block'; counter1html.innerHTML = 83; mainVideo.pause(); add3.style.display = 'bock'; add3.play() } if(mainTimer == 30){ skip.style.display = 'block' } if(mainTimer == -33){ add3.style.display = 'none'; add3.pause(); counter1html.style.display = 'none'; counterTitle2.style.display = 'none'; skip.style.display = 'none'; mainVideo.play() } skip.onclick = function () { skip.style.display = 'none'; counter1html.style.display = 'none'; counterTitle2.style.display = 'none'; add1.style.display = 'none'; add1.pause(); add2.style.display = 'none'; add2.pause(); add3.style.display = 'none'; add3.pause(); mainVideo.play(); } } setInterval(counter1,1000); } لو مش واضحه ممكن ابعت ملف الصفحة فقط1 نقطة
-
يبدو أنك قد نسيت إرفاق الملف. + أرجو بدلًا من وضع الملف وضع الشفرة البرمجية في تعليق1 نقطة
-
1 نقطة
-
اخذت مشروع تعريب قالب وردبريس و اتضح انني كنت اقوم بترجمة محتوى ديمو القالب و ليس القالب نفسه. و أخبرني صاحب المشروع انه عليي ان اقوم بتجربة القالب على موقع اخر للتأكد من العمل. لا استطيع الوصول إلى القالب جربت عدة طرق عن الانترنت ولكنه يظهر لدي ديمو القالب وليس القالب لذي عليي تعريبه1 نقطة
-
يجب عليك إضافة enctype في ال Form Tag <form method="POST" action="{{route('allPosts.new')}}" enctype="multipart/form-data"> @csrf </form> هذا يستقبل الملفات عند إرسالها في ال Request عند اخراج ال request في dd ستجد احد الخانات تدعى Converted File ، وهو الملفات المحولة والمجهزة للتخزين بداخلها الملف ومساره وكذلك نفس الملف لكن مشفر ، في الغالب ستجد الملف هناك . وليس في خانة ال request .1 نقطة
-
هل من الممكن ان ازيل سطر ال inline-block او سوف يسبب ضرارا .one::first-letter{ display: inline-block ; /*هنا*/ color: greenyellow ; font-size: 50px ; background-color: rgba(123, 255, 0, 0.137); font-weight: bold ; } و شكرا........1 نقطة
-
1 نقطة
-
يمكنك حذف المسار form/ والذي يشير للدالة index ضمن المتحكم، يكفي الاعتماد على مسار واحد، أولا حاول جعل المتغير ضمن المسار إختياري لتخديم أول عرض للصفحة كالتالي: Route::get('form/{view?}', [Formcontroller::class, 'showForm']); وفي دالة المتحكم تقوم إما بعرض النموذج المختار عبر المعامل أو تختار نموذج عشوائي، مع اختيار اسم لنموذج عشوائي آخر حالي كالتالي: public function showForm($view = null) { $forms = [ 'forms.formA', 'forms.formB', 'forms.formC', 'forms.formD' ]; $next_form_name = $forms[array_rand($forms)]; $current_form = view($view ?? $forms[array_rand($forms)]); return view('form', compact('current_form', 'next_form_name')); } وضمن ملف العرض form يجب عرض المحتوى current_form بشكل حرفي حتى يتم إظهار النموذج بشكل سليم كالتالي: @extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-lg-6"> <div> Click here to show a random form </div> <a class="btn btn-primary text-center" href="/form/{{$next_form_name}}"> Submit</a> </div> <div class="col-lg-6"> {!! $current_form !!} </div> </div> </div> @endsection ويجب تعديل كل ملفات عرض النماذج وإزالة القالب الرئيسي منها layouts.app حتى لا يتم تكرار الشريط العلوي1 نقطة
-
1 نقطة
-
يجب عليك إضافة enctype="multipart/form-data" إلى نموذج الإدخال <form method="POST" action="{{route('allPosts.new')}}" enctype="multipart/form-data"> </form> مع التأكد من إضافة @csrf <form method="POST" action="{{route('allPosts.new')}}" enctype="multipart/form-data"> @csrf </form>1 نقطة
-
يجب أن تقوم بتحديد نوع تشفير بيانات النموذج المرسلة الى الخادم من خلال استخدام الخاصية enctype الخاصة بالعنصر form وتحديد قيمتها multipart/form-data كالتالي <form method="POST" action="{{route('allPosts.new')}}" enctype="multipart/form-data"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ثم جرب الامر1 نقطة
-
أعتقد من الأفضل تخزين سلسلة نصية فارغة userSchema.pre('save', function(next) { this.proPromoName = this.proPromoName ? this.proPromoName: ""; this.proDisPrice = this.proDisPrice ? this.proDisPrice: ""; this.proQuantity = this.proQuantity ? this.proQuantity: ""; next(); }) ولكن من الممكن حذف المفتاح بشكل نهائي لكي لا يستهلك أي موارد أبداً.. يمكن المرور على الكائن و حذف أي خاصية فيه قيمتها null مثلاً for(var key in myObject){ if(myObject.hasOwnProperty(key)){ if(myObject[key] == null) delete myObject[key]; } }1 نقطة
-
في لارافل 8 يوجد عدة طرق لوضع الطابع الزمني $table->timestamp('created_at')->useCurrent(); // للتعديل عند التحديث $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); // طريقة أخرى $table->timestamps()->default('CURRENT_TIMESTAMP');1 نقطة
-
من الأفضل السير بنفس ترتيب الدورة، والبدء مع react.js حيث أن بعد هذا المسار سوف يصبح لديك فكرة جيدة عن كيفية عمل react.js في المتصفح، أما مسار Node فيه مشروع عبارة عن مدونة وهذه كواجهات أمامية معمولة عن طريق react.js. غالباً نبدأ بالواجهات الأمامية لأنها أسهل و فيها رسوميات و عناصر تفاعلية تظهر في الشاشة فنحبذ دراستها أولاً ثم لاحقاً نقوم بتطوير منطق التحكم بتدفق البيانات فيها و باقي الأمور. بالتوفيق1 نقطة
-
لإضافة ملاحظة أسفل الحقول توجد طريقتان: الطريقة الأولى في ال Model: تكونُ بإضافة الخاصية help_text إلى حقول ال Model مثالُ ذلك: class MyModel(Model): myfield = models.DateField( help_text = "Please use the following format: <em>YYYY-MM-DD</em>." ) نُلاحظ أننا أضفنا الخاصية help_text إلى الحقل myfield حتى نُوضح للعميل كيفية كتابة التاريخ بشكل صحيح. الطريقة الثانية في ال Form: تكون أيضا بإضافة الخاصية help_texts إلى الصنف Meta المُتواجد داخل ال ModelForm الخاصة بنا مثالُ ذلك: class MyModelForm(forms.ModelForm): class Meta: model = MyModel fields = ['myfield',] labels = {'myfield': "My Field",} help_texts = {'myfield': "Please use the following format: <em>YYYY-MM-DD</em>.",} لاحظ أننا قمنا بإنشاء نموذج ModelForm من MyModel يحتوي على الحقل myfield. قُمنا بإعطاء تسمية للحقل وسميناه My field وقمنا بوضع نص مُساعدة سوف يظهر أسفل الحقل عند عرضه في القالب.1 نقطة
-
يمكنك ايضا استخدام هذه الطريقه للحصول على كل المنشورات التي يزيد عمرها عن 24 ساعة قم اولا بالتوجه الي ملف ال views الخاص بالمشروع وقم باستدعاء التالي: from django.utils.timezone import now # للحصول على التاريخ الحالي from datetime import datetime , timedelta # ال timedelta تمكننا من عمل العمليات الحسابية على التاريخ بعد ذلك توجه الى ال view الذي تريد عمل الاسعلام فيه وقم بأنشاء الاستعلام كلأتي: now = datetime.datetime.now() earlier = now - datetime.timedelta(hours=5) earlier_posts.objects.filter(Created_at__range=(earlier,now)) earlier_posts يحتوي على كل المنشورات التي عمرها أكثر من 24 ساعات ، ولا تقل عن 24 ساعات!1 نقطة
-
هنالك العديد من المشاكل الشائعة تسبب عدم ظهور تطبيق الويب الخاص بك في محرك البحث، إليك بعضها مع الحلول المناسبة لها: تمت معاقبة موقعك وإزالته من فهرس Google لدى Google مجموعة من الإرشادات التي يجب على مالكي مواقع الويب اتباعها حتى يصبح موقعهم الإلكتروني مؤهلاً للترتيب في نتائج البحث. إذا انتهكت هذه الإرشادات أو اشتبهت Google حتى في أنك قد انتهكتها، فسيتم فرض عقوبة على موقعك. وسيؤدي هذا إما إلى تصنيف صفحاتك في مرتبة أدنى أو استبعادها من الفهرس تمامًا. هناك نوعان من العقوبات التي يمكنك الحصول عليها من Google: عقوبة آلية وعقوبة يدوية. تحدث العقوبات الآلية عندما ترى خوارزمية Google أن هناك مشكلات تتعلق بالجودة في موقعك وتقرر معاقبتك وفقًا لذلك. العقوبات اليدوية هي عندما يراجع أحد الأشخاص من مختصي جوجل موقع الويب الخاص بك ويقرر أن موقعك لا يتوافق مع إرشادات الجودة لمشرفي المواقع من Google. الحلول: أسرع طريقة لمعرفة ما إذا كان موقع الويب الخاص بك معاقب من Google هو البحث داخل Google Search Console. انتقل إلى قسم “الأمان والإجراءات اليدوية” وانقر على “الإجراءات اليدوية”. سيوضح لك هذا ما إذا كانت Google قد طبقت أي عقوبات يدوية على موقعك. إذا لم تعاقب ستظهر لك رسالة تقول “لم يتم اكتشاف أي مشاكل”. إذا تم اتخاذ أي إجراءات يدوية ضد موقعك، فسيتم إدراجها جميعًا هناك على الصفحة. 2. تم اختراق موقع الويب الخاص بك وإزالته من Google إذا تم اختراق موقع الويب الخاص بك ، فقد يؤثر ذلك على قدرتك على الظهور في نتائج بحث Google. حيث يمكن إدراج الموقع في القائمة السوداء وإزالته من الفهرس بالكامل أو يمكن لـ Google اختيار إضافة رسالة إلى أي نتائج يصنفها موقعك لقولها “قد تم اختراق هذا الموقع”. وبعد إختراقه ، فقد يظل موقعك موجودًا على Google ولكن فرص قيام الزوار بالنقر للوصول إلى الصفحة منخفضة. الحلول: يمكنك التحقق لمعرفة ما إذا كان الاختراق يؤثر على موقع الويب الخاص بك في Google من خلال حساب Search Console. انتقل إلى قسم “مشكلات الأمان” وابحث لمعرفة ما إذا تم إدراج أي عناوين URL. يمكنك الاطلاع على المقالة التالية:1 نقطة
-
يمكنك استدعاء المنشورات التي يزيد وقت إنشاءها عن 24 ساعة عن طريق الكود التالي : from django.shortcuts import render from .models import Post from datetime import datetime , timedelta from django.utils.timezone import now def calldate(request): time_threshold = datetime.now() - timedelta(hours=24) result = Post.objects.filter(date__lt=time_threshold) context = {'result' : result } return render(request , 'postdate.html' , context) حيثُ تم إنشاء class داخل ملف views.py متغير time_threshold يقوم بتعريف الوقت فيما معناه ( 24 ساعه بعد الوقت الحالي ) ثم استدعاء المنشورات في متغير جديد result يأخذ الوقت من المتغير الذي قمنا بكتابته في السطر السابق بالعلم أن ما بين الأقواس " date__lt " ينقسم إلى " lt " وهي مناسبة في حالة استدعاء المنشورات التي تزيد عن 24 ساعه و أن date الملاصقه للكلمة It هي متغيره حسب اسم الحقل الذي يحتوي على التاريخ في النموذج داخل models.py و التعويض بالطريقه الإفتراضيه عن طريق الـ context داخل ملف الـ html الخاص بالمنشورات1 نقطة
-
أول شيفرة مسؤولة عن إظهار زر الدفع، نضعها بجانب أزرار الدفع الأخرى أو بعدهم في الشيفرة ليتم عرض الزر. <div id="smart-button-container"> <div style="text-align: center;"> <div id="paypal-button-container"></div> </div> </div> أما بالنسبة لعناصر script يمكنك وضعهم قبل نهاية الوسم body في الأسفل ليتم تحميلهم مع إنتهاء تحميل الصفحة.. توثيق شوبيفاي للدفع عن طريق باي بال تجده من هنا ربما تحتاج لتنسيق الأزرار أو تعديل الأكواد (من الممكن) لذلك أنصح بتوظيف مبرمج ليساعدك، و يمكنك ذلك من خلال موقع خمسات أو مستقل.1 نقطة
-
نهدف خلال تقدمنا في المنهاج، إلى تعلم ما يكفي عن JavaScript، فهذا أمر بالغ الأهمية، إضافة إلى تطوير الويب. لقد تطورت JavaScript بسرعة خلال السنوات القليلة الماضية، لذلك سنعتمد هنا الميزات التي توفرها النسخ الجديدة. يطلق على معيار JavaScript رسميًا اسم ECMAScript. وتعتبر النسخة ECMAScript® 2020 هي الأحدث بعد إصدارها في يونيو (حزيران) لعام 2020، كما تُعرف بالرمز ES11. لا تدعم المتصفحات الميزات الأحدث للغة JavaScript حتى الآن. لذلك نقلت الشيفرة (transpiled) التي تنفذها المتصفحات من النسخ الأحدث إلى النسخ الأقدم لتكون أكثر توافقًا. يعتبر استخدام Babel حاليًا أكثر الطرق شعبية لإجراء عملية النقل. وتُهيّأ عملية النقل آليًا في تطبيقات React التي تبنى باستخدام create-react-app. سنلقي نظرة أقرب إلى تهيئة عمليات النقل في القسم 7 لاحقًا. تُعرَّف Node.js بأنها بيئة لتنفيذ شيفرة JavaScript مبنية على محرك JavaScript الذي طورته Google والمعروف باسم chrome V8، وتعمل هذه البيئة في أي مكان من الخوادم إلى الهواتف النقّالة. سنتدرب على كتابة شيفرة JavaScript باستخدام Node، لذلك ينبغي أن تكون نسخة Node.js المثبتة على جهازك 10.18.0 على الأقل. وطالما أنّ أحدث نسخ Node ستتوافق تلقائيًا مع أحدث نسخ JavaScript، لذلك لا حاجة لنقل الشيفرة. تُكتب الشيفرة في ملفات تحمل اللاحقة js. وتنفذ من خلال الأمر node name_of_file.js، كما يمكن أن نكتبها ضمن طرفية Node.js التي نفتحها بكتابة الأمر node في سطر الأوامر، وأيضًا بكتابة الأمر نفسه في طرفية التطوير الخاصة بالمتصفح. تتماشى التنقيحات الأخيرة للمتصفح Chrome جيدًا مع ميزات JavaScript الأحدث دون الحاجة إلى نقل الشيفرة. كما يمكن استخدام أدوات بديلة مثل JS Bin. تُذكِّرنا JavaScript من حيث الاسم والتعابير بلغة Java. لكن من ناحية آلية العمل فهي أبعد ماتكون عنها. فإذا نظرت إليها من منظور مبرمجي Java، سيبدو سلوكها غريبًا قليلًا وخاصة إن لم تكلّف نفسك عناء البحث في ميزاتها. لقد شاع فيما مضى محاكاة ميزات Java في تصميم نماذج JavaScript، لكننا لا نحبّذ هذا الأمر لأن اللغتين والبيئتين اللتين تعملان ضمنهما تختلفان كليًا. المتغيرات Variables تُعرَّف المتغيرات في JavaScript بطرقٍ عدة: const x = 1 let y = 5 console.log(x, y) // 1, 5 y += 10 console.log(x, y) // 1, 15 y = 'sometext' console.log(x, y) // 1, sometext x = 4 // خطا لا تُعرّف الكلمة const متغيرًا، بل تدل على قيمة ثابتة (constant) لايمكن بعد ذلك تغييرها. بالمقابل تُعرّف الكلمة let متغيّرًا عاديًا. يمكن أن نرى بوضوح من خلال المثال السابق أن المتغيّرات قد تأخذ بيانات من أنواع مختلفة أثناء التنفيذ، ففي البداية يخزن المتغير y قيمة صحيحة، ثم سلسة نصية في النهاية. كما تُستخدم الكلمة var في تعريف المتغيّرات، فهذه الكلمة ولفترة طويلة كانت الطريقة الوحيدة لتعريف المتغيّر، ثم أضيفت const وlet مؤخرًا في النسخة ES6. وقد تعمل الكلمة var بطريقة مختلفة لتعريف المتغيرات مقارنة بالطريقة التي تُعرّف بها في معظم اللغات. سنلتزم في منهاجنا باستخدام const وlet عند تعريف المتغيرات. يمكنك الاطلاع على المزيد حول هذا الموضوع عبر YouTube كالفيديو التالي: var, let and const - ES6 JavaScript Features، أومن خلال المقاليين التاليين في أكاديمية حسوب: المتغيرات في JavaScript إفادة var القديمة في JavaScript المصفوفات Arrays تُعرِّف الشيفرة التالية مصفوفة وتقدم مثالين عن كيفية استخدامها: const t = [1, -1, 3] t.push(5) console.log(t.length) // 4 console.log(t[1]) // -1 t.forEach(value => { console.log(value) // 1, -1, 3, 5 }) الأمر الملفت في هذه الشيفرة، هو إمكانية تعديل محتوى المصفوفة بالرغم من كونها ثابتة (const). ذلك أن المصفوفة عبارة عن كائن والمتغيّر سيشير إلى نفس الكائن دومًا حتى لو تغيّر محتواه بإضافة العناصر الجديدة. تستخدم تعليمة forEach كطريقة لتعداد عناصر المصفوفة في الشيفرة السابقة. حيث تمتلك forEach مُعاملًا على شكل دالة سهمية لها الصيغة التالية: value => { console.log(value) } تستدعي forEach الدالة من أجل كل عنصر من عناصر المصفوفة، وتمرر كل عنصر كمُعامل. يمكن للدالة التي تمثل مُعاملًا لتعليمة forEach أن تمتلك مُعاملات أخرى خاصة بها. كما استخدم التابع push لإضافة عنصر جديد إلى المصفوفة في المثال السابق. تستخدم عادة البرمجة بأسلوب الدوال (functional programming) مع React. ولهذا الأسلوب ميزة استخدام بنى المعطيات الصامدة (Immutable). ويفضل عند كتابة شيفرة React استخدام التابع concat، فلا يضيف هذا التابع عنصرًا جديدًا إلى المصفوفة، بل ينشئ مصفوفة جديدة تضم محتويات القديمة بالإضافة إلى العنصر الجديد. const t = [1, -1, 3] const t2 = t.concat(5) console.log(t) // [1, -1, 3] console.log(t2) // [1, -1, 3, 5] لن يضيف الأمر (t.concat(5 عنصرًا جديدًا إلى المصفوفة القديمة، بل سيعيد مصفوفة جديدة تضم العنصر الجديد (5) بالإضافة إلى عناصر المصفوفة القديمة. لقد عُرّفت الكثير من التوابع المفيدة للتعامل مع المصفوفات، فلنلقي نظرة على استخدام التابع map على سبيل المثال: const t = [1, 2, 3] const m1 = t.map(value => value * 2) console.log(m1) // [2, 4, 6] يُنشئ التابع map مصفوفةً جديدةً مبنيةً على أساس القديمة، وتستخدم الدالة التي مُررت كوسيط لإنشاء عناصر المصفوفة الجديدة. لاحظ في مثالنا السابق كيف نتجت العناصر الجديدة عن القديمة بضربها بالعدد 2. ويمكن للتابع map تحويل المصفوفة إلى شيءٍ مختلفٍ تمامًا: const m2 = t.map(value => '<li>' + value + '</li>') console.log(m2) // [ '<li>1</li>', '<li>2</li>', '<li>3</li>' ] لقد تحولّت المصفوفة المكوَّنة من أعداد صحيحة إلى مصفوفة تحوي عبارات HTML باستخدام map. سنستخدم هذا التابع بكثرة في React كما سنرى في القسم 2 لاحقًا. يمكن إسناد قيمة كل عنصر من المصفوفة إلى المتغيّرات بطريقة الإسناد بالتفكيك (destructuring assignment). const t = [1, 2, 3, 4, 5] const [first, second, ...rest] = t console.log(first, second) // 1, 2 console.log(rest) // [3, 4 ,5] "فُكّكت" عناصر المصفوفة t باستخدام الإسناد بالتفكيك، وأسند أول عنصرين منها إلى المتغيّرين first وsecond، ثم جُمّعت بقية عناصر المصفوفة في مصفوفة جديدة أسندت بدورها إلى المتغيّر rest. الكائنات Objects توجد طرق عدة لتعريف الكائنات في JavaScript، وأكثر هذه الطرق شيوعًا هي استخدام الشكل المختصر لتعريف الكائنات (object literals) والتي تُنشأ بوضع خصائصها على شكل قائمة عناصر تفصل بينها الفواصل بين قوسين معقوصين. const object1 = { name: 'Arto Hellas', age: 35, education: 'PhD', } const object2 = { name: 'Full Stack web application development', level: 'intermediate studies', size: 5, } const object3 = { name: { first: 'Dan', last: 'Abramov', }, grades: [2, 3, 5, 3], department: 'Stanford University', } تأخذ الخصائص قيمًا من أي نوع مثل الصحيحة والنصية والمصفوفات والكائنات، وتعرّف خصائص كائن ما باستخدام النقطة "." أو باستخدام الأقواس المعقوفة: console.log(object1.name) // Arto Hellas const fieldName = 'age' console.log(object1[fieldName]) // 35 كما تُضاف الخصائص إلى الكائن مباشرة باستخدام النقطة "." أو الأقواس المعقوفة: object1.address = 'Helsinki' object1['secret number'] = 12341 لقد توجب علينا استخدام الأقواس المعقوفة عند إضافة الخاصية الأخيرة، ذلك أن الاسم (secret number) لا يصلح اسمًا لخاصية نظرًا لوجود فراغ بين الكلمتين. تمتلك الكائنات في JavaScript توابعًا أيضًا، لكننا لن نستخدم في المنهاج كائنات لها توابعها الخاصة، وسنكتفي بمناقشتها بإيجاز خلال تقدمنا. تُعرّف الكائنات أيضًا باستخدام الدوال البانية (constructor functions) التي تشابه في آلية عملها مثيلاتها في لغات برمجة أخرى، كدوال البناء في صفوف Java. مع ذلك، لا تمتلك JavaScript صفوفًا بالمفهوم الذي نراه في البرمجة كائنية التوجه (OOP). أضيفت الصيغة class للغة JavaScript ابتداء من النسخة ES6، والتي تساعد في بعض الحالات على بناء صفوف كائنية التوجه. الدوال Functions تعرّفنا سابقًا على استخدام التوابع السهمية والتي تُكتب بصيغتها الكاملة على النحو: const sum = (p1, p2) => { console.log(p1) console.log(p2) return p1 + p2 } وتُستدعى كما هو متوقع على النحو: const result = sum(1, 5) console.log(result) يمكن تجاهل كتابة الأقواس المحيطة بالمُعاملات إن كان للدالة مُعامل واحد: const square = p => { console.log(p) return p * p } وإن احتوت الدالة على عبارة برمجية واحدة، لا حاجة عندها لاستخدام الأقواس المعقوصة، وستعيد الدالة القيمة الناتجة عن تنفيذ هذه العبارة. لاحظ كيف سيُختصر تعريف الدالة لو أزلنا العبارة التي تطبع النتيجة على الطرفية: const square = p => p * p ولهذا الشكل أهميته التطبيقية عندما نتعامل مع المصفوفات وخاصة باستعمال التابع map: const t = [1, 2, 3] const tSquared = t.map(p => p * p) // tSquared is now [1, 4, 9] أضيفت الدوال السهمية إلى JavaScript مع النسخة ES6 منذ سنتين تقريبًا. وقد عُرّفت الدوال قبل ذلك بالكلمة function فقط. وعمومًا هناك طريقتان لتعريف الدوال، الأولى تسمية الدالة بالتصريح عنها (function declaration): function product(a, b) { return a * b } const result = product(2, 6) // result is now 12 أما الطريقة الثانية فهي استخدام تعبير تعريف دالة (function expression)، وفي هذه الحالة لا داع لإعطاء الدالة اسمًا: const average = function(a, b) { return (a + b) / 2 } const result = average(2, 5) // result is now 3.5 سنستخدم في هذا المنهاج الدوال السهمية دائمًا. التمارين 1.3- 1.5 سنكمل بناء التطبيق الذي بدأناه سابقًا، لهذا يمكنك متابعة كتابة الشيفرة في نفس المشروع طالما أن المهم هو الحالة النهائية للتطبيق الذي ستسلّمه. نصيحة للاحتراف: قد تواجهك عقبات تتعلق ببنية الخصائص التي تمتلكها المكوِّنات. اجعل الأمر أكثر وضوحًا بطباعة الخصائص على الطرفية كالتالي: const Header = (props) => { console.log(props) return <h1>{props.course}</h1> } 1.3 معلومات عن المنهاج: الخطوة 3 سنمضي قدمًا نحو الأمام مستخدمين الكائنات في تطبيقنا. عدّل تعريف المتغيّر في المكوّن App بالشكل التالي وأعد صياغة التطبيق حتى يبقى قابلًا للتنفيذ: const App = () => { const course = 'Half Stack application development' const part1 = { name: 'Fundamentals of React', exercises: 10 } const part2 = { name: 'Using props to pass data', exercises: 7 } const part3 = { name: 'State of a component', exercises: 14 } return ( <div> ... </div> ) } 1.4 معلومات عن المنهاج: الخطوة 4 ضع بعد ذلك الكائنات في مصفوفة. عدّل تعريف المتغيّر في المكوِّن App إلى الشكل التالي ولا تنس تعديل بقية الأجزاء في المكوِّن بما يتوافق معه: const App = () => { const course = 'Half Stack application development' const parts = [ { name: 'Fundamentals of React', exercises: 10 }, { name: 'Using props to pass data', exercises: 7 }, { name: 'State of a component', exercises: 14 } ] return ( <div> ... </div> ) } ملاحظة: اعتبر حتى هذه اللحظة أن هناك ثلاثة عناصر فقط ضمن المصفوفة لذلك لا ضرورة لاستخدام الحلقات عند التنقل بين العناصر. سنعود لاحقًا بتفصيل أعمق إلى موضوع تصيير الكائنات المبنية من عناصر مصفوفة في القسم التالي. لاتُمرِّر كائنات مختلفة على شكل خصائص من المكوِّن App إلى المكوِّنين Content و Total، بل مررها مباشرة كمصفوفة: const App = () => { // const definitions return ( <div> <Header course={course} /> <Content parts={parts} /> <Total parts={parts} /> </div> ) } 1.5 معلومات عن المنهاج: الخطوة 5 لنتقدم خطوة أخرى إلى الأمام. حوّل المنهاج وأقسامه إلى كائن JavaScript واحد، وأصلح كل ما يتضرر: const App = () => { const course = { name: 'Half Stack application development', parts: [ { name: 'Fundamentals of React', exercises: 10 }, { name: 'Using props to pass data', exercises: 7 }, { name: 'State of a component', exercises: 14 } ] } return ( <div> ... </div> ) } توابع الكائنات والمؤشر "this" لن نستخدم كما ذكرنا سابقًا كائنات لها توابعها الخاصة، ذلك أننا سنعتمد على نسخة من React تدعم الخطافات. لن تجد فيما سيأتي لاحقًا في هذا الفصل ما يتعلق بالمنهاج، لكن من الجيد الاطلاع على المعلومات الواردة وفهمها وخاصة عندما تستخدم نسخًا أقدم من React. يختلف سلوك الدوال السهمية عن تلك المعرّفة باستخدام التصريح function فيما يتعلق باستخدام الكلمة this التي تشير إلى الكائن نفسه. من الممكن إسناد التوابع إلى كائن بتعريف بعض خصائصه على شكل دوال: const arto = { name: 'Arto Hellas', age: 35, education: 'PhD', greet: function() { console.log('hello, my name is ' + this.name) },} arto.greet() // "hello, my name is Arto Hellas" وقد تسند التوابع إلى الكائنات حتى بعد إنشاء هذه الكائنات: const arto = { name: 'Arto Hellas', age: 35, education: 'PhD', greet: function() { console.log('hello, my name is ' + this.name) }, } arto.growOlder = function() { this.age += 1} console.log(arto.age) // 35 is printed arto.growOlder() console.log(arto.age) // 36 is printed لنعدّل الكائن كما يلي: const arto = { name: 'Arto Hellas', age: 35, education: 'PhD', greet: function() { console.log('hello, my name is ' + this.name) }, doAddition: function(a, b) { console.log(a + b) },} arto.doAddition(1, 4) // 5 is printed const referenceToAddition = arto.doAddition referenceToAddition(10, 15) // 25 is printed يمتلك الكائن الآن التابع doAddition الذي يجمع الأعداد التي تمرر إليه كوسطاء. يستدعى هذا التابع بالطريقة التقليدية (arto.doAddition(1, 4، أو بإسناد مرجعٍ للتابع إلى متغّير ومن ثم استدعاءه من خلال المتغيّر كما هو الحال في السطرين الأخيرين من الشيفرة السابقة. لكن لو حاولنا استخدام الطريقة ذاتها مع التابع greet سنواجه مشكلة: arto.greet() // "hello, my name is Arto Hellas" const referenceToGreet = arto.greet referenceToGreet() // "hello, my name is undefined" فعندما نستدعي التابع باستخدام مرجع لكائن، سيفقد التابع القدرة على تحديد الكائن الذي تشير إليه الكلمة this. وعلى نقيض بقية اللغات، تُحدّد قيمة this في JavaScript وفقًا للطريقة التي يُستدعى بها التابع. فعندما تُستدعى من خلال مرجع تتحول this إلى كائن عام (global object) ولن تكون النتيجة ما يريده المطوّر في أغلب الأوقات. تقودنا الحالة السابقة لمواجهة العديد من المشاكل وخاصة عندما تحاول React أو Node استدعاء توابع عرفها المطوّر للكائن. لذلك سنتغلب على هذه العقبة بكتابة شيفرة JavaScript لا تستعمل this. لكن هناك حالة خاصة تظهر عندما نحاول ضبط قيمة انتهاء الوقت (timeout) للدالة greet التي تمثل جزءًا من اللكائن arto باستخدام setTimeout. const arto = { name: 'Arto Hellas', greet: function() { console.log('hello, my name is ' + this.name) }, } setTimeout(arto.greet, 1000) تُحدّد قيمة this في JavaScript وفقًا للطريقة التي يُستدعى بها التابع كما أشرنا سابقًا. فعندما نستخدم الدالة setTimeout لاستدعاء تابع، فإن محرك JavaScript هو من يستدعى التابع حقيقةً، وستشير this عندها إلى كائن عام. هناك آليات عديدة للاحتفاظ بمرجع this الأصلي، منها استدعاء الدالة bind كالتالي: setTimeout(arto.greet.bind(arto), 1000) سينشئ الاستدعاء (arto.greet.bind(arto دالة جديدة تشير فيها this إلى Arto بصرف النظر عن الطريقة التي تم بها استدعاء التابع. تُستخدم الدوال السهمية في بعض الأحيان لحل بعض مشاكل this. لكن لا ينبغي استخدام هذه الدوال كتوابع لكائنات، لأن this لن تعمل عندها أبدًا. سنعرّج لاحقًا على توضيح سلوك this مع الدوال السهمية. تمتلئ صفحات الانترنت بمعلومات عن استخدام this في JavaScript، إن أردت فهم ذلك أكثر، نوصيك بشدة متابعة سلسة Understand JavaScript's this Keyword in Depth التي أعدها egghead.io على سبيل المثال. الأصناف Classes لا تمتلك JavaScript كما أشرنا سابقًا صفوفًا تشابه تلك التي تقدمها اللغات كائنية التوجه. لكنها تمتلك ميزات تمكّننا من محاكاة تلك الأصناف. لنلق نظرة سريعة على الصيغة class التي قدمتها النسخة ES6 والتي تبسّط تعريف الأصناف (أو الأغراض الشبيه بالأصناف). سنعرّف في المثال التالي صفًا يسمى Person وكائنين Person: class Person { constructor(name, age) { this.name = name this.age = age } greet() { console.log('hello, my name is ' + this.name) } } const adam = new Person('Adam Ondra', 35) adam.greet() const janja = new Person('Janja Garnbret', 22) janja.greet() تتشابه الأصناف والكائنات الناتجة من الناحية التعبيرية (Syntax) مع تلك التي تقدمها Java، كما تتشابه من ناحية السلوك أيضًا. لكنها تبقى في الصميم كائنات ناتجة عن وراثة نماذج أولية (prototypal inheritance) تقدمها JavaScript. فنمط كلٍّ من الكائنين عمليًا هو Object، لأن JavaScript تعرّف افتراضيًا أنواع البيانات الأساسية التالية: Boolean و Null و Undefined و Number و String و Symbol و Object فقط. على أية حال كان تقديم الصيغة class محط خلاف، يمكنك القراءة أكثر عن ذلك في المقالة Not Awesome: ES6 Classes أو في المقالة ?Is “Class” In ES6 The New “Bad” Part. لقد استخدمت هذه الصيغة مرارًا في النسخ القديمة من React وكذلك في Node.js، لكننا لن نستخدمها في المنهاج كبنية أساسية، طالما أننا سنستخدم ميزة الخطافات الجديدة في React. مواد إضافية لتعلم JavaScript تقدم أكاديمية حسوب توثيقًا عربيًا شاملًا مترجمًا عن الموقع javascript.info وننصح بشدة الاطلاع على كل المقالات الموجودة. بالإضافة إلى ذلك ستجد على شبكة الانترنت الكثير من المواد المتعلقة باللغة منها الجيد ومنها السيء، فيمكنك الاطلاع على المراجع التي تقدمها شركة Mozilla والمتعلقة بميزات JavaScript من خلال الدليل Mozilla's Javascript Guide. كما نوصيك بشدة أن تتطلع مباشرة على الدورة التعليمية A re-introduction to JavaScript JS tutorial على موقع الويب لشركة Mozilla. إن أردت التعمق بهذه اللغة نوصيك بسلسلة كتب مجانية ممتازة على الإنترنت تدعى You-Dont-Know-JS. يعتبر الموقع egghead.io مرجعًا جيدًا حيث يضم شروحات قيّمة عن JavaScript وعن React وغيرها من المواضيع الهامة، لكن ليست جميع المواد الموجودة مجانية. ترجمة -وبتصرف- للفصل JavaScript من سلسلة Deep Dive Into Modern Web Development1 نقطة