المحتوى عن 'دليل تعلم جافاسكربت'.



مزيد من الخيارات

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

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

نوع المُحتوى


التصنيفات

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

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
    • React
  • HTML
    • HTML5
  • CSS
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • Sass
    • إطار عمل Bootstrap
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • ASP.NET
    • ASP.NET Core
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • سهولة الوصول
  • مقالات برمجة عامة

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

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

التصنيفات

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

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

  1. يجب أن تكون الشيفرة البرمجية مرتبة ونظيفة وسهلة القراءة قدر الإمكان. هذا ما يسمى بفن البرمجة وهو أخذ مهمة معقدة وبرمجتها بطريقة صحيحة ومقروءة في الوقت ذاته. يساعد نمط كتابة الشيفرة كثيرا في ذلك. الصياغة في الصورة أدناه يوجد صفحة تحوي بعض القواعد المهمة في تركيب الشيفرات البرمجية. سنناقش هذه القواعد وأسبابها بالتفصيل. ملاحظة: لا يوجد قواعد الزامية. لا يوجد قاعدة إلزامية لنمط التكويد. هذه القواعد تعد تفضيلات وليست أساسيات برمجية. الأقواس المعقوصة (Curly Braces) تُكتب الأقواس المعقوصة في معظم مشاريع JavaScript المعقوصة بالطريقة "المصرية" بوضع قوس الفتح في نفس السطر الذي يحوي الكلمة المفتاحية - ليس في سطر جديد. يجب وضع مسافة قبل القوس الافتتاحي كما يلي: if (condition) { // افعل هذا //…. وذاك //…. وذاك } التعليمة المكونة من سطر واحد مثل if(condition) doSomething()‎ تعد حالة طرفية مهمة، فهل يجب استخدام الأقواس فيها؟ فيما يلي بعض البدائل المشروحة. يمكنك قراءتها والحكم على درجة سهولة قراءتها بنفسك: 1- يقوم بعض المبتدئون أحيانًا بما يلي. ما يعد ممارسة خاطئة. فلا حاجة لِلأقواس المعقوصة هنا: if (n < 0) {alert(`Power ${n} is not supported`);} 2- الانتقال إلى سطر جديد بدون استخدام أقواس. تجنب هذا الأمر لأنه يسبب بعض الأخطاء: if (n < 0) alert(`Power ${n} is not supported`); 3- سطر واحد بدون أقواس يُعد مقبولا في حال كان السطر قصيرًا: if (n < 0) alert(`Power ${n} is not supported`); 4- أفضل الطرق: if (n < 0) { alert(`Power ${n} is not supported`); } يمكن استخدام سطر واحد في حالة الشيفرات البرمجية المختصرة مثل: if (cond) return null. لكن استخدام شيفرة برمجية كتلية (كما في رقم 4) هو الأفضل من ناحية سهولة القراءة. طول السطر لا يحب أحدٌ قراءة سطر برمجي طويل. أصبح فصل الأسطر الطويلة ممارسة عامة لدى الجميع. إليك المثال التالي: // تسمح أقواس الفاصلة العليا المائلة ` بتقسيم النص إلى عدة أسطر let str = ` Ecma International's TC39 is a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript. `; بالنسبة للتعليمة البرمجية if: if ( id === 123 && moonPhase === 'Waning Gibbous' && zodiacSign === 'Libra' ) { letTheSorceryBegin(); } يتم الاتفاق على الحد الأقصى لطول السطر البرمجي على مستوى فريق العمل. يكون طول السطر البرمجي غالبا 80 أو 120 محرفَا. مسافة البادئة يوجد نوعان من البادئة "Indents": البادئة الأفقية: 2 أو 4 مسافات: تُصنع البادئة الأفقية بوضع 2 أو 4 مسافات أو باستخدام رمز البادئة الأفقية (الزر Tab). وُجدت اختلافات قديمة حول أيهما أفضل، لكن المسافات هي الأكثر شيوعا هذه الأيام. تتميز المسافات عن الزر Tab بكونها أكثر مرونة أثناء التعديل. مثلًا، يمكننا إزاحة المتغيرات داخل قوس مفتوح كالتالي: show(parameters, aligned, // 5 spaces padding at the left one, after, another ) { // ... } البادئة العمودية: الأسطر الفارغة لفصل الشيفرات البرمجية إلى أجزاء منطقية: يمكن تقسيم حتى دالة بحد ذاتها إلى أجزاء منطقية لتسهل قرائتها. في المثال أدناه، تم تقسيم تعريف المتغيرات وحلقة التكرار الرئيسية والنتيجة عموديا: function pow(x, n) { let result = 1; // <-- for (let i = 0; i < n; i++) { result *= x; } // <-- return result; } يمكنك وضع سطر فارغ حيثما تريد لجعل الشيفرة البرمجية مقروءة بسهولة. لا يجب أن يوجد أكثر من 9 أسطر بدون بادئة عمودية. الفواصل المنقوطة ";" يجب أن توضع فاصلة منقوطة بعد كل تعليمة برمجية، حتى وإن كان من الممكن عدم إضافتها. يوجد بعض اللغات حيث يكون استخدام الفاصلة المنقوطة اختياريا وتُستخدم نادرا. لكن في بعض الحالات في JavaScript لا يحل السطر الجديد محل الفاصلة المنقوطة مما يجعل الشيفرة البرمجية عرضة للخطأ. يمكنك الاطلاع أكثر عن ذلك في جزء بنية الشيفرة البرمجية. إن كنت مبرمجًا متمرسًا في JavaScriptK، يمكنك اختيار نمط تكويد بدون فاصلة منقوطة مثل StandardJS. أو يُفَضَّل استخدام فواصل منقوطة لتجنب الأخطاء. أغلب المبرمجين يضعون فواصل منقوطة. مستويات التداخل تجنب تداخل الشيفرة البرمجية للعديد من المستويات. مثلًا، في الحلقة المتكررة يُفَضَّل استخدام التعليمة continue لتجنب التداخل العميق. مثلا، بدلا من إضافة if شرطية داخلية كالتالي: for (let i = 0; i < 10; i++) { if (cond) { ... // <- مستوى تشعيب إضافي } } يمكننا كتابة: for (let i = 0; i < 10; i++) { if (!cond) continue; ... // <- لا مزيد من التشعيبات } يمكن استخدام الأسلوب ذاته مع if/else و return. مثلًا، نحتاج لجزئين في المثال أدناه. خيار 1: function pow(x, n) { if (n < 0) { alert("Negative 'n' not supported"); } else { let result = 1; for (let i = 0; i < n; i++) { result *= x; } return result; } } خيار 2: function pow(x, n) { if (n < 0) { alert("Negative 'n' not supported"); return; } let result = 1; for (let i = 0; i < n; i++) { result *= x; } return result; } يُعد الخيار 2 أسهل قراءةً من الخيار 1 لأن الحالة الخاصة n < 0 فُحصت مسبقًا. بعد فحص قيمتها يمكننا الانتقال إلى جزء الشيفرة البرمجية الرئيسية بدون الحاجة لتداخل أكثر. موضِع الدوال في حال كنت تكتب العديد من الدوال المساعدة والتي تستخدمها الشيفرة البرمجية، فإن هناك 3 طرق لتنظيم هذه الدوال. 1- تعريف الدوال أعلى الشيفرة البرمجية التي تستخدمها: // تعريفات الدوال function createElement() { ... } function setHandler(elem) { ... } function walkAround() { ... } // الشيفرة التي تستخدمها let elem = createElement(); setHandler(elem); walkAround(); 2- الشيفرة البرمجية أولا ثم الدوال: // الشيفرة التي تستخدم الدوال let elem = createElement(); setHandler(elem); walkAround(); // --- دوال مساعدة --- function createElement() { ... } function setHandler(elem) { ... } function walkAround() { ... } 3- الطريقة المختلطة: تُعرَّف الدالة في أول مكان تستخدم فيه. الخيار الثاني هو الأفضل غالبا. ذلك لأنه عند قراءة شيفرة برمجية، فإننا نريد معرفة ما تقوم به أولا. إن كانت الشيفرة البرمجية في البداية فإن غرضها يكون واضحا مباشرة. أيضا، قد لا نحتاج لقراءة الدوال، خاصة إن كانت وظائفها واضحة من مسمياتها. شروحات لأنماط كتابة الشيفرة يحتوي دليل أنماط التكويد قواعد عامة حول كيفية كتابة شيفرة برمجية. مثلًا، الأقواس المستخدمة، وعدد مسافات البادئة، وأقصى طول للسطر، …الخ. والعديد من التفاصيل الدقيقة. عندما يستخدم جميع أعضاء الفريق الدليل نفسه لنمط التكويد، ستبدو الشيفرة البرمجية موحدة بغض النظر عمن قام بكتابتها. يمكن لأي الفريق الكتابة بنمط تكويد خاص بهم، لكن لا حاجة لذلك لوجود العديد من المعايير العالمية للاختيار منها. بعض الخيارات الشهيرة: Google JavaScript Style Guide Airbnb JavaScript Style Guide Idiomatic.JS StandardJS وغيرها في حال كنت مطورًا مبتدئًا، ابدأ بالشرح المزود هنا. ثم يمكنك الانتقال إلى دليل آخر مما ذكرناه لتتعرف على المزيد من التفاصيل وتختار الأسلوب الأنسب لك. منقحات الصياغة التلقائية (Automated Linters) منقحات الصياغة (Linters): هي عبارة عن أدوات يمكنها فحص نمط الشيفرة البرمجية تلقائيا واقتراح تعديلات لتحسينها. الأمر الذي يجعلها مفيدة أكثر هو قدرتها على العثور على بعض الأخطاء، مثل الخطأ باسم متغير أو دالة ما، لهذا فإنه من المستحسن استخدام منقح صياغة (Linter) حتى لو لم تُرد اتباع نمط تكويد معين. هنا بعض أدوات تنقيح الصياغة المعروفة مثل: JSLint – تُعد من أدوات تنقيح الصياغة الأولى. JSHint – تحوي اعدادات أكثر من JSLint. ESLint – الأحدث تقريبا. كلها تؤدي الغرض ذاته. الكاتب هنا يستخدم ESLint. معظم هذه الأدوات تكون مدمجة مع العديد من المحررات الشهيرة: يجب عليك أن تُفَعِّل الإضافة في المحرر وتحدد نمط التكويد الذي تريده. مثلا، لاستخدام ESLint اتَّبع ما يلي: ثبت Node.js. ثبت ESLint باستخدام الأمر npm install -g eslint (يُعد npm مُثَبِّت حزم JavaScript). أنشئ ملف إعداد وسمِّه "‎.eslintrc" في ملف مشروع JavaScript الرئيسي (الملف الذي يحتوي على جميع الملفات). ثبت/فعِّل الإضافة لمحررك الذي يدعم ESLint. معظم المحررات تدعم ESLint. هنا مثال على ملف "‎.eslintrc": { "extends": "eslint:recommended", "env": { "browser": true, "node": true, "es6": true }, "rules": { "no-console": 0, "indent": ["warning", 2] } } التعليمة "extends" هنا تعني أن الإعداد يعتمد على مجموعة إعدادات ESLint الافتراضية "eslint:recommended". يُمكننا تعديل الإعدادات التي نريدها لاحقًا. يمكن أيضا تنزيل مجموعة قواعد نمط التكويد وتوسيعها بدلا من ذلك. انظر في الرابط http://eslint.org/docs/user-guide/getting-started للمزيد من التفاصيل حول كيفية التثبيت. تحتوي بعض المحررات على منقح صياغة مدمج فيها إلا أنها ليست بدقة ESLint. الخلاصة تهدف جميع قواعد بناء الجمل في هذا الفصل (وفي باقي مراجع أنماط التكويد) لرفع مستوى سهولة قراءة الأكواد. وجميع القواعد قابلة للنقاش. عند التفكير في كتابة شيفرة برمجية بشكل أفضل، يجب أن نسأل أنفسنا: "ما الذي يجعل الشيفرة البرمجية أسهل للقراءة والفهم؟" و "ما الذي قد يساعدنا لتجنب الأخطاء؟" يوجد العديد من الأشياء التي يجب الانتباه لها أثناء اختيار نمط تكويد معين. سيتيح لك قراءة العديد من أنماط التكويد معرفة أحدث الأفكار عن أنماط التكويد وأفضل الممارسات. تمارين نمط تكويد سيء الأهمية: 4 ما الخطأ في نمط التكويد أدناه؟ function pow(x,n) { let result=1; for(let i=0;i<n;i++) {result*=x;} return result; } let x=prompt("x?",''), n=prompt("n?",'') if (n<=0) { alert(`Power ${n} is not supported, please enter an integer number greater than zero`); } else { alert(pow(x,n)) } قم بإصلاحه. الحل يمكنك ملاحظة ما يلي: function pow(x,n) // <- لا يوجد مسافات بين المُعاملات { // <- قوس الفتح في سطر مستقل let result=1; // <- عدم وجود مسافات قبل أو بعد = for(let i=0;i<n;i++) {result*=x;} // <- لا يوجد مسافات // يجب أن يكون محتوى { ... } في سطر جديد return result; } let x=prompt("x?",''), n=prompt("n?",'') // <-- ممكنة تقنيا، // لكن يُفَضَّل جعلها في سطرين، بالإضافة إلى عدم وجود مسافات وعدم وجود ; // if (n<0) // <- لا يوجد مسافات بين (n < 0), ويجب وجود سطر فارغ قبلها { // <- قوس الفتح في سطر مستقل // يمكن فصل الأسطر الطويلة في الأسفل حتى تصبح سهلة القراءة alert(`Power ${n} is not supported, please enter an integer number greater than zero`); } else // <- يمكن كتابتها في سطر واحد هكذا: "} else {" { alert(pow(x,n)) // لا يوجد مسافات ولا يوجد ; } بعد تصحيح الأخطاء تصبح الشيفرة البرمجية كما يلي: function pow(x, n) { let result = 1; for (let i = 0; i < n; i++) { result *= x; } return result; } let x = prompt("x?", ""); let n = prompt("n?", ""); if (n < 0) { alert(`Power ${n} is not supported, please enter an integer number greater than zero`); } else { alert( pow(x, n) ); } ترجمة -وبتصرف- للفصل Coding Style من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: التعليقات المقال السابق: تنقيح الأخطاء في Chrome
  2. قبل كتابة شيفرات برمجية أكثر تعقيدا، لنتطرَّق إلى تنقيح الأخطاء. تنقيح الأخطاء هي عملية إيجاد الأخطاء في السكريبت وإصلاحها. تدعم جميع المتصفحات الحديثة وبعض البيئات الأخرى "تنقيح الأخطاء" -واجهة مستخدم خاصة في أدوات المطور والتي تجعل العثور على الأخطاء وتصحيحها أمرا سهلا. تُتيح هذه الواجهة أيضا تَتَبُّع الأكواد خطوة بخطوة لمعرفة ما يحدث فيها بالتفصيل. سنستخدم Chrome لأن لديه ميزات كافية لذلك، كما تتوفر هذه الميزات في معظم المتصفحات الأخرى. جزء الموارد "sources" قد يبدو إصدار Chrome لديك مختلفًا بعض الشيء، إلا أن المحتوى هو ذاته. افتح صفحة example في Chrome. شغِّل أدوات المطور بالضغط على F12 (على أجهزة Mac، استعمل الاختصار Cmd+Opt+I). اختر الجزء sources. إن كانت هذه هي مرتك الأولى للدخول إلى جزء sources، فهذا ما ستراه: يفتح هذا الزر علامة تبويب تحوي الملفات. اضغط عليها واختر hello.js من العرض الشجري "tree view". يجب أن ترى التالي: هنا يمكننا رؤية ثلاث مناطق: منطقة الموارد "Resources zone" والتي تعرض ملفات HTML، و JavaScritp، و CSS، وغيرها من الملفات بما في ذلك الصور المُلحقة بالصفحة. قد تظهر إضافات Chrome هنا أيضًا. منطقة المصدَر "Source zone" والتي تعرض الشيفرة البرمجية المصدرية. منطقة المعلومات والتحكم "Information and control zone" لتنقيح الأخطاء، سَنكتشفها أكثر فيما يلي يمكنك الضغط على الزر مُجددًا لِإخفاء قائمة الموارد وإعطاء الشيفرة البرمجية مساحة كافية. شاشة التحكم (Console) تظهر شاشة تحكم عند الضغط على Esc. يمكن كتابة الأوامر في شاشة التحكم ثم الضغط على Enter لتنفيذها. تظهر مخرجات تنفيذ أمر ما أسفله مباشرة في شاشة التحكم. مثال: كما في الصورة بالأسفل 1+2 ينتج 3، بينما hello("debugger")‎ لا يُظهِر أي نتائج، لذلك فإننا نرى undefined: نقاط التوقف (Breakpoints) لنختبر ما يحدُث أثناء تنفيذ الشيفرة البرمجية في صفحة example. في الصفحة hello.js، اضغط على السطر رقم 4؛ نضغط على الرقم ذاته وليس السطر. هكذا تكون قد أنشأت نقطة توقف. اضغط على الرقم 8 أيضًا. يجب أن يبدو الشكل كما في الصورة التالية: نقطة التوقف هي نقطة يتوقف فيها مصحح الأخطاء عن تنفيذ JavaScript تلقائيًا. يمكننا فحص المتغيرات الحالية وتنفيذ الأوامر أو أي شيء آخر في لوحة التحكم أثناء توقف عمل الشيفرة البرمجية. أي أنه يمكننا تتبع الشيفرة البرمجية عند نقطة معينة عبر ايقافها والتأكد من أي شيء فيها كما نريد. يمكننا رؤية قائمة بالعديد من نقاط التوقف في الجزء الأيمن من الشاشة. يكون الأمر مفيدًا عند وجود عدة نقاط توقف في أكثر من ملف، وذلك يتيح لنا: التنقل بسرعة إلى نقاط التوقف في الشيفرة البرمجية (بالضغط عليها من الجزء الأيمن). إلغاء تفعيل نقاط التوقف مؤقتا بإلغاء تحديدها. حذف نقطة التوقف بالضغط عليها باليمين واختيار حذف "remove". وهكذا … نقاط التوقف الشرطية يتيح لك الضغط يمينَا على رقم السطر إنشاء نقطة توقف شرطية تُنَفَّذ عند تحقق الشرط المحدد فقط. يكون ذلك مفيدَا عندما تريد إيقاف التنفيذ لمعرفة قيمة أي متغير أو قيمة أي معامل في دالة. أمر Debugger يمكن أيضا إيقاف تنفيذ الشيفرة البرمجية بوضع الأمر debugger فيه كما يلي: function hello(name) { let phrase = `Hello, ${name}!`; debugger; // <-- يتوقف المنقح هنا say(phrase); } هذه الطريقة سهلة عندما نُعدِّل الشيفرة البرمجية باستخدم محرر الشيفرات البرمجية ولا نريد الانتقال إلى المتصفح وتشغيل السكربت في وضع أدوات المطور لإنشاء نقاط توقف. توقف وتحقق في المثال، يتم استدعاء الدالة hello()‎ أثناء تحميل الصفحة، لذلك فإن أسهل طريقة لتفعيل مُنقِّح الأخطاء (بعد إعداد نقاط التوقف) هي إعادة تحميل الصفحة. اضغط على F5 (لمستخدمي ويندوز أو لينكس)، أو اضغط على Cmd+R (لمستخدمي Mac). سيتوقف تنفيذ الشيفرة البرمجية في السطر الرابع حيث تم إنشاء نقطة التوقف: افتح قوائم المعلومات المنسدلة على اليمين (موضحة بأسهم). تتيح هذه القوائم التحقق من حالة السكريبت الحالية: 1- Watch - تعرض القيم الحالية لأي تعابير. يمكنك الضغط على + وإدخال أي تعبير تريده. سيعرض المعالج قيمته في أي وقت وحساب قيمته تلقائيا أثناء التنفيذ. 2- Call Stack - تعرض سلسلة الاستدعاءات المتداخلة. في الوقت الحالي، المعالج وصل حتى استدعاء الدالة hello()‎، المُستدعاة من خلال السكريبت index.html (لا يوجد دوال أخرى لذلك سُمِّيَت "anonymous"). إن ضَغَطت على عنصر من الحزمة (stack) مثلا "anonymous"، فسينتقل المعالج مباشرة إلى الشيفرة البرمجية المُمَثِّل لهذا العنصر وستتمكن من فحص جميع متغيراته أيضا. 3- Scope - تعرض المتغيرات الحالية. Local تعرض متغيرات الدالة المحلية. يمكنك أيضا رؤية قيم هذه المتغيرات موضحة على اليمين. Global تعرض المتغيرات الخارجية (خارج نطاق أي دالة). يوجد أيضا الكلمة المفتاحية this والتي لم تُشرح بعد، لكن سيتم شرحها قريبا. تتبع التنفيذ يوجد بعض الأزرار لِتتبع التنفيذ أعلى يمين اللوحة. لِنتطرق إليها. - زر استمرار التنفيذ F8. يستأنف التنفيذ. إن لم توجد أي نقاط توقف، فإن التنفيذ سيستمر بعد الضغط على هذا الزر وسَيفقد مصحح الأخطاء السيطرة على السكريبت. هذا ما سنراه بعد الضغط عليه: تم استئناف التنفيذ، ووصل لنقطة توقف أخرى داخل الدالة say()‎ وتوقف هناك. انظر في Call stack على اليمين. تم تنفيذ استدعاء آخر. وصل التنفيذ الآن حتى الدالة say()‎. - نفِّذ خطوة (نفّذ الأمر التالي)، لكن لا تنتقل إلى الدالة الأخرى، الوصول السريع F10. سيظهر alert إذا ضغطنا على هذا الزر الآن. الأمر المهم هنا هو أن alert قد يحوي على أي دالة، لذلك فإن التنفيذ سيتخطاها. - نفِّذ خطوة، الوصول السريع F11. يقوم بنفس آلية عمل الأمر السابق بالإضافة إلى تنفيذ الدوال المتداخلة. سَتتنفذ أوامر السكريبت بالضغط عليه خطوة بخطوة. - استمر بالتنفيذ حتى نهاية الدالة الحالية، باستخدام الزر Shift+F11. سيتوقف التنفيذ عند آخر سطر للدالة الحالية. يكون هذا الأمر مفيدا عند الدخول إلى استدعاء دالة مصادفةَ باستخدام الزر ، لكن تتبع هذه الدالة ليس أمرا مهما، لذلك نقوم بتخطي تتبعها. - تفعيل/تعطيل جميع نقاط التوقف. لا يقوم هذا الزر بمتابعة التنفيذ، إنما يُفَعِّل/يُعطِّل كمَا كبيرا من نقاط التوقف. - تفعيل/تعطيل التوقف التلقائي في حال حدوث خطأ. عند تفعيله في وضع أدوات المطور، فإن الشيفرة البرمجية تتوقف عن التنفيذ تلقائيا عند أي خطأ في السكريبت. ثم يمكننا تحليل المتغيرات لمعرفة سبب الخطأ. لذلك، إن أوقف خطأ مَا متابعة تنفيذ الشيفرة البرمجية، يمكننا فتح مصحح الأخطاء وتفعيل هذا الخيار وإعادة تحميل الصفحة لرؤية مكان توقف الشيفرة البرمجية وما هو المحتوى عند تلك النقطة. الاستمرار حتى هنا "Continue to here" الضغط يمينا على سطر من الشيفرة البرمجية يفتح قائمة السياق المحتوية على خيار مفيد يُدعى "Continue to here". يكون هذا الخيار مُفيدا عندما نريد التقدم بضع خطوات للأمام بدون وضع نقطة توقف. التسجيل (Logging) يمكن استخدام الدالة console.log لِعرض شيء على الشاشة كمُخرج من الشيفرة البرمجية. مثلا، يعرض الأمر التالي القيم من 0 حتى 4 إلى الشاشة: // open console to see for (let i = 0; i < 5; i++) { console.log("значение", i); } لا يرى المستخدم العادي هذه المخرجات. لرؤيتها، افتح علامة التبويب Console أو اضغط على Esc إن كنت في علامة تبويب أخرى هكذا تُفتًح الشاشة في الأسفل. إن كانت الشيفرة البرمجية تحتوي على أوامر console.log، فسنرى ما يحدث من خلال سجلات التتبع بدون الدخول إلى المُصحح. الخلاصة كما رأينا، فإن هناك ثلاث طرائق رئيسية لإيقاف السكريبت: باستخدام نقطة توقف. الأمر debugger. وجود خطأ (في حال كانت أداوت المطور مفتوحة وكان الزر مُفَعًّلَأ). عند توقف السكريبت، يمكننا فحص المتغيرات وتتبع الشيفرة البرمجية لرؤية أي أخطاء. يوجد العديد من الخيارات الأخرى في أدوات المطور أكثر مما تم شرحه سابقا. تجد الدليل كاملا على https://developers.google.com/web/tools/chrome-devtools. تُعَد المعلومات التي وُضِعَت كافية لبدء تتبع أي شيفرة برمجية وتنقيحها، لكنك ستحتاج للاطلاع على الدليل لاحقا لتعلم ميزات متقدمة في أدوات المطور، خاصة إن كنت تتعامل كثيرا مع المتصفح. يمكنك الضغط على عدة أماكن في أدوات المطور ورؤية ما يحدث. تُعد هذه أفضل طريقة لتعلم أدوات المطور. لا تنسَ الضغط يمينا وتجريب قوائم السياق. ترجمة -وبتصرف- للفصل Debugging in Chrome من كتاب The JavaScript Language. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: نمط كتابة الشيفرة المقال السابق: مراجعة لما سبق
  3. يلخِّص هذا الدرس مُميِّزات JavaScript التي تعرَّفنا عليها باختصار في الدروس السابقة مع إيلاء اهتمام خاص لأدق المواضيع. صياغة الشيفرة تنتهي التعليمات البرمجية في JavaScript بفاصلة منقوطة ;: alert('مرحبًا'); alert('بالعالم'); يمكنك إنهاء تعليمة برمجية ببدء سطر جديد، إذ يعد محرف السطر الجديد على أنَّه فاصل للتعليمات: alert('مرحبًا') alert('بالعالم') هذا ما يسمى «الإدراج التلقائي للفاصلة المنقوطة» (automatic semicolon insertion)، لكن يعمل كما هو متوقع في بعض الأحيان مثل الحالة التالية: alert("سيحدث خطأ هنا بعد الرسالة") [1, 2].forEach(alert) تخبرك أغلب أدلة الصياغة بضرورة وضع فاصلة منقوطة بعد كل تعليمة برمجية (statement). انتبه إلى أنَّ الفاصلة المنقوطة غير مطلوبة بعد الكتل البرمجية {...} مثل كل الدوال وحلقات التكرار وغيرها: function f() { // لا حاجة لفاصلة منقوطة بعد التصريح عن دالة } for(;;) { // لا حاجة لفاصلة منقوطة بعد حلقة تكرار } ولكن إذا وضعت فاصلة منقوطة «زائدة» في مكان ما داخل الشيفرة، لن تعدها JavaScript خطأ وستتجاهلها ببساطة. تجد المزيد في فصل «بنية الشيفرة». الوضع الصارم إن أردت تمكين جميع ميزات JavaScript الحديثة كاملًا، يجب أن تستفتح السكربت بالموجه "use strict" مثل: 'use strict'; ... يجب أن يُكتَب هذا الموجِّه في بداية السكربت أو في بداية جسم الدالة. تعمل الشيفرة بدون الموجه "use strict"، لكن هناك بعض الميزات تبقى على صورتها القديمة «المتوافقة» (compatible way). عمومًا، يُفضَّل العمل بالوضع الحديث دومًا. تُفعِّل بعض مميزات JavaScript الحديثة، مثل الأصناف التي ستتعرف عليها لاحقًا، الوضع الصارم ضمنيًّا. تجد المزيد في فصل «الوضع الصارم: ». المتغيرات يمكنك تعريف المتغيرات عبر: let const (متغير ثابت لا يمكن تغيير قيمته) var (نمط قديم سوف تتعرف عليه لاحقًا) يمكن أن يحتوي اسم المتغير على: حروف وأعداد، لكن الحرف الأول لا يجب أن يكون عددًا. الرمزان _ و $. يُسمَح أيضًا باستخدام الحروف الهجائية غير اللاتينية والحروف الهيروغليفية، ولكنها غير شائعة الاستخدام. يمكنك تخزين أي قيمة داخل المتغير مثل: let x = 5; x = "عبد الله"; إليك سبعة أنواع من البيانات: number: يمثِّل الأعداد العشرية والأعداد الصحيحة. string: يمثِّل السلاسل النصية. boolean: يمثِّل القيم المنطقية: true/false. null: هذا النوع يعني أن القيمة «فارغة» أو «غير موجودة». undefined: هذا النوع يعني أن القيمة «غير معرّفة». object و symbol: يمثِّل الأول بنية معقدة من البيانات والثاني مُعرِّفًا فريدًا، لم نسلط الضوء عليهما بعد. يرجع المعامل typeof نوع القيمة، مع استثناءين: typeof null == "object" // خطأ من أصل اللغة typeof function(){} == "function" // تعامل الدوال معاملةً خاصة تجد المزيد في فصل «المتغيرات وأنواع البيانات». الدوال التفاعلية لمَّا كنا نستعمل المتصفح بيئة عمل لنا، فستكون دوال واجهة المستخدم الأساسية هي: prompt(question, [default])‎: هي دالة تطرح السؤال question، ثم ترجع ما أدخله المستخدم أو تُرجع القيمة null في حال ألغى المستخدم عملية الإدخال (بالضغط على الزر "cancel"). confirm(question)‎: هي دالة تطرح السؤال question ثم تُتيح لك الاختيار بين «موافق» (Ok) أو «إلغاء» (Cancel) ثم تعاد قيمة منطقية، true/false، تمثِّل هذا الاختيار. alert(message)‎: دالةٌ تُظهِر لك الرسالة message المُمرَّرة إليها فقط. تظهر جميع هذه الدوال نافذة صغيرة تدعى «النافذة المنبثقة الشرطية» (modal window، وهي عنصر تحكم رسومي)، فهي توقف تنفيذ الشيفرة وتمنع المستخدم من التفاعل مع صفحة الويب حتى يتفاعل معها. جرب تنفيذ المثال التالي: let userName = prompt("ما اسمك؟‎", "محمد"); let isTeaWanted = confirm("هل تريد كوبًا من الشاي؟"); alert( "الزائر: " + userName ); // محمد alert( "أيريد كوبًا من الشاي؟" + isTeaWanted ); // true تجد المزيد في فصل «الدوال التفاعلية». المعاملات تدعم JavaScript المعاملات التالية: المعاملات الحسابية معاملات الحساب الأساسية وهي * + - / بالإضافة إلى المعامل % لإيجاد باقي القسمة، و أيضًا معامل القوة **. يدمج معامل الجمع الثنائي + السلاسل النصية. إذا كان أحد العاملين عبارة عن سلسلة نصية، فسيُحوَّل الآخر إلى سلسلة نصية أيضًا مثل: alert( '1' + 2 ); // '12', سلسلة نصية alert( 1 + '2' ); // '12', سلسلة نصية معاملات الإسناد هنالك معامل إسناد بسيط وهو a = b ومعامل إسناد مركَّب مثل a *= 2. المعاملات الثنائية تعمل المعاملات الثنائية في المستوى المنخفض للبيانات أي في مستوى البتات، لذا ارجع إلى توثيقها في موسوعة حسوب إن لزمتك. المعاملات الشرطية المعامل الوحيد الذي يحوي ثلاث عوامل هو معامل علامة استفهام ? أو كما يسمى «المعامل الثلاثي»: cond ? resultA : resultB إذا تحقق الشرط cond، يُرجع المعامل القيمة resultA؛ خلا ذلك، يُرجع القيمة resultB. المعاملات المنطقية ينفِّذ المعاملان المنطقيان AND && و OR || «دارة تقييم قصيرة» (short-circuit evaluation) ثمَّ يرجعان القيمة المُقيَّمة الناتجة. يُحوِّل المعامل المنطقي NOT ! العامل الذي استدعي معه لقيمة منطقية ثم يُرجع القيمة المعاكسة له. معاملات الموازنة يُحوّل معامل المساواة == القيم المختلفة إلى أعداد ثم يتحقق من المساواة (ما عدا القيمة الفارغة null والقيمة غير المُعرَّفة undefined التي تساوي بعضها بعضًا). إليك المثال التالي: alert( 0 == false ); // true alert( 0 == '' ); // true وكذلك تفعل معاملات الموازنة الأخرى. فيما يخص معامل المساواة الصارمة === لا يُوحَّد نوع القيم المراد التحقق من تساويها، إذ تعد عملية التحقق من نوعين مختلفين بالنسبة لهذا المعامل عملية غير محققة دومًا (أي النوعان غير متساويين). تعدُّ القيمة الفارغة null والقيمة غير المحددة undefined حالة خاصة، إذ تساوي إحداهما الأخرى (عبر المعامل ==) ولا تساويان أي شيء آخر. توازن إشارة الأكبر وإشارة الأصغر >/< بين كل محرفين متقابلين من السلسلتين النصيّتين المراد موازنتها مع بعضهما، وأما بالنسبة لموازنة أنواع البيانات الأخرى، فتُحوَّل أولًا إلى أعداد ثم توازن. معاملات أخرى هنالك معاملات أخرى غير شائعة الاستخدام مثل معامل الفاصلة. تجد المزيد في الفصول التالية: فصل «المعاملات في JavaScript» وفصل «معاملات الموازنة» وفصل «المعاملات المنطقية». حلقات التكرار لقد غطينا ثلاثة أنواع من حلقات التكرار هي: // 1 while (condition) { ... } // 2 do { ... } while (condition); // 3 for(let i = 0; i < 10; i++) { ... } المتغير الذي يُعرَّف داخل حلقة التكرار for(let...)‎ مرئيٌّ داخلها فقط، ولكن يمكنك حذف let وإعادة استخدام المتغير. يأمر الموجهان break/continue بإنهاء التكرار الحالي والعودة للحلقة (continue) أو الخروج من الحلقة بأكملها (break). يمكن استخدام اللافتات (labels) للتحكم بالحلقات المتداخلة وإنهائها مثلًا. تجد المزيد في فصل «حلقتي التكرار while و for». وسوف نتعرف لاحقًا على المزيد من حلقات التكرار. التعبير switch يمكن للتعبير switch أن يحل محل التعبير الشرطي if، إذ يمنحك طريقة وصفية أكثر لموازنة قيمة ما مع عدَّة قيم. ويستخدم معامل المساواة الصارمة === في عملية الموازنة. إليك المثال التالي: let age = prompt('كم عمرك؟‎', 18); switch (age) { case 18: alert("لن يعمل"); // ‎الناتج سلسلة نصية وليس عدد case "18": alert("يعمل"); break; default: alert("أي قيمة غير مساوية للقيمتين السابقتين"); } تجد المزيد في فصل «التعبير switch». الدوال إليك ثلاث طرائق لإنشاء دالة في JavaScript: 1- التصريح عن دالة: تُعَّرف الدالة في سياق الشيفرة الرئيسية (ضمن النطاق العام) بشكل منفصل عن بقية التعليمات: function sum(a, b) { let result = a + b; return result; } 2- تعبير دالة: تُنشَأ الدالة داخل تعبير برمجي أو داخل كتلة برمجية أخرى: let sum = function(a, b) { let result = a + b; return result; }; 3- الدوال السهمية: // التعبير في الطرف الأيمن let sum = (a, b) => a + b; // يمكن أن تمتد على عدة أسطر باستعمال الأقواس المعقوصة شرط إعادة شيء let sum = (a, b) => { // ... return a + b; } // دون معاملات let sayHi = () => alert("مرحبًا"); // مع معامل وحيد let double = n => n * 2; قد تحتوي الدوال على متغيرات محلية، إذ تُعرَّف تلك المتغيرات داخل جسم الدالة وتبقى مرئيًّة داخل الدالة فقط. يمكن أن تملك المعاملات قيمًا افتراضية مثل: function sum(a = 1, b = 2) {...}‎. تُرجع الدوال قيمة ما دائمًا. وإذا لم يكن هناك الموجه return، فستكون تلك القيمة المعادة القيمة undefined. تجد المزيد في فصل «الدوال». المزيد قادم كان ما سبق قائمةً مختصرةً بميزات JavaScript. لقد تعرّفت إلى الآن على الأساسيات في JavaScript. أمَّا في الدروس القادمة، فستتعرف على المزيد من المميزات والمواضيع المتقدمة، لذا خذ استراحة وتهيَّأ جيدًا لما هو قادم. ترجمة -وبتصرف- للفصل JavaScript specials من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: تنقيح الأخطاء في Chrome المقال السابق: تعابير الدوال والدوال السهمية
  4. الدالة ليست بنية سحرية تُبنَى بها الشيفرة في JavaScript، وإنما هي نوع خاص من القيم. يُطلق على الصياغة التي تُستخدم في بناء الدوال «التصريح عن دالة» (Function Declaration): function sayHi() { alert( "مرحبًا" ); } هناك صياغة أخرى لبناء دالة تسمى «تعبير دالة» (function expression) وتبدو بالشكل التالي: let sayHi = function() { alert( "مرحبًا" ); }; تُعامل الدالة في هذه الصياغة مثل أي قيمة، إذ تُنشَأ ثم تُسنَد للمتغير sayHi كما توضح الشيفرة. بغض النظر عن الصياغة التي تُعرَّف بها الدالة، فهي مُجرَّد قيمة مُخزَّنة في المتغير sayHi. المراد من هذه الشيفرة هو نفسه في الشيفرة التي تسبقها: «إنشاء دالة ووضعها في المتغير sayHi». يمكن عرض الدالة ككل باستخدام alert دون الأقواس: function sayHi() { alert( "Hello" ); } alert( sayHi ); // إظهار شيفرة الدالة لاحظ أنَّ السطر الأخير في الشيفرة لا يستدعي الدالة لتنفيذها، وذلك بسبب عدم وجود الأقواس بعد اسمها sayHi. العديد من لغات البرمجة لا تتأثر بذلك (أي لا تهتم بوجود هذه الأقواس)، ولكن JavaScript ليست منها. الدالة في JavaScript عبارة عن قيمة، لذا يمكن التعامل معها على أنها قيمة. يُظهِر الاستدعاء alert( sayHi );‎ سلسلة نصية تمثِّل شيفرة الدالة المُعرَّفة بأكملها، أي الشيفرة المصدرية للدالة. الدالة عبارة عن قيمة خاصة، وأحد الأمور الخاصَّة التي تنفرد بها عن القيم العادية هي إمكانية استدعائها وتنفيذها وذلك بوضع الأقواس بعد اسمها بالشكل sayHi()‎ في أي مكان داخل الشيفرة. على أي حال، الدالة تبقى قيمة ولا مشكلة في معاملتها مثل أنواع القيم الأخرى. فيمكنك مثلًا إسناد دالة إلى متغير آخر: function sayHi() { // (1) إنشاء alert( "مرحبًا" ); } let func = sayHi; // (2) نسخ func(); // مرحبًا‎ // (3) تنفيذ النسخة المنسوخة عن الدالة sayHi(); // مرحبًا‎ // !وهذا يعمل أيضًا ولمَ لا إليك تفصيلٌ لما تنفِّذه الشيفرة السابقة: ينشئ التصريح عن الدالة في السطر (1)الدالة ثمَّ يضعها في المتغير sayHi. تُنسَخ الدالة في السطر (2) إلى المتغير func. لاحظ هنا عدم وجود أقواس استدعاء الدالة بعد sayHi. إذا أُضيفَت هذه الأقواس فإنَّ الاستدعاء func = sayHi()‎ سوف يُسنِد الناتج الذي تعيده الدالة إلى المتغير func وليس الدالة sayHi نفسها. يمكنك الآن استدعاء الدالة عبر استدعاء sayHi()‎ أو func()‎. يمكنك أيضًا استخدم تعبير الدالة للتصريح عن الدالة sayHi في السطر الأول: let sayHi = function() { ... }; let func = sayHi; // ... كلا التعبيرين يعطيان النتيجة نفسها. ملاحظة حول وجود الفاصلة المنقوطة في نهاية تعبير الدالة قد تتساءل عن سبب وجود الفاصلة المنقوطة في نهاية «تعبير الدالة»، وعدم وجودها في نهاية «تعريف الدالة»: function sayHi() { // ... } let sayHi = function() { // ... }; الجواب بسيط: ليس هناك حاجة للفاصلة المنقوطة ; في نهاية كتل التعليمات البرمجية والصِيغ التي تستخدمها مثل if { ... }‎، و function f { }‎، و for { }‎ …إلخ. يُعدُّ تعبير الدالة، let sayHi = ...;‎، تعليمة برمجية (statement) ويُستخدَم على أنَّه قيمة أي هو ليس كتلة برمجية (code block) ولكنه عملية إسنادٍ لقيمة. تكتب الفاصلة المنقوطة ; في نهاية التعليمات البرمجية، بغض النظر عن ماهية هذه التعليمات؛ لذلك، لا ترتبط الفاصلة المنقوطة بتعبير الدالة نفسه إطلاقًا، وإنها فقط تُنهِي التعليمة البرمجية. دوال رد النداء إليك مزيدًا من الأمثلة حول تمرير الدوال على أنَّها قيم واستخدام تعابير الدوال. سنكتب الدالة ask(question, yes, no)‎ التي تقبل تمرير ثلاثة معاملات إليها: المعامل question: يمثِّل نص السؤال. المعامل yes: يُمثِّل دالة يراد تنفيذها إذا كانت إجابة المعامل question هي yes. المعامل no: يمثِّل دالة يراد تنفيذها إذا كانت إجابة المعامل question هي no. تطرح الدالة السؤال question، وبناءً على إجابة المستخدم، تُستدعى الدالة yes()‎ أو الدالة no()‎: function ask(question, yes, no) { if (confirm(question)) yes() else no(); } function showOk() { alert( "لقد وافقت!" ); } function showCancel() { alert( "لقد ألغيت التنفيذ." ); } // ask على أنها وسائط إلى الدالة showOk و showCancel تمرر الدالتين ask("Do you agree?", showOk, showCancel); مثل هذه الدوال مفيدةٌ للغاية في الحياة العملية. ويتمثَّل الاختلاف الرئيسي بين التنفيذ الواقعي العملي والمثال النظري أعلاه في أنَّ الأولى تستخدم طرائقًا أكثر تعقيدًا للتفاعل مع المستخدم من مجرد دالة confirm بسيطة. تُظهِر مثل هذه الدالة نافذة أسئلة مُعدَّلة جميلة المظهر في المتصفح لكن هذا موضوع آخر متقدم. يسمى الوسيطين showOk و showCancel للدالة ask «بدوال ردود النداء» (callback functions) أو «ردود النداء» (callbacks) فقط. الفكرة القابعة خلف «رد النداء» هي تمرير دالة ثم توقُّع إعادة استدعائها لاحقًا إذا لزم الأمر. أي مثلها كَمَثِل ارتداد صدى الصوت وعودته للمنادي أو مناداة أحدهم بطلبِ تنفيذ أمرٍ وتوقع إجابة النداء (رد النداء) بنتيجة الفعل وغيرها من الأمثلة المستقاة من الواقع. في المثال السابق، تصبح الدالة showOk رد نداء للإجابة "نعم" (yes)، والدالة showCancel رد نداء للإجابة "لا" (no). يمكن استخدام تعبير دالة لكتابة الدالة نفسها بشكل أقصر: function ask(question, yes, no) { if (confirm(question)) yes() else no(); } ask( "هل تقبل؟", function() { alert("لقد قبلت."); }, function() { alert("لقد ألغيت التنفيذ."); } ); تُعرَّف الدوال هنا مباشرةً داخل الدالة ask(...)‎ ولا تملك اسمًا في هذه الحالة، لذا يُطلَّق عليها «دوال مجهولة» (anonymous). لا يمكن الوصول إلى مثل هذه الدوال من خارج الدالة ask(...)‎ (لعدم إسنادها إلى متغيرات) ولا يهمنا هذا الأمر هنا. ملاحظة: الدالة عبارة عن قيمة تمثل «إجراء» القيم العادية مثل السلاسل النصية أو الأعداد تمثِّل البيانات ولكن يمكن اعتبار الدالة -بما أنها قيمة خاصة- على أنَّها إجراء، لذا يمكنك تمريرها بين المتغيرات وتنفيذها عند الحاجة. تعبير الدوال مقابل التصريح عن الدوال يسلط هذا القسم الضوء على الاختلافات الرئيسية بين تعبير الدوال (Function Expressions) والتصريح عن الدوال (Function Declarations). أولًا، الصياغة: كيف نفرِّق بينهما في الشيفرة. التصريح عن دالة: يُصرَّح عن الدالة في سياق الشيفرة الرئيسية بشكل منفصل عن بقية التعليمات البرمجية. // التصريح عن دالة function sum(a, b) { return a + b; } تعبير دالة: تُنشَأ الدالة داخل تعبير برمجي (expression) أو داخل صياغة بانية أخرى. هنا، أُنشئَت الدالة في الجانب الأيمن من «تعبير الإسناد» (الإشارة =): // تعبير دالة let sum = function(a, b) { return a + b; }; يظهر الفرق جليًّا عندما يُنشِئ محرك JavaScript كلتا الدالتين. يُنشَأ تعبير الدالة عندما يصل تنفيذ الشيفرة إليه ويصبح جاهزًا للاستخدام من تلك اللحظة فقط. بمجرد أن ينتقل تنفيذ الشيفرة إلى الجانب الأيمن من معامل الإسناد، let sum = function…‎، تُنشَأ الدالة وتستطيع استخدامها بدءًا من تلك اللحظة (إسناد، استدعاء، …إلخ). التصريح عند الدوال مختلف بعض الشيء. يمكن استدعاء الدالة المُصرَّح عنها قبل أن يصل تنفيذ الشيفرة إليها. فمثلًا تبقى الدالة المُصرَّح عنها في المجال العام مرئيَّةً لجميع أجزاء السكربت كله (أي يمكن أي يمكن استدعاؤها من أي مكان)، بغض النظر عن مكان وجودها في الشيفرة. ذلك الأمر عائدٌ لخوارزميات داخلية تنفِّذها JavaScript قبل تنفيذ الشيفرة. فعندما تستعد JavaScript لتنفيذ سكربت ما، فإنَّها تبحث أولًا عن الدوال المصرَّح عنها في النطاق العام فيه وتنشئها. الأمر مشابه «لمرحلة التهيئة» (initialization stage). وبعد أن تعالج جميع تلك الدوال، تبدأ عملية تنفيذ الشيفرة، لذلك يمكن الوصول إليها من أي جزء من السكربت وتنفيذها. إليك المثال التالي: sayHi("جعفر"); // ‎مرحبًا، جعفر function sayHi(name) { alert( `مرحبًا، ${name}` ); } تُنشَأ الدالة sayHi المُصرَّح عنها في المجال العام عندما تستعد JavaScript لتنفيذ السكربت، وتصبح مرئية في كامل أرجائه. لاحظ هنا أن السكربت لن يعمل كما سبق في حال استخدمت تعبير دالة بدلًا من عملية التصريح: sayHi("جعفر"); // ‎!خطأ let sayHi = function(name) { // (*) ;-) بَطُل مفعول السحر هنا alert( `Hello, ${name}` ); }; تُنشَأ الدالة هنا عند وصول التنفيذ إليها، يحدث ذلك في السطر (*)، لتعريفها بتعبيرٍ ويكون ذلك أي بعد فوات الأوان. ميزة أخرى خاصة بعملية التصريح عن الدوال هي نطاق الكتلة الخاصة بها (block scope). فإن طُبِّق الوضع الصارم (strict mode) في السكربت، تُرَى الدالة المُعرَّفة داخل كتلة برمجية ضمنها فقط ولا يمكن الوصول إليها من خارجها. على سبيل المثال، لنفترض أننا نريد تعريف الدالة welcome()‎ بناءً على قيمة المتغير age التي نحصل عليها أثناء تنفيذ السكربت من المستخدم. نلاحظ أن التنفيذ لن يسير على ما هو مخطط له إن صرحنا عن الدالة بالشكل التالي: let age = prompt("كم عمرك؟‎", 18); // welcome التصريح الشرطي عن الدالة if (age < 18) { function welcome() { alert("مرحبًا!"); } } else { function welcome() { alert("السلام عليكم!"); } } // ثم استعمالها لاحقًا welcome(); // غير معرّفة welcome خطأ: الدالة حصلنا على ذلك الخطأ لأن الدالة المُصرَّح عنها داخل كتلة تبقى مرئية فقط ضمن نطاقها.دعنا نعدِّل على المثال السابق في محاولة لحل المشكلة: let age = 16; // لنعتمد العمر 16 مثلًا if (age < 18) { welcome(); // \ (تنفيذ) // | function welcome() { // | alert("مرحبًا!"); //‎ | تعريف الدالة موجود داخل النطاق حيث استدعيت } // | // | welcome(); // / (تنفيذ) } else { function welcome() { // لمَّا كان العمر 16، لن تُنشَأ هذه الدالة alert("السلام عليكم!"); } } // لم يعد هنالك نطاق (مجال) لأي كتلة هنا لعدم وجود أقواس معقوصة // لذا، لن نستطيع رؤية الدالتين المنشأتين داخل الكتلتين السابقتين welcome(); // غير معرّفة welcome خطأ: الدالة ما الذي يمكن فعله لجعل الدالة welcome مرئية خارج نطاق الكتلة if الشرطية؟ تتمثَّل الطريقة الصحيحة في استخدام تعبير الدوال وذلك بإسناد تنفيذ الدالة welcome لمتغير يُعرَّف في النطاق العام لتصبح مرئية خارج الشرط if. لاحظ أنَّ الشيفرة تعمل على النحو المطلوب بهذه الطريقة: let age = prompt("كم عمرك؟‎", 18); let welcome; if (age < 18) { welcome = function() { alert("مرحبًا!"); }; } else { welcome = function() { alert("السلام عليكم!"); }; } welcome(); // تمام، لا يوجد أي خطأ بإمكانك ببساطة استخدام معامل علامة الاستفهام ? بالشكل التالي: let age = prompt("كم عمرك؟‎", 18); let welcome = (age < 18) ? function() { alert("مرحبًا!"); } : function() { alert("السلام عليكم!"); }; welcome(); // تمام، لا يوجد أي خطأ متى عليك التصريح عن الدالة ومتى تستعمل تعبير الدالة؟ تمنح عملية التصريح عن الدوال حريةً أكبر في تنظيم الشيفرة، لتوفير إمكانية استدعاء هذه الدوال قبل تعريفها وهذه أهم نقطة تُؤخذ في الحسبان عند المفاضلة بين التصريح والتعبير. أضف إلى ذلك أنَّ صياغة التصريح تُسهِّل قراءة الشيفرة أيضًا، إذ من الأسهل البحث عن function f(…) {…}‎ في الشيفرة بدلًا من let f = function(…) {…}‎. فالتصريح ملفت للنظر أكثر من التعبير. وإذا كان التصريح غير مناسب لسبب ما، أو احتجت إلى تعريف مشروط لدالة (مثل المثال السابق)، فيجب عليك استخدام التعبير عوضًا عن التصريح. الدوال السهمية هناك صياغة بسيطة وموجزة لإنشاء الدوال تسمى «الدوال السهمية» (Arrow functions)، وغالبًا ما تكون أفضل من تعبير الدوال. سُمي هذا النوع من الدوال بالدوال السهمية لأنها تشبه السهم ببساطة، وإليك صياغتها: let func = (arg1, arg2, ...argN) => expression يُنشِئ هذا دالة func تملك الوسائط arg1..argN، وتُقيِّم التعبير expression على الجانب الأيمن ثم تُرجِع قيمته. لاحظ أنَّ السطر السابق يقابل الشيفرة التالية بالضبط: let func = function(arg1, arg2, ...argN) { return expression; }; ولكنه أكثر إيجازًا. إليك مثال آخر: let sum = (a, b) => a + b; /* صياغة الدالة السهمية أصغر من الصياغة العادية التالية let sum = function(a, b) { return a + b; }; */ alert( sum(1, 2) ); // 3 يمكن حذف الأقواس في حال كان لديك وسيط واحد فقط: // الصياغتان التاليتان متماثلتان // let double = function(n) { return n * 2 } let double = n => n * 2; alert( double(3) ); // 6 إذا لم يكن هناك أي وسيط، توضع أقواس فارغة بالشكل التالي: let sayHi = () => alert("مرحبًا!"); sayHi(); يمكن استخدام الدوال السهمية كما يُستخدَم تعبير الدوال. فإليك مثال الدالة welcome()‎ السابق باستعمال الدوال السهمية: let age = prompt("كم عمرك؟‎", 18); let welcome = (age < 18) ? () => alert('مرحبًا!') : () => alert("السلام عليكم!"); welcome(); // تمام، الدالة تعمل تبدو الدوال السهمية للوهلة الاولى غير مألوفة وصعبة القراءة، لكن هذا سرعان ما يتغير عند اعتياد العينين على صيغة وهيكل تلك الدوال. الدوال السهمية مناسبة جدًا لكتابة إجراءات بسيطة بسطر واحد فقط وستحبها كثيرًا إن كنت كسولًا (ميزة الكسل في المبرمج إيجابية جدًا :-D ) وتستثقل كتابة كلمات كثيرة. ملاحظة: الدوال السهمية متعددة الأسطر الأمثلة أعلاه أخذت الوسائط الموجودة على يسار المعامل <=، ومررتها إلى التعبير expression الموجود على جانبها الأيمن لتقييمه. نحتاج في بعض الأحيان إلى شيء أكثر تعقيدًا، مثل التعابير والجمل البرمجية المتعددة، إذ في هذه الحالة استخدام الأقواس المعقوصة ثم استخدام الموجه return داخلها. إليك المثال التالي: let sum = (a, b) => { // يفتتح القوس المعقوص دالة متعددة الأسطر let result = a + b; return result; // للحصول على نتائج return إن استعملت الأقواس المعقوصة، فاستعمل }; alert( sum(1, 2) ); // 3 ملاحظة: ما زال هنالك المزيد! تناول هذا الدرس الدوال السهمية بإيجاز، لكن هذا ليس كل شيء! الدوال السهمية لها ميزات أخرى مثيرة للاهتمام، لذا سنعود إليها لاحقًا في الفصل «زيارة الدوال السهمية مجدَّدًا». يمكننا في الوقت الحالي استخدامها مع إجراءات السطر الواحد وردود النداء. الخلاصة الدالة عبارة عن قيمة يمكن إسنادها أو نسخها أو تعريفها في أي مكان في الشيفرة. إن صُرِّح عن دالة في تعليمة برمجية (statement) منفصلة في سياق الشيفرة الرئيسية (النطاق العام)، فذلك يدعى «التصريح عن دالة». إن أُنشئت دالة في تعبير برمجي (expression)، فذلك يدعى «تعبير دالة». تعالج الدوال المُصرَّح عنها قبل تنفيذ الكتلة البرمجية (السكربت) الحاوية لها، وتصبح - نتيجةً لذاك - مرئيةً في أي مكان داخل الكتلة. تُنشَأ الدالة المُعرَّفة بوساطة تعبير عندما يحين دورها في التنفيذ بحسب مكان وجودها في السكربت. التصريح هو الخيار المفضل والشائع في إنشاء الدوال، لتوفير إمكانية رؤية الدالة قبل أن يحين دورها في التنفيذ حيثما كان موضعها في السكربت. أضف إلى ذلك أنه يساعد على تنظيم الشيفرة ويُسهِّل من قراءتها. من جهة أخرى، يُفضَّل تجنب استخدام تعبير الدوال إلا في الحالات التي لا يكون الصريح فيها ملائمًا. لقد تعرفت على مثالين في هذا الدرس يشرحان هذه النقطة، وسترى المزيد من الأمثلة في الدروس القادمة. أخيرًا، وجدنا أن الدوال السهمية مفيدةٌ جدًا خصوصًا في كتابة إجراءات بسطر واحد، وتأتي بشكلين: بدون أقواس معقوصة: ‎(...args) => expression، الجانب الأيمن عبارة عن تعبير، إذ تقيِّمه وترجع الناتج. مع أقواس معقوصة: ‎(...args) => { body }‎، تساعدك الأقواس في كتابة تعليمات برمجية متعددة داخل الدالة الواحدة، لكننا بحاجة إلى الموجه return لإرجاع شيء ما. تمارين اكتب الشيفرة التالية مجدَّدًا باستعمال الدوال السهمية ضع دوالًا سهمية مقابلة لتعابير الدوال الموجودة في الشيفرة التالية: function ask(question, yes, no) { if (confirm(question)) yes() else no(); } ask( "هل تقبل؟", function() { alert("لقد قبلت."); }, function() { alert("لقد ألغيت التنفيذ."); } ); الحل function ask(question, yes, no) { if (confirm(question)) yes() else no(); } ask( "هل قبلت؟", () => alert("لقد قبلت."), () => alert("لقد ألغيت التنفيذ.") ); تبدو الشيفرة واضحة وقصيرة، أليس كذلك؟ ترجمة -وبتصرف- للفصل Function expressions والفصل Arrow functions, the basics من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: مراجعة لما سبق المقال السابق: الدوال في JavaScript
  5. قد تحتاج أحيانًا إلى تنفيذ إجراء مماثل في أكثر من موضع في السكربت مثل عرض رسالةٍ جميلةٍ للمستخدم عند تسجيل الدخول وتسجيل الخروج وربما في مكان آخر أيضًا. الدوال (Functions) عبارةٌ عن كتل برمجيَّة تُنفِّذ مجموعة من المهام وفق تسلسل مُحدَّد، فهي بذلك تُشكل «اللبنات الأساسية» للبرنامج. تسمح الدوال باستدعاء شيفرة ما عدَّة مرات دون الحاجة لكتابتها من جديد. لقد رأيت خلال الدروس السابقة أمثلةً على دوال مبنيَّة مسبقًا (built-in functions)، مثل alert(message)‎، و prompt(message, default)‎، و confirm(question)‎، ويمكنك أيضًا إنشاء دوال خاصَّة بك. تعريف الدوال تُعرَّف الدالة بالصياغة التالية: function showMessage() { alert( 'مرحبًا بالجميع!' ); } تأتي الكلمة المفتاحية function أولًا، يليها اسم الدالة (showMessage في حالتنا)، ثم المعاملات (parameters) التي هي مجموعة من متغيرات تفصل فيما بينها بفاصلة لاتينية , (غير موجودة في المثال أعلاه لأنَّها اختياريَّة)، وأخيرًا جسم الدالة (function body) بين الأقواس المعقوصة وهو الإجراء المراد تنفيذه. function name(parameters) { // جسم الدالة } تُستدعَى الدالة بكتابة اسمها متبوعًا بقوسين هلاليين () مثل showMessage()‎: function showMessage() { alert( مرحبًا جميعًا!' ); } showMessage(); showMessage(); يُنفِّذ الاستدعاء showMessage()‎ جسم الدالة أي أنك سترى الرسالة مرتين. يوضِّح هذا المثال أحد أسباب استخدام الدوال، وهو تجنب تكرار الجمل البرمجية ذاتها. وفي حال أحتجت لتغيير الرسالة أو الطريقة التي تُعرَض بها، يكفي أن تُعدِّل الشيفرة من مكان واحد أي من الدالة فقط. المتغيرات المحلية المتغير الذي عُرِّف (صُرِّح عنه) داخل حدود دالةٍ ما مرئيٌّ فقط داخل هذه الدالة ويدعى أنذاك «متغيِّرًا محليًّا» (Local variable). إليك المثال التالي: function showMessage() { let message = "Hello, I'm JavaScript!"; // متغِّير محلي alert( message ); } showMessage(); // Hello, I'm JavaScript! alert( message ); // <-- خطأ! المتغيِّر محلي وموجود ضمن نطاق الدالة فقط المتغيرات العامة يُعرَّف المتغير العام (outer variable، ويدعى أيضًا global variable) خارج حدود الدالة ويمكنها الوصول إليه أيضًا مثل المتغيِّر userName في المثال التالي: let userName = 'محمد'; function showMessage() { let message = 'مرحبًا، ' + userName; alert(message); } showMessage(); // مرحبًا، محمد تملك الدالة الوصول الكامل إلى المتغير العام، إذ يمكنها التعديل عليه. فيمكننا مثلًا تعديل قيمة المتغيِّر userName العام من داخل الدالة قبل استعماله مثلًا: let userName = 'محمد'; function showMessage() { userName = "أحمد‎"; // (1) تغيير قيمة المتغير العام let message = 'مرحبًا، ' + userName; alert(message); } alert( userName ); // محمد، قبل استدعاء الدالة showMessage(); alert( userName ); // أحمد، بعد تعديل الدالة قيمته تستخدم الدالة المتغير العام في حالة واحدة وهي عدم وجود متغير محلي. إذا عُرِّف متغير محلي داخل دالة يحمل اسم المتغير العام نفسه، يغطي المتغيرالمحلي على العام ضمن حدود الدالة ويطغى عليه. في الشيفرة أدناه مثلًا، تستخدم الدالة showMessage()‎ المتغير userName الذي أنشئ محليًّا وتتجاهل المتغير الخارجي العام: let userName = 'محمد'; function showMessage() { let userName = "أحمد‎"; // التصريح عن متغيِّر محلي let message = 'مرحبًا، ' + userName; // أحمد alert(message); } // ستنشئ الدالة المتغيّر المحلي الخاص بها وتستعمله showMessage(); alert( userName ); // محمد، لم تغير الدالة المتغير العام أو تصل إليه ملاحظة: المتغيرات العامة هي المتغيرات التي تُعرَّف خارج حدود الدالة وتكون مرئيةً من أي دالة (إلا إن حُجِبَت [shadowed] بمتغيِّر محلي يحمل الاسم نفسه). يعدُّ الحد من استخدام المتغيرات العامة سلوكًا جيدًا، إذ تحوي الشيفرة الحديثة عددًا قليلًا من المتغيرات العامة أو لا تحتوي عليها إطلاقًا. ومعظم المتغيرات مُعرَّفةٌ داخل دوالها (أي الاقتصار على المتغيرات المحلية). تفيد المتغيرات العامة أحيانًا في تخزين بيانات على مستوى المشروع ككل (project-level data). المعاملات يمكنك تمرير أية بيانات إلى الدوال باستخدام «المعاملات» (Parameters، وتسمى أيضًا وسائط الدالة [function arguments]). فتملك الدالة في المثال التالي على معاملين هما: from و text. function showMessage(from, text) { // text و from :الوسائط هي alert(from + ': ' + text); } showMessage('مريم',‎ 'مرحبًا!'); // ‎مريم: مرحبًا (*) showMessage('مريم',‎ "كيف الحال؟"); ‎// مريم: كيف الحال؟ (**) عندما تُستدعَى الدالة في السطر (*) والسطر (**)، تُنسَخ القيم المُعطاة إلى متغير محلي باسم from ومتغير محلي آخر باسم text ثم تستخدمها الدالة آن ذاك. إليك مثال آخر مهم أرجو التركيز عليه؛ لدينا متغيّر باسم from مرَّرناه إلى الدالة showMessage()‎. لاحظ أن التعديل على المتغير from مرئيٌّ فقط داخل الدالة ولا ينعكس خارجها، وذلك لأنَّ الدالة تحصل دائمًا على نسخة من قيمة المتغيِّر ثم تتركه وشأنه: function showMessage(from, text) { from = '*' + from + '*'; // بمظهر مختلف "from" إظهار alert( from + ': ' + text ); } let from = "مريم"; showMessage(from, "مرحبًا"); ‎// *مريم*: مرحبًا // لأن الدالة عدّلت على متغير محلي مسمًى باسمه "from" لا تتغير قيمة alert( from ); // مريم القيم الافتراضية إن لم تُمرَّر أية قيمة لمعاملات دالةٍ، تُصبِح قيمها آنذاك غير مُعرَّفة undefined. فيمكن استدعاء الدالة showMessage(from, text)‎ التي ذكرناها مسبقًا مع تمرير قيمةٍ واحدةٍ لها مثل: showMessage("مريم"); لا يُعدُّ هذا خطأً بل يُظهِر القيمة "مريم: undefined". لمَّا لم تُعطَ قيمةٌ للمعامل text، فسيُفترَّض أن text === undefined (أي يُعطَى القيمة showMessage). إذا أردت إسناد قيمة افتراضية للمعامل text، فيمكنك تحديدها عبر معامل الإسناد = أثناء تعريف الدالة بالشكل التالي: function showMessage(from, text = "لم تُعطَ قيمة لـ text") { alert( from + ": " + text ); } showMessage("مريم"); // text مريم: لم تُعطَ قيمة لـ إن لم تُمرَّر الآن قيمةٌ للمعامل textعند استدعاء الدالة، فستُسنَد له القيمة "لم تُعطَ قيمة لـ text" التي هي عبارة عن سلسلة نصية. ويمكن أيضًا أن تكون القيمة الافتراضية تعبيرًا معقَّدًا يُقيَّيم ثم تُسنَد القيمة الناتجة إليه إذا، وفقط إذا، لم تُعطَ قيمة لذلك المعامل؛ لذلك، الشيفرة التالية صحيحةٌ أيضًا: function showMessage(from, text = anotherFunction()) { // text إذا لم تُمرَّر قيمة للمعامل anotherFunction() تُنفَّذ الدالة // الحالية آنذاك text وتصبح القيمة التي تعيدها هي قيمة المعامل } ملاحظة حول تقييم المعاملات الافتراضية يُقيّم المعامل الافتراضي في JavaScript في كل مرة تُستدعَى فيها الدالة دون المعامل المقابل له. ففي المثال أعلاه، تُستدعَى الدالة anotherFunction()‎ في كل مرة تُستدعَى فيها الدالة showMessage()‎ دون المعامل text. ملاحظة حول الطراز القديم للمعاملات الافتراضية لم تكن الإصدارات القديمة من JavaScript تدعم المعاملات الافتراضية، ولكن كان هناك طرائق بديلة لدعمها، قد تمر معك عند مراجعتك لسكربتات قديمة مثل التحقق الصريح من كون المعامل غير مُعرَّف بالشكل التالي: function showMessage(from, text) { if (text === undefined) { text = 'no text given'; } alert( from + ": " + text ); } أو عن طريق استعمال المعامل || بالشكل التالي: function showMessage(from, text) { // غير معطاة (أي غير معرفة)، فستُستعمَل القيمة الافتراضية text إن كانت قيمة المعامل text = text || 'لم تُعطَ أية قيمة'; ... } إرجاع قيمة يمكن للدالة إرجاع قيمة معنيَّة إلى من استدعاها. وأبسط مثال على ذلك دالةٌ تجمع قيمتين ثم تعيد القيمة الناتجة: function sum(a, b) { return a + b; } let result = sum(1, 2); alert( result ); // 3 يمكن أن يقع الموجه return في أي مكان داخل الدالة، ولكن انتبه لأن تنفيذ الدالة يتوقف عند الوصول إليه، وتُعَاد القيمة (أو ناتج تقييم تعبير برمجي) التي تليه إلى من استدعاها (result أعلاه). قد تحوي دالةٌ واحدةٌ عدَّة موجهات return مثل: function checkAge(age) { if (age > 18) { return true; } else { return confirm('هل تملك إذنًا من والديك؟'); } } let age = prompt('كم عمرك؟‎', 18); if ( checkAge(age) ) { alert( 'سُمِح له/ا بالوصول' ); } else { alert( 'مُنِعـ/ت من الوصول' ); } يمكن إنهاء تنفيذ الدالة فورًا عن طريق استخدام return دون قيمة مثل: function showMovie(age) { if ( !checkAge(age) ) { return; } alert( "يبدأ عرض الفلم" ); // (*) // ... } في الشيفرة أعلاه، إذا كان التعبير checkAge(age)‎ يُرجع القيمة false، فلن تُظهر عندئذٍ الدالة alert السلسلة النصية showMovie. ملاحظة حول إرجاع قيمة غير مُعرَّفة تُرجع دالةٌ القيمة undefined إن لم يلي الموجه return أية قيمة أو تعبير أو لم يكن موجودًا (أي لا تعيد الدالة أي شيء): function doNothing() { /* */ } alert( doNothing() === undefined ); // true بعبارة أخرى، حالة عدم إرجاع الدالة أي شيء وحالة استعمال return;‎ فقط وحالة استعمال return undefined;‎ كلها سواسية وهي أنَّ الدالة تعيد القيمة undefined: function doNothing() { return; } alert( doNothing() === undefined ); // true تنبيه بخصوص إضافة سطر جديد بين الموجه return والقيمة المعادة لا تضف سطرًا جديدًا بين الموجه return والقيمة (أو التعبير) التي يفترض أن تعيدها الدالة. فعند وجود تعبير طويل يلي return، قد تضعه في سطر منفصل مثل السطر التالي: return (some + long + expression + or + whatever * f(a) + f(b)) ولكنَّ هذه الشيفرة لا تعمل، لأنَّ JavaScript تفترض من تلقاء نفسها وجود الفاصلة المنقوطة بعد return. أي أن هذه الشيفرة ستُقيَّم كما يلي: return; (some + long + expression + or + whatever * f(a) + f(b)) كما ترى، تصبح return فعليًّا فارغة ولا تُرجع الدالة أية قيمة؛ لذلك، يجب أن تكون القيمة المراد إرجاعها والموجه return على السطر نفسه. أي إن أردت وضع التعبير المراد إعادته على عدة أسطر، فيجب كتابة بدايته على السطر نفسه الموجود عليه الموجه return أو وضع قوس هلالي افتتاحي على أقل تقدير كما يلي: return ( some + long + expression + or + whatever * f(a) + f(b) ) وستعمل الشيفرة بالشكل المتوقع لها. تسمية الدوال الدوال عبارة عن إجراءات، لذلك من الأفضل تسميتها بفعل مختصر ودقيق قدر الإمكان يصف ما تقوم به ليسهل على قارئ الشيفرة الغريب فهم عملها. أحد ممارسات التسمية الشائعة استعمال فعل مصدري ابتدائي في بداية اسم الدالة لوصف الإجراء الرئيسي الذي تُنفِّذه باختصار. ومن الأفضل أن يتفق أعضاء الفريق على معنى أسماء هذه الأفعال البادئة المستخدمة. فالدوال التي تبدأ تسميتها بالفعل "show" مثلًا عادةً ما تُظهِر شيئًا ما. الدوال التي تبدأ عادةً بالفعل: "get…‎" تجلب قيمة. "calc…‎" تحسب شيئًا ما. "create…‎" تنشئ شيئًا ما. "check…‎" تفحص شيئًا ما وتُرجع قيمة منطقية وهلم جرًّا. وإليك أمثلة على هذه الأسماء: showMessage(..) // إظهار رسالة getAge(..) // (جلب العمر (بطريقة ما calcSum(..) // حساب المجموع وإعادة الناتج createForm(..) // (إنشاء إستمارة (وإعادتها غالبًا checkPermission(..) // التحقق من إذنٍ وإعادة قيمة منطقية يتبادر لذهنك سريعًا ماهية العمل الذي تنفِّذه الدالة ونوع القيمة التي تُرجعها عند استخدام البادئات في تسمية الدوال استخدامًا ملائمًا وصحيحًا. دالة واحدة مقابل إجراء واحد يجب أن تُنفذ الدالة ما أشار لها اسمها بالضبط، لا أكثر ولا أقل. فعندما تُريد تنفيذ إجراءين مستقلين، يجب عليك تعريف دالتيّن مستقلتين، حتى لو جرى استدعاؤهما معًا (يمكنك في هذه الحالة تعريف دالة ثالثة تستدعي هاتين الدالتين). بعض الأمثلة لكسر هذه القاعدة: getAge سيكون سيئًا إذا أظهرت الدالة العمر بتنبيه (يجب أن تجلب العمر وتعيده فقط). createForm سيكون سيئًا إذا عدَّلت الصفحة وأضافت الإستمارة إليها (يجب أن تنشئه وأن تعيده فقط). checkPermission سيكون سيئًا إذا عرضت رسالةً تشرح حالة الوصول (يجب أن تفحص الإذن وأن تعيد النتيجة فقط). تعتمد هذه الأمثلة على المعاني الشائعة لتلك البادئات. ولك مطلق الحرية أنت وفريقك في الاتفاق على معانٍ أخرى، لكن عادةً لا تختلف كثيرًا عما هو شائع لها. على أي حال، يجب أن يترسخ لديك فهمٌ لما تعنيه كل بادئة وما الأشياء التي يمكن أو لا يمكن للدوال الملحق اسمها ببادئة القيام بها. وأخيرًا، ينبغي للفريق تبادل هذا العُرف الموضوع. أسماء دوال قصيرة جدًا الدوال التي تُستخدَم بكثرة تملك أحيانًا أسماءً قصيرة جدًا مثل الدالة $ المُعرَّفة في المكتبة jQuery والدالة _ الأساسية الخاصة بالمكتبة Lodash. تعد تلك الحالات استثنائية، ولكن يجب عمومًا أن تكون أسماء الدوال وصفية وموجزة. الدوال == التعليقات يجب أن تكون الدوال قصيرة وتُنفذ إجراءً واحدًا فقط. إذا كان هذا الإجراء معقد، فيفضَّل تقسيم الدالة إلى عدد من الدوال البسيطة. قد لا يكون اتباع هذه القاعدة في بعض الأحيان سهل، لكنه بالتأكيد أمر جيد. الدالة المنفصلة ليست سهلة الاختبار والتنقيح فقط بل تُعدُّ تعليقًا عظيم الشأن! فمثلًا وازن بين الدالتين showPrimes(n)‎ أدناه، إذ تخرج كل واحدة منهما الأعداد الأولية حتى العدد n المعطى. الدالة في المثال الأول تستخدم لافتة: function showPrimes(n) { nextPrime: for (let i = 2; i < n; i++) { for (let j = 2; j < i; j++) { if (i % j == 0) continue nextPrime; } alert( i ); // عدد أولي } } الدالة في المثال الثاني تستخدم الدالة الإضافية isPrime(n)‎ لاختبار الأعداد إذا كانت أولية أم لا: function showPrimes(n) { for (let i = 2; i < n; i++) { if (!isPrime(i)) continue; alert(i); // a prime } } function isPrime(n) { for (let i = 2; i < n; i++) { if ( n % i == 0) return false; } return true; } لاحظ أن المثال الثاني سهل الفهم، أليس كذلك؟ فنرى بدلًا من قطعة مبعثرة من الشيفرة اسم إجراءٍ (الذي هو isPrime في مثالنا). يشير الأشخاص أحيانًا إلى هذه الشيفرة بأنها «ذاتية الوصف» أي تصف نفسها بنفسها. بناءً على ذلك، يمكنك إنشاء الدوال حتى إذا لم ترد إعادة استخدامها وذلك لتنظيم الشيفرة وتسهيل قراءتها. الخلاصة صياغة تعريف دالة ما يبدو بالشكل التالي: function name(parameters, delimited, by, comma) { /* الإجراء الذي تنفذه الدالة */ } تُنسَخ القيم التي تُمرَّر إلى الدالة على أنها معاملات إلى متغيرات محلية تستخدمها الدالة. تستطيع أي دالة الوصول إلى المتغيرات العامة الخارجية، لكن أولوية الاستخدام تكون للمتغيرات المحلية ثم المتغيرات العامة (إن لم تتوفر المحلية). لا تملك الشيفرة التي تقع خارج حدود الدالة وصولًا إلى متغيراتها المحلية. يمكن للدالة إرجاع أي قيمة، أو القيمة undefined إن لم تُرجع أي شيء. يوصى باستخدام المتغيرات المحلية والمعاملات بشكل أساسي في الدالة وذلك لجعل الشيفرة واضحة وسهلة الفهم، وتجنب استخدام المتغيرات العامة قدر الإمكان. فهم عمل دالة تحتوي على معاملات وتستعملها ثم تُرجع قيمةً أسهل من فهم دالة لا تحتوي على أي معاملات، ولكنها تستعمل المتغيرات العامة وتعدل عليها. تسمية الدالة: يجب أن يصف اسم الدالة الإجراء الذي تُنفِّذه بوضوح. عندما ترى استدعاءً لدالة في شيفرةٍ ما، فإن الاسم الجيد يساعدك على فهم ما ستُنفذه وما ستُرجعه على الفور. الدالة عبارة عن إجراء (فعل)، لذلك عادة ما تستعمل الأفعال المصدرية في تسمية الدوال. يوجد العديد من البادئات المشهورة في إلحاقها بأسماء الدوال مثل create…‎، و show…‎، و get…‎، و check…‎ تُستخدَم للإشارة لما ستُنفذه هذه الدوال. الدوال هي اللبنات الأساسية في بناء الشيفرة. أصبحت الآن تملك فهمًا جيدًا للأساسيات ويمكنك البدء في إنشائها واستخدامها. سنطبق في الدروس القادمة كل ما تعملناه ونعود له مرارًا وتكرارًا والغوص أكثر في مواضيع متقدمة. التمارين هل وجود التعبير البرمجي else مهم في الشيفرة؟ الأهمية: 4 ترجع الدالة التالية القيمة true إذا كانت قيمة المعامل age أكبر من 18 وإلا فإنها تطلب تأكيدًا ثم ترجع نتيجته: function checkAge(age) { if (age > 18) { return true; } else { // ... return confirm('هل أخذت إذن والديك؟'); } } هل سيتغير عمل الدالة إذا حُذف التعبير else؟ function checkAge(age) { if (age > 18) { return true; } // ... return confirm('Did parents allow you?'); } هل يوجد اختلاف في سلوك هذين المثالين؟ الحل لا يوجد أي اختلاف. أعد كتابة الدالة باستخدام المعامل ? أو المعامل || الأهمية: 4 ترجع الدالة التالية القيمة true إذا كانت قيمة المعامل age أكبر من 18 وإلا فإنها تطلب تأكيدًا وتُرجع نتيجته: function checkAge(age) { if (age > 18) { return true; } else { return confirm('هل يأذن لك والديك؟'); } } أعد كتابة الدالة السابقة بدون استخدام التعبير الشرطي if لتنفيذ الإجراء نفسه الموضح أعلاه في سطر واحد. اكتب نموذجين مختلفين للدالة checkAge، وليكن النموذج الأول باستخدام المعامل الشرطي علامة الاستفهام ?، والثاني باستخدام المعامل ||. الحل النموذج الأول باستخدام المعامل الشرطي علامة الاستفهام ?: function checkAge(age) { return (age > 18) ? true : confirm('هل يأذن لك والديك؟'); } النموذج الثاني باستخدام المعامل || (النموذج الأبسط): function checkAge(age) { return (age > 18) || confirm('هل يأذن لك والديك؟'); } لاحظ أن الأقواس حول التعبير age > 18 ليست مطلوبة هنا لكن وجودها يُسهِّل قراءة الشيفرة. الدالة min(a,b)‎ الأهمية: 1 اكتب الدالة min(a,b)‎ التي ترجع قيمة العدد الأصغر من العددين a و b المعطيين. أي دالة مثل: min(2, 5) == 2 min(3, -1) == -1 min(1, 1) == 1 الحل النموذج الأول باستخدام التعبير الشرطي if: function min(a, b) { if (a < b) { return a; } else { return b; } } النموذج الثاني باستخدام المعامل الشرطي علامة الاستفهام ?: function min(a, b) { return a < b ? a : b; } ملاحظة: في حال كان العددان متساويين a == b، لا فرق إذا أُرجِع الأول أم الثاني. الدالة pow(x,n)‎ الأهمية: 4 اكتب الدالة pow(x,n)‎ التي تُرجع قيمة العدد x مرفوع للقوة n. بعبارة أخرى، تُرجع ناتج ضرب العدد x في نفسه عدد n من المرات. pow(3, 2) = 3 * 3 = 9 pow(3, 3) = 3 * 3 * 3 = 27 pow(1, 100) = 1 * 1 * ...* 1 = 1 أنشئ صفحة ويب باستخدام الدالة prompt تسمح للمستخدم إدخال العدد x والعدد n، ثم تُظهر نتيجة الدالة pow(x,n)‎. ملاحظة: في هذا التمرين، يجب أن يكون العدد n من الأعداد الطبيعية الصحيحة وأكبر من العدد 1 أيضًا. الحل function pow(x, n) { let result = x; for (let i = 1; i < n; i++) { result *= x; } return result; } let x = prompt("x?", ''); let n = prompt("n?", ''); if (n < 1) { alert(`القوة ${n} غير مدعومة، لذا أدخل عددًا صحيحًا أكبر من الصفر`); } else { alert( pow(x, n) ); } ترجمة -وبتصرف- للفصل Functions من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: تعابير الدوال والدوال السهمية المقال السابق: التعليمة switch
  6. يمكن للتعليمة switch أن تحل محل الشرط if المُتعدِّد، إذ تمنحك طريقة وصفية أكثر لموازنة قيمة ما مع عدَّة متغيرات. الصياغة تحتوي التعليمة switch على واحدة أو أكثر من كتل case (حالة) وكتلة default (حالة افتراضية) أخيرة اختيارية. الصياغة العامة هي: switch(x) { case 'value1': // if (x === 'value1') ... [break] case 'value2': // if (x === 'value2') ... [break] default: ... [break] } يُتحقَّق من المساواة الصارمة (strict equality) لقيمة المتغير x مع القيمة في الحالة case الأولى (أي value1) ثم الحالة الثانية (أي value2) وهلم جرًّا. في حال تحقَّقت المساواة الصارمة، يبدأ تنفيذ الشيفرة بدءًا من الكتلة البرمجية case المتطابقة حتى أقرب تعليمة خروج break (أو حتى نهاية التعليمة switch بأكملها). في حال لم تتحقق المساواة الصارمة مع أي حالة case، فستُنفَّذ كتلة default الافتراضيَّة (إن وجدت). مثال تطبيقي مثال على التعليمة switch: let a = 2 + 2; switch (a) { case 3: alert( 'صغير جدًا' ); break; case 4: alert( 'بالضبط!' ); break; case 5: alert( 'كبير جدًا' ); break; default: alert( "لا أعرف ما هذه القيمة" ); } تبدأ switch هنا في موازنة المُتغيِّر a مع قيمة الحالة case الأولى التي هي 3. لن تتحقق المطابقة في مثالنا لأنَّ قيمة a هي 4 ولكن ستتحقَّق مع قيمة الحالة الثانية، 4. بعد تَحقُّق المطابقة، يبدأ تنفيذ الشيفرة الموجودة بين case 4 وحتى أقرب break. إذا لم يكن هناك تعليمة break (توقف وخروج)، ستُنفَّذ الحالة case 5 (والحالات اللاحقة) أيضًا دون إجراء عملية التحقُّق. سنعيد كتابة المثال نفسه دون التعليمة break لترى الفرق: let a = 2 + 2; switch (a) { case 3: alert( 'Too small' ); case 4: alert( 'Exactly!' ); case 5: alert( 'Too big' ); default: alert( "I don't know such values" ); } في المثال أعلاه، ستُنفَّذ الدوال alert الثلاث تنفيذًا متسلسلًا وكأننا ننفِّذ الشيفرة التالية: alert( 'Exactly!' ); alert( 'Too big' ); alert( "I don't know such values" ); أي تعبير برمجي يمكن أن يكون وسيطًا للمبدِّل switch/case يمكنك تمرير تعابير تعسفية إلى switch/case مثل: let a = "1"; let b = 0; switch (+a) { case b + 1: alert("this runs, because +a is 1, exactly equals b+1"); break; default: alert("this doesn't run"); } قيمة التعبير ‎+a هنا هي 1 والتي توازن مع قيمة التعبير b + 1 في case لتُنفَّذ آنذاك الشيفرة المقابلة للقيمة المطابقة. تجميع حالات case متعدِّدة يمكنك تجميع العديد من الحالات case المختلفة لتتشارك الكتلة نفسها المراد تنفيذها عند تطابق إحداها. على سبيل المثال، إن أردنا تنفيذ الشيفرة نفسها للحالتين case 3 و case 5 (أي عندما تكون قيمة a هي 3 و 5)، نكتبهما بالشكل التالي: let a = 2 + 2; switch (a) { case 4: alert('Right!'); break; case 3: // (*) جمع حالتين case 5: alert('Wrong!'); alert("Why don't you take a math class?"); break; default: alert('The result is strange. Really.'); } الآن، تُظهِر كلًا من الحالة case 3 والحالة case 5 الرسالة نفسها عند تطابق إحداهما. القدرة على تجميع الحالات هي أحد الآثار الجانبية لكيفيَّة تنفيذ switch/case دون الحاجة إلى الفاصل break. يبدأ هنا تنفيذ الحالة case 3 (عند تطابقها) من السطر (*) ويمر عبر الحالة case 5 بسبب عدم وجود التعليمة break. نوع القيم دعني أركز على أمر مهم يتعلق بالتحقق من المساواة وهو أنَّ عملية المساواة تكون «صارمة» دومًا. أي يجب أن تكون القيم من النوع نفسه دائمًا لتتساوى. لتكن لدينا الشيفرة التالية مثلًا: let arg = prompt("أدخل قيمة عددية؟"); switch (arg) { case '0': case '1': alert( 'صفر أو واحد' ); break; case '2': alert( 'اثنان' ); break; case 3: alert( 'لا تُنفَّذ أبدًا' ); break; default: alert( 'قيمة مجهولة' ); } عند الحالة 0 والحالة 1، تُنفَّذ الدالة alert الاولى. عند الحالة 2، تُنفَّذ الدالة alert الثانية. لكن عند الحالة 3، ناتج الدالة prompt هي السلسلة النصية "3"، والتي لا تحقق المساواة الصارمة === مع العدد 3، لذا فإنَّ الشيفرة المكتوبة في الحالة case 3 هي شيفرة ميتة ولا تُنفَّذ أبدًا. وستُنفَّذ آنذاك شيفرة الحالة default الافتراضيَّة. تمارين أعد كتابة المبدِّل switch بصيغة الشرط if الأهمية: 5 اكتب الشرط if..else المقابل للمبدِّل switch التالي: switch (browser) { case 'Edge': alert( "لديك المتصفح Edge!" ); break; case 'Chrome': case 'Firefox': case 'Safari': case 'Opera': alert( 'حسنًا، نحن ندعم هذه المتصفحات أيضًا!' ); break; default: alert( 'نرجو أن تظهر هذه الصفحة بمظهر جيد!' ); } الحل لمحاكاة عمل التعليمة switch بدقَّة، يجب استخدام المساواة الصارمة '===' في الشرط if. ويمكنك استخدام المساواة '==' أيضًا مع السلاسل النصية المعروفة مسبقًا. if(browser == 'Edge') { alert("لديك المتصفح Edge!"); } else if (browser == 'Chrome' || browser == 'Firefox' || browser == 'Safari' || browser == 'Opera') { alert( 'حسنًا، نحن ندعم هذه المتصفحات أيضًا!' ); } else { alert( 'نرجو أن تظهر هذه الصفحة بمظهر جيد!' ); } ملاحظة: كُتِب التعبير التالي: browser == 'Chrome' || browser == 'Firefox' …‎ في الشيفرة أعلاه على أسطر متعدِّدة لتسهيل قراءته. على أي حال، تبقى صيغة التعليمة switch أكثر وضوحًا. أعد كتابة الشرط if بصيغة المبدِّل switch الأهمية: 4 اكتب المبدِّل switch المقابل للشيفرة التالية: let a = +prompt('a?', ''); if (a == 0) { alert( 0 ); } if (a == 1) { alert( 1 ); } if (a == 2 || a == 3) { alert( '2,3' ); } الحل تتحول عملية التحقُّق الأولى والثانية إلى حالتين منفصلتين (أي case) بينما تُجمَع عملية التحقق الثالثة في حالتين معًا: let a = +prompt('a?', ''); switch (a) { case 0: alert( 0 ); break; case 1: alert( 1 ); break; case 2: case 3: alert( '2,3' ); break; } ملاحظة: التعليمة break في آخر الشيفرة غير مطلوبة، ولكنها تجنبنا الحصول على خطأ في الصياغة مستقبلًا. فقد تود لاحقًا إضافة حالة case أخرى، مثل case4. فإذا نسيت إضافة break قبلها (لأنك لم تكتبها الآن، أي في نهاية case3)، فستواجه خطأ حتميًّا؛ لذلك، هذا نوع من الاحتياط والحذر المسبق من ارتكاب الأخطاء. ترجمة -وبتصرف- للفصل The "switch" statement من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: الدوال في JavaScript المقال السابق: حلقتا التكرار while و for
  7. نحتاج في بعض الأحيان لتكرار إجراء ما مثل إخراج قيم من قائمة واحدة تلو الأخرى أو تشغيل نفس الشيفرة للأعداد من 1 إلى 10 لكل عدد على حدة. حلقات التكرار (loops) عبارة عن وسيلة لتكرار شيفرة ما عدة مرات. حلقة التكرار while الصيغة الخاصة بها: while (condition) { // الشيفرة المراد تكرار تنفيذها // تدعى جسم الحلقة } طالما كان الشرط condition مُحققًا، أي true، تُنفذ الشيفرة الموجودة في جسم الحلقة. على سبيل المثال، تطبع حلقة التكرار أدناه قيمة المتغير i طالما كان الشرط i < 3 مُحققًا: let i = 0; while (i < 3) { // إظهار 0 ثم 1 ثم 2 alert( i ); i++; } يُسمى التنفيذ الواحد من جسم حلقة التكرار «تكرارًا» (iteration). ينفِّذ المثال السابق ثلاثة تكرارات. إذا كان i++‎ غير موجود في المثال أعلاه، ستُكرَّر الحلقة (نظريًا) إلى اللانهاية. أمَّا عمليًا، يوقف المتصفح تكرار مثل هذه الحلقات اللانهائية عند حدٍّ معيَّن، ويمكنك إنهاء العملية أيضًا من طرف الخادم في JavaScript. شرط حلقة التكرار غير مقصور على تعبيرات الموازنة، بل يمكن أن يكون أي تعبير أو متغير: يُقّيم الشرط ويُحوّل إلى قيمة منطقية بواسطة حلقة التكرار while. على سبيل المثال، الطريقة الأقصر لكتابة while (i != 0)‎ هي while (i)‎: let i = 3; while (i) { // وتتوقف الحلقة false صفرًا 0، يُقيَّم إلى القيمة i عندما تصبح قيمة المتغير alert( i ); i--; } ليس هناك حاجة للأقواس المعقوصة عند كتابة سطر برمجي واحد إذا كان جسم حلقة التكرار عبارة عن سطر واحد، يمكنك حذف الاقواس المعقوصة {…}: let i = 3; while (i) alert(i--); حلقة التكرار do..while يمكن إزاحة شرط حلقة التكرار الى أسفل جسم الحلقة باستخدام الصيغة do..while: do { // جسم الحلقة } while (condition); ستُنفذ أولًا الشيفرة الموجودة في جسم الحلقة ثم يتم التحقق من الشرط؛ فإذا كان الشرط مُحققًا، تُكرَّر هذه العملية مرة أخرى. إليك المثال التالي: let i = 0; do { alert( i ); i++; } while (i < 3); تستخدم هذه الصيغة عندما ترغب في تنفيذ الشيفرة مرة واحدة على الأقل بغض النظر عن كون الشرط محققًا أم لا. عادة ما تُفضل الصيغة الأخرى: while(…) {…}‎. حلقة التكرار for تعد حلقة التكرار for أكثر حلقات التكرار شيوعًا. تبدو صياغة الحلقة بالشكل التالي: for (begin; condition; step) { // ... جسم الحلقة ... } إليك المثال التالي لتتعرف ماهية هذه الأجزاء begin; condition; step. تُنفِّذ حلقة التكرار أدناه الدالة alert(i)‎ لقيمة المتغيِّر i العددية من 0 إلى 3 ( باستثناء العدد 3، لأن الشرط i < 3 لا يشمل العدد 3): for (let i = 0; i < 3; i++) { // إظهار 0 ثم 1 ثم 2 alert(i); } يعرض الجدول التالي شرحًا مفصلًا لأجزاء حلقة التكرار السابقة: الجزء الشيفرة المقابلة الوظيفة البدء (التهيئة) i = 0 يُنفَّذ مرةً واحدةً لحظة ولوج الحلقة الشرط i < 3 يُتحقَّق من هذا الشرط قبل كل تكرار (دورة تنفيذ) للحلقة، فإذا كان غير محقَّق (false)، يوقف تنفيذ الحلقة. الجسم alert(i)‎ يُنفَّذ ما دام الشرط محقَّقًا (true). الخطوة i++‎ يُنفَّذ بعد تنفيذ جسم الحلقة في كل تكرار. تعمل خوارزمية حلقة التكرار كالتالي: دخول الحلقة: -> إذا تحقَّق الشرط -> نفِّذ جسم الحلقة ثمَّ نفِّذ الخطوة -> إذا تحقَّق الشرط -> نفِّذ جسم الحلقة ثمَّ نفِّذ الخطوة -> إذا تحقَّق الشرط -> نفِّذ جسم الحلقة ثمَّ نفِّذ الخطوة -> … في حال كنت مبتدئًا، قد يساعدك ذلك في العودة إلى المثال وتدوين آلية تنفيذه خطوة بخطوة على الورق. إليك شرح لما يحدث في هذه الحالة: // for (let i = 0; i < 3; i++) alert(i) // دخول الحلقة let i = 0 // إذا تحقَّق الشرط -> نفِّذ جسم الحلقة ثمَّ نفِّذ الخطوة if (i < 3) { alert(i); i++ } // إذا تحقَّق الشرط -> نفِّذ جسم الحلقة ثمَّ نفِّذ الخطوة if (i < 3) { alert(i); i++ } // إذا تحقَّق الشرط -> نفِّذ جسم الحلقة ثمَّ نفِّذ الخطوة if (i < 3) { alert(i); i++ } // i == 3 أوقف الحلقة لعدم تحقُّق الشرط 3 > 3 عند التصريح عن المتغيرات داخل نطاق الحلقة يُصرَّح عن المتغير i المسمى «بالعدَّاد» مباشرةً ضمن حلقة التكرار، لذا تكون متاحةً ضمن نطاقها فقط وغير ظاهرة للعموم. for (let i = 0; i < 3; i++) { alert(i); // 0, 1, 2 } alert(i); // خطأ! لا يوجد متغيِّر بهذا الاسم بدلًا من تعريف متغير جديد، يمكنك استخدام متغير معرَّف مسبقًا: let i = 0; for (i = 0; i < 3; i++) { // استعمال متغير موجود مسبقًا alert(i); // 0, 1, 2 } alert(i); // يعرض القيمة 3 لوجوده ضمن النطاق العام تجاهل بعض أجزاء التحكم بحلقة التكرار for يمكن تجاهل أي جزء من أجزاء الحلقة begin; condition; step مثل حذف جزء التهيئة begin في حال لم يكن وجوده مهمًا كما في المثال التالي: let i = 0; // المتغيِّر جاهز for (; i < 3; i++) { // فلا حاجة لقسم التهيئة alert( i ); // 0, 1, 2 } يمكنك أيضًا حذف الخطوة step: let i = 0; for (; i < 3;) { alert( i++ ); } فتصبح حينئذٍ الحلقة for مطابقة للحلقة while (i < 3)‎. يمكنك فعليًا إزالة جميع الأجزاء، وخلق حلقة تكرار لانهائية: for (;;) { // تكرار لا نهائي } يرجى التحقق من وجود الفاصلتين المنقوطتين ; في صيغة حلقة التكرار for. خلاف ذلك، سيُطلَق خطأ. إيقاف حلقة التكرار يتوقف تنفيذ حلقة التكرار عندم عدم تحقُّق الشرط أي أصبح التقييم المنطقي للشرط false. مع ذلك، يمكنك إيقاف تنفيذ الحلقة في أي وقت باستخدام التعليمة break. في المثال التالي، تطلب حلقة التكرار من المستخدم إدخال سلسلة من الأرقام عن طريق الدالة prompt، ويتوقف تنفيذ الحلقة عندما لا يُدخَل أي رقم: let sum = 0; while (true) { let value = +prompt("Enter a number", ''); if (!value) break; // (*) sum += value; } alert( 'Sum: ' + sum ); عند النظر إلى الشيفرة، تُنفَّذ الكلمة المفتاحية break في السطر (*) إذا أدخل المستخدم سطرًا فارغًا أو ألغى عملية الإدخال وبذلك تتوقف حلقة التكرار فورًا، وينتقل تنفيذ الشيفرة إلى السطر الأول بعد حلقة التكرار أي إلى الدالة alert. يمكن استعمال حلقة أبدية (لانهائية) مع الكلمة المفتاحية break في الحالات التي لا يُعرَّف فيها متى يصبح الشرط غير محقَّقٍ. الاستمرار في التكرار التالي الموجِّه continue هو "نسخة أخف" من الموجِّه break، إذ لا يوقف تنفيذ حلقة التكرار بأكملها بل يوقف تنفيذ التكرار الحالي فقط، وينتقل لتنفيذ التكرار التالي (إذا تحقق الشرط طبعًا). أي نستعمله في الحالات التي نرغب فيها بإيقاف تنفيذ التكرار الحالي والانتقال إلى التكرار التالي. حلقة التكرار أدناه تستخدم الموجِّه continue لإخراج القيم الفردية فقط من الأعداد 0 وحتى 10: for (let i = 0; i < 10; i++) { // إذا تحقق التعبير، تخطى جسم الحلقة وانتقل للتكرار التالي if (i % 2 == 0) continue; alert(i); // 1, 3, 5, 7, 9 } فيما يخص القيم الزوجية i، يوقف التعبير البرمجي continue تنفيذ حلقة التكرار وينتقل إلى التكرار التالي في الحلقة for (مع الرقم التالي)، لذلك تظهر الدالة alert فقط القيم الفردية. تقليل مستوى التداخل عبر الموجِّه continue حلقة التكرار المسؤولة عن إخراج القيم الفردية سوف تبدو كما يلي: for (let i = 0; i < 10; i++) { if (i % 2) { alert( i ); } } من الناحية التقنية، هذا مشابه للمثال أعلاه. يمكنك استخدام الشرط if بدلًا من استخدام الموجِّه continue. ولكن سيؤدي ذلك لخلق مستوى إضافي من التداخل (استدعاء الدالة alert داخل الأقواس المعقوصة {}). تقل قابلية القراءة الإجمالية إذا كانت الشيفرة الموجودة داخل التعبير الشرطي if أطول من بضعة أسطر. لا يُستخدم الموجهان break/continue في المعامل الشرطي الثلاثي ? لاحظ أنه لا يمكن استخدام البنى التي لا تشبه صياغتها التعابير البرمجية مع المعامل الثلاثي ? تحديدًا موجهات مثل break/continue. إليك الشيفرة التالية: if (i > 5) { alert(i); } else { continue; } أعد كتابتها باستخدام المعامل الشرطي ?: (i > 5) ? alert(i) : continue; // هنا continue لا يسمح باستخدام الموجه توقفت الشيفرة عن العمل بسبب خطأ في الصياغة (syntax error)، وهذا سبب آخر لعدم استخدام المعامل ? بدلًا من الشرط if. تسمية حلقات التكرار تحتاج في بعض الأحيان لإيقاف حلقات تكرار متداخلة ومتعددة في وقت واحد. على سبيل المثال، تُنفذ حلقتي تكرار في الشيفرة التالية على المتغيرين i و j ، لإخراج الإحداثيات (i, j) من (0,0) إلى (3,3): for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { let input = prompt(`Value at coords (${i},${j})`, ''); // التي تليها؟ alert ماذا لو أردت الخروج من الحلقة والانتقال للدالة } } alert('Done!'); ستحتاج إلى طريقة لإيقاف حلقة التكرار إذا ألغى المستخدم الإدخال. استخدام الموجِّه break بعد input سيُوقف حلقة التكرار الداخلية فقط. هذا ليس كافيًا، فما الحل يا ترى؟! هنا يأتي دور اللافتات (labels)! اللافتة (label) عبارة عن مُعرِّف (وليست كلمةً محجوزةً) يتبعه نقطتين رأسيتين وتأتي قبل حلقة التكرار مباشرةً: labelName: for (...) { ... } يوقف الموجِّه break <labelName>‎ في المثال أدناه تنفيذه حلقة التكرار ذات المُعرِّف <labelName> (أي outer في حالتنا): outer: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { let input = prompt(`Value at coords (${i},${j})`, ''); // الخروج من كلتا الحلقتين إن ألغيت عملية الإدخال أو أعطيت قيم فارغة if (!input) break outer; // (*) // افعل شيئًا بالقيم المعطاة } } alert('Done!'); وظيفة الموجِّه break outer في المثال السابق إيقاف حلقة تكرار مسماة بالاسم outer؛ لذلك، ينتقل تنفيذ الشيفرة مباشرة من السطر (*) إلى السطر alert('Done!')‎. يمكنك وضع اللافتة في سطر منفصل: outer: for (let i = 0; i < 3; i++) { ... } يُستخدَم الموجِّه continue مع لافتة أيضًا وينتقل تنفيذ التعليمات البرمجية آنذاك إلى التكرار التالي ليس لحلقة التكرار الموجود فيها وإنما للحلقة الموسومة بتلك اللافتة. لا تسمح اللافتات بالقفز إلى أي مكان في الشيفرة لا تسمح لك اللافتات بالانتقال إلى أي سطر تريده في الشيفرة لتنفيذه إجباريًا، فمن المستحيل مثلًا تنفيذ المثال التالي: break label; // label لا يمكن القفز إلى السطر التالي المعنون بـ label: for (...) لا يمكن إستدعاء break/continue إلا من داخل حلقة تكرارية ويمكن استعمال لافتة معهما إن سبق عنونة الحلقة بها مسبقًا. الخلاصة تعرفت إلى الآن على ثلاثة أنواع من حلقات التكرار: while: تتحقَّق من الشرط قبل كل تكرار، ويُنفذ التكرار إذا كان الشرط محققًا. do..while: تتحقَّق من الشرط بعد كل تكرار، ولا يُنفذ التكرار التالي إذا لم يكن الشرط محققًا. for (;;): تتحقَّق من الشرط قبل كل تكرار، وهناك أيضًا عناصر تتيح التحكم أكثر بالحلقة. لبناء حلقة أبدية (لا نهائية)، تُستخدم حلقة التكرار while(true)‎ (أي أن يكون الشرط دائمًا محققًا)، ويمكن إيقاف هذه الحلقة مثل أي حلقة أخرى، عن طريق الموجِّه break. في حال عدم رغبتك بتنفيذ التكرار الحالي وتود الانتقال إلى التكرار التالي، فيمكنك استخدام الموجِّه continue. ويمكنك استخدام الموجين break/continue مع لافتات (labels) شرط أن تكون مُعرّفة قبل بداية حلقة التكرار. فتوفر اللافتة عندما استعمالها مع break/continue تحكمًا أكبر بالحلقات المتداخلة. التمارين قيمة حلقة التكرار الأخيرة الأهمية: 3 ما هي القيمة الأخيرة التي أظهرتها الدالة alert في هذه الشيفرة؟ ولماذا؟ let i = 3; while (i) { alert( i-- ); } الحل الإجابة: 1. let i = 3; while (i) { alert( i-- ); } تَنقص قيمة المتغير i بمقدار 1 في كل تكرار في حلقة التكرار هذه. تتوقف حلقة التكرار while(i)‎ عندما يتحقق الشرط i = 0، وبالتالي تشكل خطوات حلقة التكرار هذه التسلسل التالي: let i = 3; alert(i--); // بمقدار 1 لتصبح 2 i إظهار 3 ثم إنقاص قيمة alert(i--) // بمقدار 1 لتصبح 1 i إظهار 2 ثم إنقاص قيمة alert(i--) // بمقدار 1 لتصبح 0 i إظهار 1 ثم إنقاص قيمة // توقف الحلقة لعدم تحقق الشرط ما هي القيم التي ستظهرها حلقة التكرار while؟ الأهمية: 4 سجّل القيمة الناتجة من كل تكرار في الحلقة، ثم وازنها بالإجابة النهائية. هل تُظهر الدالة alert نفس القيم في الحلقتين أم لا؟ النموذج السابق i++: let i = 0; while (++i < 5) alert( i ); النموذج اللاحق ++i: let i = 0; while (i++ < 5) alert( i ); الحل يوضح هذا التمرين كيف يمكن أن يؤدي النموذج السابق/ اللاحق (postfix/prefix) إلى نتائج مختلفة عند استخدامهما في الموازنات. من 1 إلى 4 let i = 0; while (++i < 5) alert( i ); القيمة الأولى هي i = 1، لأن معامل الزيادة i++ الأول يزيد i ثم يُرجع القيمة الجديدة. لذلك الموازنة الأولى هي‎1 < 5 وتُظهر الدالة alert العدد 1. ثم تتبعها الأعداد 2، 3، 4. تستخدم الموازنة دائمًا القيمة الأخيرة، نظرًا لأن معامل الزيادة ++ قبل المتغير. وأخيرًا، يُزاد i = 4 إلى 5، ولا يتحقق شرط حلقة التكرار while(5 < 5)‎ في هذه الحالة، وتتوقف الحلقة. لذلك لا يظهر العدد 5. من 1 إلى 5 let i = 0; while (i++ < 5) alert( i ); القيمة الأولى هي i = 1. يزيد النموذج اللاحق ++i المتغير i ثم يُرجع القيمة القديمة، لذلك ستستخدم الموازنة i++ < 5 التعبير i = 0 (على عكس ‎++i < 5). لكن استدعاء الدالة alert منفصل وتُنفَّذ بعد معامل الزيادة والموازنة، لذلك تحصل على القيمة الحالية للمتغير i = 1. ثم تتبعها الأعداد 2، 3، 4. عند i = 4، يزيد النموذج السابق i++ قيمة المتغير ويستخدم العدد 5 في الموازنة ولكن هنا لدينا النموذج اللاحق ++i. أي أن قيمة المتغير i تصبح 5، لكنه يُرجع القيمة القديمة. وبهذا تصبح الموازنة while(4 < 5)‎- صحيحة، وتُنفذ الدالة alert. القيمة i = 5 آنذاك هي القيمة الأخيرة، لأن الشرط في التكرار التالي while(5 < 5)‎ غير مُحقق. ما القيم التي ستظهرها حلقة التكرار for؟ الأهمية: 4 سجّل القيمة الناتجة من كل حلقة تكرار، ثم قارنها بالإجابة. هل تُظهر الدالة alert نفس القيم في الحلقتين أم لا؟ النموذج اللاحق ++i: for (let i = 0; i < 5; i++) alert( i ); النموذج السابق i++: for (let i = 0; i < 5; ++i) alert( i ); الحل الإجابة في كلتا الحلقتين: من 0 إلى 4. for (let i = 0; i < 5; ++i) alert( i ); for (let i = 0; i < 5; i++) alert( i ); يمكنك بسهولة استنتاج التالي من الخوارزمية for: تُنفذ التعليمة i = 0 مرة واحدة في البداية. يتم يُتحقَّق من الشرط i < 5. إذا كان الشرط محققًا true- يُنفذ جسم الحلقة alert(i)‎ ويليه معامل الزيادة ++i. معامل الزيادة ++i منفصل عن الشرط في الحالتين، فهو عبارة عن تعليمة أخرى. لا تُستخدَم القيمة التي يعيدها معامل الزيادة هنا، لذلك لا يوجد فرق بين النموذجين ++i و i++. إخراج الأعداد الزوجية باستخدام حلقة التكرار الأهمية: 5 استخدم حلقة التكرار for لإظهار الأعداد الزوجية من 2 إلى 10. الحل for (let i = 2; i <= 10; i++) { if (i % 2 == 0) { alert( i ); } } يمكنك استخدام معامل باقي القسمة (modulo) % للحصول على باقي القسمة والتحقق من التكافؤ هنا. استبدال حلقة التكرار for بحلقة التكرار while الأهمية: 5 أعد كتابة الشيفرة باستبدال حلقة التكرار for بحلقة التكرار while دون تغيير سلوك الشيفرة (يجب أن يظل ناتج حلقة التكرار كما هو). for (let i = 0; i < 3; i++) { alert( `number ${i}!` ); } الحل let i = 0; while (i < 3) { alert( `number ${i}!` ); i++; } كرر حتى يكون الإدخال صحيحًا الأهمية: 5 اكتب حلقة تكرار تطلب من المستخدم إدخال عدد أكبر من 100. إذا أدخل المستخدم عدد آخر، فاطلب منه الإدخال مرة أخرى. يجب أن تسأل حلقة التكرار عن العدد حتى يُدخل المستخدم عدد أكبر من 100 أو يلغي الإدخال / يُدخل سطرًا فارغًا. في هذا التمرين، يدخل المستخدم الأعداد فقط أي ليس هناك حاجة لتنفيذ معالجة خاصة للتحقق من القيم المدخلة. الحل let num; do { num = prompt("Enter a number greater than 100?", 0); } while (num <= 100 && num); تتكرر حلقة التكرار do..while طالما أن الشرط num <= 100 && num محقق: الجزء الأول من الشرط num <= 100- أي أن القيمة التي أدخلها المستخدم لا تزال أقل من 100. الجزء الثاني من الشرط && num- لا يتحقق هذا الجزء إذا كان الإدخال null أو سلسلة نصية فارغة. تتوقف حلقة التكرار while في هذه الحالة أيضًا. ملاحظة: إذا كان المتغير num فارغًا null، فسيكون الجزء الأول من الشرط num <= 100 صحيحًا true، دون الجزء الثاني من الشرط لن تتوقف الحلقة إذا نقر المستخدم على زر الإلغاء (CANCEL). لذا لا يمكن الاستغناء عن أي الجزئين. إظهار الأعداد الأولية الأهمية: 3 يسمى العدد عددًا أولي (prime) إذا كان عددًا صحيحًا أكبر من 1، ولا يقبل القسمة (القسمة دون باقي قسمة) إلا على نفسه وعلى العدد 1. بعبارة أخرى، n > 1 المتغير n عبارة عن عدد أولي إذا كان لا يقبل القسمة بالتساوي على أي عدد باستثناء 1 و n. على سبيل المثال، العدد 5 هو عدد أولي، لأنه لا يمكن تقسيمه بالتساوي دون وجود باقي قسمة بمقدار 2، و 3 و4. اكتب الشيفرة التي تخرج الأعداد الأولية في المجال من 2 إلى n. إذا كانت n = 10 مثلًا، فستكون النتيجة 2,3,5,7. ملاحظة: يجب أن تعمل الشيفرة من أجل أي قيمة للمتغير n، أي لا يكون المتغير n معرّف لقيمة ثابتة. الحل هناك العديد من الخوارزميات لهذا التمرين: كتابة الشيفرة باستخدام حلقة تكرار متداخلة: For each i in the interval { check if i has a divisor from 1..i if yes => the value is not a prime if no => the value is a prime, show it } كتابة الشيفرة باستخدام اللافتة (label): let n = 10; nextPrime: for (let i = 2; i <= n; i++) { // i لكل قيمة من قيم for (let j = 2; j < i; j++) { // ابحث عن المقسوم عليه if (i % j == 0) continue nextPrime; // ليس عددًا أوليًا، انتقل للتكرار التالي } alert( i ); // عددٌ أولي } هناك مساحة كبيرة لتحسين الشيفرة، على سبيل المثال يمكنك البحث عن القواسم من 2 إلى الجذر التربيعي ل i (مثال: القواسم الموجبة للعدد 24 هي 1، 2، 3، 4، 6، 8، 12، 24). على أي حال، إذا كنت تريد تنفيذ التمرين السابق على مجالات كبيرة، فستحتاج إلى تغيير النهج والاعتماد على الرياضيات المتقدمة والخوارزميات المعقدة مثل المنخل التربيعي (Quadratic sieve) أو منخل الأعداد العام (General number field sieve) وما إلى ذلك. ترجمة -وبتصرف- للفصل Loops: while and for من كتاب The JavaScript Language table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: التعليمة switch المقال السابق: المعاملات المنطقية
  8. هناك ثلاثة معاملات منطقية في JavaScript وهي: || (OR)، و && (AND)، و ! (NOT). رغم أنها تسمى معاملات منطقية، إلا أنه يمكن تطبيقها على أي نوع من أنواع البيانات وليس فقط على البيانات المنطقية. دعنا نرى التفاصيل. المعامل || (OR) المنطقي يُمثَّل معامل OR المنطقي بخطين عموديين ||: result = a || b; في لغات البرمجة القديمة، يعالج المعامل OR المنطقي البيانات المنطقية فقط. إذا كانت أي من وسائطه (arguments) تحمل القيمة true، فإن المعامل يُرجع القيمة true، عدا ذلك يُرجع false. في JavaScript، المعامل أصعب قليلاً وأكثر فائدة، دعنا نرى ما يحدث مع البيانات المنطقية. هناك أربع مجموعات منطقية محتملة: alert( true || true ); // true alert( false || true ); // true alert( true || false ); // true alert( false || false ); // false كما ترى، النتيجة صحيحة true دائمًا باستثناء الحالة التي يكون فيها كلا العاملين (operands) خطأ false. إذا لم يكن العامل منطقيًا، سيُحوّل إلى قيمة منطقية لتتم عملية التقييم. على سبيل المثال، يتم التعامل مع العدد 1 على أنه true، والعدد 0 على أنه false: if (1 || 0) { // تمامًا true || false يقابل التعبير alert( 'truthy!' ); } غالبًا ما يُستخدام المعامل || في الجُمل الشرطية if لاختبار ما إذا كان أي من الشروط محقق true. على سبيل المثال: let hour = 9; if (hour < 10 || hour > 18) { alert( 'The office is closed.' ); } يمكن التحقُّق من عدَّة شروط في الوقت نفسه مثل: let hour = 12; let isWeekend = true; if (hour < 10 || hour > 18 || isWeekend) { alert( 'The office is closed.' ); //الوقت عطلة } المعامل OR يرجع القيمة الصحيحة الاولى المنطق الموصوف أعلاه قديم إلى حد ما. لنوضّح ميزات إضافية في JavaScript. الخوارزمية الموسعة تعمل على النحو التالي. بالنظر إلى قيم المعامل OR المتسَلسَلة في المثال التالي: result = value1 || value2 || value3; يقوم المعامل المنطقي || بالتالي: يقيم المعاملات (operand) من اليسار إلى اليمين. يحوِّل جميع العاملات (operands) إلى قيم منطقية إن لم تكن كذلك. إذا كانت النتيجة صحيحة true، يتوقف ويرجع القيمة الأصلية لذلك العامل. إذا قيِّمَت جميع العامِلات وكانت جميعها false خطأ، يرجع العامل الأخير. تُرجَع القيمة بشكلها الأصلي. بمعنى آخر، سلسلة من المعامل || تُرجع القيمة الصحيحة الأولى أو القيمة الأخيرة إذا لم يتم العثور على قيمة صحيحة. على سبيل المثال: alert( 1 || 0 ); // 1 العدد 1 عبارة عن القيمة الصحيحة alert( true || 'no matter what' ); // (true is truthy) alert( null || 1 ); // 1 القيمة الصحيحة الأولى هي alert( null || 0 || 1 ); // 1 القيمة الصحيحة الأولى هي alert( undefined || null || 0 ); // 0 جميع القيم خاطئة، يرجع القيمة الأخيرة هذه المميزات تؤدي إلى بعض الاستخدامات المثيرة للاهتمام موازنةً بالمعامل المنطقي (OR) الكلاسيكي، أو المنطقي فقط. 1- الحصول على أول قيمة صحيحة من قائمة المتغيرات أو التعبيرات. تخيل أن لدينا قائمة من المتغيرات التي يمكن أن تحتوي على بيانات أو تكون فارغة/غير محددة null/undefined. كيف يمكننا العثور على أول واحد مع البيانات؟ باستخدام المعامل المنطقي ||: let currentUser = null; let defaultUser = "John"; let name = currentUser || defaultUser || "unnamed"; alert( name ); // يختار القيمة الصحيحة الأولى إذا كان كلًا من المتغيرات currentUser و defaultUser خطأ، فستكون النتيجة unnamed. 2- تقييم الدارة القصيرة (Short-circuit) لا تكون العاملات (operands) قيمًا فقط، بل يمكن أن تكون تعبيرات عشوائية. المعامل المنطقي OR يفحصها ويقييمها من اليسار إلى اليمين، يتوقف التقييم عند الوصول إلى قيمة صحيحة وتُرجع القيمة. تسمى هذه العملية "تقييم الدارة القصيرة" لأنها تختصر عملية التقييم قدر الإمكان من اليسار إلى اليمين. يظهر هذا بوضوح عندما يكون التعبير المعطى كعامل ثاني، له تأثير جانبي مثل إسناد متغير. في المثال أدناه، لا يتم إسناد x: let x; true || (x = 1); alert(x); // undefined, because (x = 1) not evaluated إذا كان العامل الأول خطأ false، يقييم المعامل المنطقي || العامل الثاني، وبالتالي تستمر عملية الإسناد: let x; false || (x = 1); alert(x); // 1 عملية الإسناد عملية بسيطة. قد تكون هناك آثار جانبية، لن تظهر إذا كان التقييم لم يصل إليها. كما نرى، فإن حالة الاستخدام هذه هي "طريقة أقصر للقيام بالمعامل الشرطيif". يُحوَّل العامل الأول إلى قيمة منطقية؛ فإذا كان خطأ، فسيُقيَّم العامل الثاني. في أغلب الأحيان، من الأفضل استخدام المعامل الشرطي if بشكله الاعتيادي للحفاظ على سهولة فهم الشيفرة، ولكن لا يكون دائمًا في متناول اليد. المعامل AND) &&‎) المنطقي يُمثَّل المعامل AND المنطقي بعلامتين &&: result = a && b; عند كتابة شيفرة بلغات البرمجة القديمة، يُرجع المعامل AND المنطقي true إذا كان كلا العاملان صحيحين ويُرجع false عدا ذلك: alert( true && true ); // true alert( false && true ); // false alert( true && false ); // false alert( false && false ); // false مثال مع المعامل الشرطي if: let hour = 12; let minute = 30; if (hour == 12 && minute == 30) { alert( 'The time is 12:30' ); } تمامًا كما هو الحال مع المعامل المنطقي OR، يُسمح بأي قيمة لتكون عاملًا للمعامل المنطقي AND: if (1 && 0) { // تقييم كأنها صواب او خطأ alert( "won't work, because the result is falsy" ); } المعامل AND يرجع القيمة الخطأ الأولى بالنظر إلى قيم المعامل AND المتعدِّدة في المثال التالي: result = value1 && value2 && value3; يقوم المعامل المنطقي AND بالتالي: يقيم العاملات من اليسار إلى اليمين. يحوّل جميع العاملات إلى قيم منطقية. إذا كانت النتيجة false، يتوقف ويرجع القيمة الأصلية لذلك العامل. إذا قيِّمَت جميع العامِلات وكانت جميعها صحيحة، يُرجَع العامل الأخير. بمعنى آخر، سلسلة من المعامل AND تُرجع القيمة الخطأ الأولى أو القيمة الأخيرة إذا لم يُعثَر على أي قيمة خطأ. القواعد المذكورة أعلاه تشبه قواعد المعامل المنطقي OR. الفرق هو أن المعامل المنطقي AND يُرجع القيمة الخطأ الأولى بينما يُرجع المعامل المنطقي OR القيمة الصحيحة الأولى. على سبيل المثال: // إذا كان العامل الأول صحيحًا، يرجع // العامل الثاني AND المعامل المنطقي alert( 1 && 0 ); // 0 alert( 1 && 5 ); // 5 // إذا كان العامل الأول خطأ، يرجع المعامل // هذا العامل ويتجاهل العامل الثاني AND المنطقي alert( null && 5 ); // null alert( 0 && "no matter what" ); // 0 يمكننا أيضا سَلسَلة العديد من القيم على التوالي. انظر كيف تُرجَع أول قيمة خطأ: alert( 1 && 2 && null && 3 ); // null عندما تكون جميع القيم صحيحة، تُرجَع آخر قيمة: alert( 1 && 2 && 3 ); // 3, القيمة الأخيرة أولوية المعامل المنطقي && في التنفيذ أعلى من أولوية المعامل المنطقي || لذلك، التعبير التالي a && b || c && d هو نفس التعبير (a && b) || (c && d) مع الأقواس التي لها الأولية دومًا. يمكن استعمال المعامل المنطقي && بدلًا من if مثل المعامل المنطقي ||. على سبيل المثال: let x = 1; (x > 0) && alert( 'Greater than zero!' ); لن ينفَّذ الإجراء في الجزء الأيمن الخاص بالمعامل المنطقي && إلا إذا وصل التقييم إليه، أي فقط إذا كان x>0 محقَّقًا (صحيحًا): let x = 1; if (x > 0) { alert( 'Greater than zero!' ); } يبدو المثال الأول الذي استعملنا فيه المعامل المنطقي && أقصر، ولكن المثال الثاني الذي استعلمنا فيه if أوضح أضف إلى سهولة قراءة الشيفرة. لذلك، نوصي باستخدام كل معامل للغرض المناسب له: أي يمكنك استخدام التعليمة الشرطية if عندم وجود شرط، واستعمل المعامل المنطقي &&عند التعامل مع القيم المنطقية. المعامل المنطقي ! (NOT) المعامل المنطقي NOT يُعبَّر عنه بعلامة التعجب !. الصيغة الخاصة به بسيطة للغاية: result = !value; يقبل المعامل المنطقي NOT عاملًا واحدًا (operand) ويقوم بما يلي: يُحوِّل العامل إلى قيمة منطقية: true/false. يرجع القيمة العكسية لتلك القيمة المنطقية. على سبيل المثال: alert( !true ); // false alert( !0 ); // true يُستخدَم المعامل المنطقي NOT المزدوج (أي !!) في بعض الأوقات لتحويل قيمة معينة إلى قيمة منطقية: alert( !!"non-empty string" ); // true alert( !!null ); // false بمعنى، المعامل المنطقي NOT الأول (أي ‎!"non-empty string"‎) يحول القيمة إلى قيمة منطقية (true!) ويرجع القيمة العكسية (false)، والمعامل المنطقي NOT الثاني (أي ‎!!"non-empty string"‎) يرجع القيمة العكسية مرة أخرى للقيمة التي أعادها المعامل الأول (true الناتج النهائي). في النهاية، يوفر لنا المعامل !! وسيلةً لتحويل أي قيمة إلى قيمة منطقية بسهولة. هناك طريقة مطوّلة أكثر قليلاً لفعل الشيء نفسه - باستخدام الدالة Boolean: alert( Boolean("non-empty string") ); // true alert( Boolean(null) ); // false أولوية المعامل المنطقي ! هي الأعلى بين جميع المعاملات المنطقية الأخرى، لذلك ينفذ أولًا قبل المعامل && والمعامل ||. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } تمارين ما هي نتيجة المعامل OR الأهمية: 5 ما ناتج الشيفرة التالية؟ alert( null || 2 || undefined ); الحل: الإجابة هي 2، هذه هي القيمة الصحيحة الأولى. alert( null || 2 || undefined ); ما هي نتيجة سلسلة من المعامل OR للدالة alert الأهمية: 3 ما ناتج الشيفرة التالية؟ alert( alert(1) || 2 || alert(3) ); الحل: الإجابة هي: أولًا 1 ثم 2 alert( alert(1) || 2 || alert(3) ); استدعاء الدالة alert لا يُرجع قيمة. بصيغة أخرى، يرجع undefined . معامل OR الأول || يُقييم العامل الأيسر alert(1)‎ وهذا يُظهر رسالةً تحوي 1. الدالة alert ترجع undefined، لذلك ينتقل المعامل OR للعامل التالي بحثًا عن قيمة صحيحة. العامل الثاني 2 عبارة عن قيمة صحيحة (أي يقيَّم إلى true)، وبذلك يتوقف تنفيذ سلسلة المعامل OR ويُرجع العامل 2 ثم يُعرَض بواسطة الدالة alert. لن يظهر العدد 3، لأن التقييم انتهى عند العامل الثاني و لن يصل إلى alert(3)‎. ما هي نتيجة المعامل AND الأهمية: 5 ما ناتج الشيفرة التالية؟ alert( 1 && null && 2 ); الحل: الإجابة هي: null لأنها القيمة الخطأ الأولى في التعبير: alert( 1 && null && 2 ); ما هي نتيجة سلسلة من المعامل AND للدالة alert الأهمية: 3 ماذا ستُظهر الشيفرة التالية؟ alert( alert(1) && alert(2) ); الحل: الإجابة هي: 1 ثم undefined: alert( alert(1) && alert(2) ); استدعاء الدالة alert يُرجع undefined (هي فقط تُظهر رسالة للمستخدم، أي ليس هناك شيء ذا معنى لإعادته). لهذا السبب، يقيِّم المعامل && العامل الأيسر (المخرجات 1) ، ويتوقف على الفور، لأنَّ القيمة undefined هي قيمة خطأ (أي false). والمعامل && يبحث عن أول قيمة خطأ ويُرجعها، فقط. الناتج من سَلسلة المعاملات على النحو التالي (OR AND OR) الأهمية: 5 ما الناتج من الشيفرة التالية؟ alert( null || 2 && 3 || 4 ); الحل: الإجابة هي: 3. alert( null || 2 && 3 || 4 ); أولوية المعامل && في التنفيذ أكبر من أولوية المعامل ||، لذلك يُنفَّذ أولًا. نتيجة لذلك 2 && 3 = 3، يصبح التعبير: null || 3 || 4 الآن، الإجابة هي القيمة الصحيحة الأولى: 3. حصر قيمة متغير ضمن مجال الأهمية: 3 اكتب شرطًا (أي if) للتحقق من أنَّ قيمة المتغير age محصورة بين 14 و 90 (داخلة ضمن المجال). الحل: if (age >= 14 && age <= 90) حصر متغير خارج مجال الأهمية: 3 اكتب شرطًا (أي if) للتحقق من أنَّ قيمة المتغير age لا تقع ضمن 14 و 90 (داخلة ضمن المجال). أنشئ تعبيرين مختلفين: الأول باستخدام معامل النفي !، والثاني دونه. الحل: التعبير الأول: if (!(age >= 14 && age <= 90)) التعبير الثاني: if (age < 14 || age > 90) سؤال باستخدام التعبير الشرطي if الأهمية: 5 أي دالة من الدوال alert سوف تُنفَّذ؟ ماذا ستكون نتائج التعبيرات الموجودة في داخل التعبير الشرطي if(...)‎: if (-1 || 0) alert( 'first' ); if (-1 && 0) alert( 'second' ); if (null || -1 && 1) alert( 'third' ); الحل: الإجابة هي: التعبير الأول والثالث والسبب: // يعمل // ناتج 0 || 1- هو 1-، أول قيمة صحيحة if (-1 || 0) alert( 'first' ); // لا يعمل // ناتج 0 && 1- هو 0، قيمة خطأ if (-1 && 0) alert( 'second' ); // يُنفَّذ بالشكل التالي // || المعامل && له أولوية في التنفيذ أكبر من المعامل // || يُنفذ أولًا 1 && 1- ثم المعامل // null || -1 && 1 -> null || 1 -> 1 if (null || -1 && 1) alert( 'third' ); التحقق من تسجيل الدخول الأهمية: 3 اكتب الشيفرة التي تطلب من المستخدم تسجيل الدخول بواسطة الدالة prompt. إذا أدخل المستخدم Admin، فاطلب كلمة مرور بواسطة الدالة prompt، إذا كان الإدخال عبارة عن سطر فارغ أو مفتاح الهروب Esc - فأظهر "تم الإلغاء"، إذا كان عبارة عن سلسلة نصية أخرى - فأظهر "لا أعرفك". تُقحص كلمة المرور على النحو التالي: إذا كانت تساوي "TheMaster"، سيُعرَض "مرحبًا!" ، سلسلة نصية أخرى، سيُعرَض "خطأ" ، سلسلة نصية فارغة أو "Cancel"، أظهر "إلغاء". يُرجى استخدام الشرط المتداخل (nested) للمحافظة على سهولة قراءة الشيفرة. ملاحظة: تمرير قيمة فارغة للدالة prompt يُرجِع سلسلة فارغة ''. الضغط على مفتاح الهروب ESC أثناء تنفيذ الدالة prompt يُرجِع null. الحل: let userName = prompt("من أنت؟", ''); if (userName == 'Admin') { let pass = prompt(ما كلمة المرور؟', ''); if (pass == 'TheMaster') { alert( 'مرحبًا!' ); } else if (pass == '' || pass == null) { alert( 'إلغاء' ); } else { alert( 'خطأ' ); } } else if (userName == '' || userName == null) { alert( 'إلغاء' ); } else { alert( "لا أعرفك" ); } لاحظ أن المسافات البادئة الرأسية داخل الشرط if غير مطلوبة من الناحية التقنية، لكنها تجعل الشيفرة سهلة القراءة. ترجمة -وبتصرف- للفصل Logical operators من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: حلقتا التكرار while و for المقال السابق: المعاملات الشرطية
  9. نحتاج في بعض الأحيان إلى تنفيذ إجراءات مختلفة بناءً على شروط مختلفة. للقيام بذلك، يمكنك استخدام التعبير الشرطي if والمعامل الشرطي ? الذي يسمى أيضًا «معامل علامة استفهام» (question mark operator، أو المعامل الثلاثي كما سنرى من صياغته). التعبير الشرطي if يُقيّم التعبير الشرطي if شرطًا، فإذا تحقَّق true، فيُنفِّذ مجموعة من الشيفرات البرمجية. على سبيل المثال: let year = prompt('In which year was ECMAScript-2015 specification published?', ''); if (year == 2015) alert( 'You are right!' ); في المثال أعلاه، يكون الشرط عبارة عن فحص بسيط لعملية المساواة year == 2015، ولكنه قد يصبح أكثر تعقيدًا. إذا كنت ترغب في تنفيذ أكثر من تعبير واحد، يجب أن تضع الشيفرة المطلوبة داخل الأقواس المعقوصة {}: if (year == 2015) { alert( "That's correct!" ); alert( "You're so smart!" ); } من الأفضل استخدام الأقواس المعقوصة {} في كل مرة تستخدم فيها التعبير الشرطي if، حتى إذا كنت تريد تنفيذ تعبير واحد فقط لأنَّها تُسهِّل قراءة الشيفرة. التحويل المنطقي يُقيِّم التعبير الشرطي if التعبير الموجود بين القوسين، ثم تُحوّل النتيجة إلى قيمة منطقية. هل تتذكر قواعد التحويل من الفصل التحويل بين الأنواع؟ إذن، لنتذكرها سويةً: يُحوَّل العدد 0، والسلسلة النصية الفارغة ""، و null، و undefined، وNaN جميعها إلى القيمة false. يُطلَق عليها بسبب ذلك «قيم زائفة خاطئة» (falsy values). تُحوَّل القيم الأخرى (أي باستثناء ما سبق) إلى القيمة المنطقية true، لذلك يطلق عليها «القيم الصادقة الصحيحة» (truthy values). لذلك، لن تُنفَّذ الشيفرة البرمجية التالية بناءً على الشرط المعطى: if (0) { // false القيمة 0 تُقيَّم إلى القيمة ... } بينما ستُنفَّذ شيفرة الشرط التالي: if (1) { // true ألقيمة 1 تُقيَّم إلى القيمة ... } يمكنك أيضًا تمرير قيمة منطقية قُيِّمَت مسبقًا إلى الشرط if بالشكل التالي: let cond = (year == 2015); // قيمة عملية التحقق من المساواة هي قيمة منطقية if (cond) { ... } الكتلة الشرطية else قد يحتوي التعبير الشرطي if على كتلة اختيارية تسمى else تُنفذ عندما يكون الشرط غير محقَّق. أي إن تحقق الشرط، فنفِّذ كذا، أو نفِّذ كذا. إليك المثال التالي: let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year == 2015) { alert( 'You guessed it right!' ); } else { alert( 'How can you be so wrong?' ); // أي قيمة باستثناء 2015 } الشروط المتعددة else if تود في بعض الأحيان التحقق من العديد من الحالات لشرط ما. التعبير الشرطي else if (أو إذا كان كذا، فنفِّذ كذا) تفي بهذا الغرض. اطلع على هذا المثال: let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year < 2015) { alert( 'Too early...' ); } else if (year > 2015) { alert( 'Too late' ); } else { alert( 'Exactly!' ); } في الشيفرة أعلاه، تتحقق JavaScript أولاً من الشرط year < 2015. فإذا كان ذلك غير محقَّق، فسيتم الانتقال إلى الشرط التالي year > 2015. وإذا كان هذا أيضًا غير محقَّق، فستُنفَّذ الكتلة المرتبطة بالفرع else أي تُنفَّذ الدالة alert. يمكن أن يكون هناك أكثر من فرع else if، والكتلة الشرطية الأخيرة else اختيارية. المعامل الشرطي ? تحتاج في بعض الأحيان إلى إسناد قيمة لمُتغيِّر وفقًا لشرط ما. يمكن تحقيق ذلك بالشكل التالي: let accessAllowed; let age = prompt('How old are you?', ''); if (age > 18) { accessAllowed = true; } else { accessAllowed = false; } alert(accessAllowed); يتيح لك المعامل الشرطي ? أو «علامة الاستفهام» القيام بذلك بطريقة أقصر وأبسط. يُمثِّل هذا المعامل بعلامة استفهام ?. في بعض الأحيان يُطلَق عليه «المعامل الثلاثي» (ternary)، لأن لديه ثلاثة عاملات (operands) وهو المعامل الوحيد في JavaScript الذي يحتوي على هذا العدد. الصيغة الخاصة به: let result = condition ? value1 : value2; يتم تقييم الشرط condition: إذا كان محقَّقًا، يُرجع value1، عدا ذلك يُرجِع value2. على سبيل المثال: let accessAllowed = (age > 18) ? true : false; من الناحية التقنية، يمكنك حذف الأقواس الموجودة حول age > 18. المعامل الشرطي هذا (أي علامة الاستفهام) له أولوية منخفضة، لذلك يُنفَّذ بعد معامل الموازنة <. يشبه المثال التالي المثال السابق مع اختلاف طفيف جدًا: // أولًا age > 18 يُنفَّذ معامل الموازنة // (لا حاجة لاستخدام الأقواس) let accessAllowed = age > 18 ? true : false; لكن الأقواس تجعل الشيفرة أكثر قابلية للقراءة، لذلك نوصي باستخدامها. ملاحظة: في المثال أعلاه، تجنب استخدام معامل علامة الاستفهام لأنَّ معامل الموازنة نفسه يُرجِع true/false؛ أي المثال السابق يكافئ: let accessAllowed = age > 18; المعامل الشرطي ? المتعدد يمكن لسلسلة من المعاملات الشرطية ? إرجاع القيمة التي تعتمد على أكثر من شرط واحد. اطلع بتفحُّص على المثال التالي: let age = prompt('age?', 18); let message = (age < 3) ? 'Hi, baby!' : (age < 18) ? 'Hello!' : (age < 100) ? 'Greetings!' : 'What an unusual age!'; alert( message ); قد يكون من الصعب فهم الشيفرة السابقة في البداية. ولكن بعد التمعُّن بها قليلًا، يمكنك أن ترى أنَّها مجرد تسلسل عادي من الاختبارات: يتحقق المعامل ? الأول من الشرط age < 3 إذا كان الشرط السابق محقَّقًا، فسيُرجِع Hi, baby!‎؛ خلاف ذلك، فإنَّه يستمر في التحقق من التعبير بعد النقطتين :، ويتحقق من الشرط age < 18. إذا كان الشرط السابق محقَّقًا، فسيُرجِع Hello!‎؛ خلاف ذلك، فإنه يستمر في التحقق من التعبير بعد النقطتين : الثانية، ويَتحقَّق من الشرط age < 100. إذا كان الشرط السابق محقَّقًا، فسيُرجِع Greetings! خلاف ذلك، فإنه يستمر في التحقق من التعبير بعد النقطتين : الأخيرتين، ويتحقق من الشرط What an unusual age!‎. إليك تنفيذ المثال السابق باستخدام if..else فقط: if (age < 3) { message = 'Hi, baby!'; } else if (age < 18) { message = 'Hello!'; } else if (age < 100) { message = 'Greetings!'; } else { message = 'What an unusual age!'; } الاستخدام غير التقليدي للمعامل ? في بعض الأحيان، يُستخدَم معامل علامة الاستفهام ? بديلًا عن المعامل الشرطي if بالشكل التالي: let company = prompt('Which company created JavaScript?', ''); (company == 'Netscape') ? alert('Right!') : alert('Wrong.'); اعتمادًا على الشرط company == 'Netscape'‎، إمَّا أن يُنفَّذ التعبير الأول أو الثاني بعد المعامل ? وتٌظهر الدالة alert القيمة الناتجة بناءً على الشرط. على أي حال، لا نوصي باستخدام معامل علامة الاستفهام ? بهذه الطريقة. الشيفرة السابقة أقصر من شيفرة المعامل الشرطي if المقابلة، الذي يطبقه بعض المبرمجين لكنه يولد شيفرة صعبة القراءة. سنعيد كتابة الشيفرة السابقة باستخدام المعامل الشرطي if: let company = prompt('Which company created JavaScript?', ''); if (company == 'Netscape') { alert('Right!'); } else { alert('Wrong.'); } عند إلقاء نظرة على الشيفرة للوهلة الأولى، من السهل فهم كتل التعليمات البرمجية التي تمتد عموديًّا لعدة أسطر بشكل أسرع من تلك الأفقية الطويلة. الغرض من معامل علامة الاستفهام ? هو إرجاع قيمة ما حسب الشرط المُعطى. يُفضل استخدامه لذلك فقط. استخدم المعامل الشرطي if عندما تحتاج لتنفيذ فروع مختلفة من الشيفرة. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } تمارين التعبير الشرطي if (سلسلة نصية مع صفر) الأهمية: 5 هل ستُنفَّذ الدالة alert داخل الشرط التالي؟ if ("0") { alert( 'Hello' ); } الحل: نعم، سوف تُنفَّذ وتظهر الرسالة. الغرض من هذا التمرين هو التذكير بأنَّ أي سلسلة نصية باستثناء الفارغة منها (من ضمنها "0" الغير فارغة) تُحوَّل إلى القيمة true في السياق المنطقي. اسم JavaScript الأهمية: 2 باستخدام الصيغة if..else، اكتب الشيفرة التي تسأل: "ما هو الاسم الرسمي لجافاسكربت؟" إذا أدخل المستخدم "ECMAScript"، تخرج الشيفرة "صحيح!"، وإلا - تُخرج: "ألا تعرف؟ ECMAScript!" الحل: <!DOCTYPE html> <html> <body> <script> 'use strict'; let value = prompt('ما هو الاسم الرسمي لجافاسكربت؟', ''); if (value == 'ECMAScript') { alert('صحيح!'); } else { alert("ألا تعرف الاسم الرسمي؟ إنه ECMAScript!"); } </script> </body> </html> إظهار إشارة الأهمية: 2 باستخدام الصيغة if..else، اكتب الشيفرة التي تحصل على عدد عن طريق الدالة prompt ثم أظهر عبر الدالة alert القيمة: 1 إذا كان العدد أكبر من صفر. -1 إذا كان العدد أقل من صفر. 0 إذا كان العدد يساوي الصفر. في هذا التمرين، نفترض أنَّ القيمة المُدخلة دائمًا عدد. الحل: let value = prompt('Type a number', 0); if (value > 0) { alert( 1 ); } else if (value < 0) { alert( -1 ); } else { alert( 0 ); } تحويل التعبير الشرطي if إلى صيغة المعامل ? الأهمية: 5 أعد كتابة التعبير الشرطي if باستخدام المعامل الثلاثي ? if (a + b < 4) { result = 'Below'; } else { result = 'Over'; } الحل: result = (a + b < 4) ? 'Below' : 'Over'; تحويل التعبير الشرطي if..else إلى صيغة المعامل ? الأهمية: 5 أعد كتابة التعبير الشرطي if..else باستخدام المعامل الثلاثي ?. لتسهيل قراءة الشيفرة، يوصى بتقسيمها إلى أسطر متعددة . let message; if (login == 'Employee') { message = 'Hello'; } else if (login == 'Director') { message = 'Greetings'; } else if (login == '') { message = 'No login'; } else { message = ''; } الحل let message = (login == 'Employee') ? 'Hello' : (login == 'Director') ? 'Greetings' : (login == '') ? 'No login' : ''; ترجمة -وبتصرف- للفصل conditional operators من كتاب The JavaScript Language .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } اقرأ أيضًا المقال التالي: المعاملات المنطقية المقال السابق: الدوال التفاعلية: confirm ،prompt ،alert
  10. في هذا الجزء من المقالات التعليمية، نتطرَّق إلى لغة JavaScript كما هي بدون تعديلات خاصة بالبيئة. لكن ما زلنا نستخدم المتصفح كبيئة تجريبية، لذلك يجب أن تتعرف على عددٍ قليلٍ من دوال واجهة المستخدم الخاصة به. ستتعرف في هذا الفصل على هذه الدوال التفاعلية الخاصة بالمتصفح. الدالة alert الصياغة: alert(message); تعرض هذه الدالة رسالة نصية وتوقف تنفيذ السكربت مؤقتًا حتى يضغط المستخدم على «موافق» (OK). إليك الشيفرة البسيطة التالية مثلًا: alert("مرحبًا"); تسمى الرسالة النصية التي تظهر على شكل نافذة صغيرة تدعى «النافذة المنبثقة الشرطية» (modal window، وهي عنصر تحكم رسومي)؛ تعني كلمة «شرطية» أنه لا يمكن للزائر التفاعل مع بقية الصفحة، أو الضغط على أزرار أخرى وما إلى ذلك، إذ تشترط عليه التفاعل معها فقط، أي حتى يضغط على «موافق» (Ok) في هذه الحالة. الدالة prompt تقبل الدالة prompt وسيطين (arguments) لتكون صياغتها بالشكل التالي: result = prompt(title, [default]); تعرض هذه الدالة نافذة منبثقة شرطية مع رسالة نصية مخصصة، وحقل إدخال للمستخدم، وزرَّين (موافق [OK] وإلغاء [CANCEL]). الوسيط title: هو عبارة عن النص الذي سيعرض للمستخدم. الوسيط default: هو وسيط اختياري يمثِّل القيمة الأولية لحقل الإدخال الخاص بالمستخدم. قد يكتب المستخدم شيئًا ما في حقل الإدخال، ثمَّ يضغط على موافق (Ok). أو يمكنه إلغاء الإدخال عند الضغط على إلغاء (CANCEL) أو الضغط على مفتاح الهروب (Esc). استدعاء الدالة prompt يرجع سلسلة نصية تمثِّل القيمة التي أدخلها المستخدم في حقل الإدخال أو يرجع القيمة null إذا تم الخروج من النافذة وإلغائها. جرب نفِّذ المثال التالي في الطرفية وعدل عليه: let age = prompt('كم عمرك؟', 100); alert(`عمرك ${age} سنة!`); في IE (أي المتصفح Internet Explorer)، دائمُا ما يتم إضافة الوسيط default. أي هذا الوسيط اختياري في جميع المتصفحات باستثناء المتصفح IE الذي يعدُّه اجباريًّا، وإذا لم نحدِّد قيمته، يفترض المتصفح Internet Explorer أنَّ قيمته "undefined". نفِّذ هذه الشيفرة في متصفح Internet Explorer لرؤية الناتج: let test = prompt("Test"); لجعل الدالة prompt تعمل جيدًا في المتصفح IE، نوصي دائمًا بتمرير قيمة الوسيط الثاني default: let test = prompt("Test", ''); // <-- IE للمتصفح الدالة confirm الصياغة: result = confirm(question); تُظهر الدالة confirm نافذة منبثقة شرطية تحتوي على سؤال question، وزريّن (موافق [OK] وإلغاء [CANCEL]). تكون النتيجة true إذا ضغط المستخدم على الزر "Ok" وتكون false عدا ذلك. جرِّب المثال التالي في طرفيتك: let isBoss = confirm("Are you the boss?"); alert( isBoss ); الخلاصة ذكرنا في هذا المقال ثلاثة دوال للتفاعل مع مستخدمي الموقع وهي: الدالة alert: تعرض رسالة لإعلام المستخدم بشئ ما، وُتعطل كافة عمليات الصفحة حتى يتفاعل مع هذه الرسالة. الدالة prompt: تعرض رسالة تطلب من المستخدم إدخال شيء ما في حقل إدخال خاص لتعيد القيمة المدخلة في سلسلة نصية، أو ترجع القيمة null إذا تم العملية. الدالة confirm: تعرض رسالة (بمثابة سؤال) وتنتظر من المستخدم الرد عليها بالقبول أو الرفض، أي تكون النتيجة true إذا تم الضغط على زر "Ok" أو تكون false عدا ذلك. كل هذه الدوال مشروطة: فهي تتوقف عن تنفيذ السكربت ولا تسمح للمستخدم بالتفاعل مع بقية الصفحة حتى يتم التفاعل مع النافذة التي تعرضها. هناك اثنين من القيود التي تشترك بها جميع الدوال المذكورة أعلاه: يحدد المتصفح الموقع الذي ستظهر فيه النافذة، وعادة ما يكون في الوسط أو الأعلى. يعتمد شكل النافذة أيضًا على المتصفح، ولا يمكننا تعديله. هذا هو ثمن البساطة. هناك طرق أخرى لإظهار نوافذ أكثر جمالًا وفاعلية، ولكن إذا كانت التنسيقات الجمالية غير مهمة، فهذه الدوال تفي بالغرض. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } تمارين صفحة بسيطة الأهمية: 4 قم بإنشاء صفحة ويب تطلب اسمًا ما ثم تعرضه. الحل شيفرة JavaScript: let name = prompt("ما اسمك؟", ""); alert(name); الشيفرة كاملة: <!DOCTYPE html> <html> <body> <script> 'use strict'; let name = prompt("ما اسمك؟", ""); alert(name); </script> </body> </html> ترجمة -وبتصرف- للفصل Interaction: alert, prompt, confirm من كتاب The JavaScript Language اقرأ أيضًا المقال التالي: المعاملات الشرطية المقال السابق: معاملات الموازنة كامل مقالات دليل تعلم جافاسكربت
  11. لا شك أنَّك تعرَّفت مسبقًا على معاملات الموازنة من أحد دروس الرياضيات في المدرسة وهي: أكبر/أصغر من: a < b - a > b. أكبر/أصغر من أو يساوي: a <= b - a >= b. المساواة: a == b (يرجى ملاحظة أنَّ هذا المعامل عبارة عن علامة يساوي مزدوجة =. العلامة المنفردة a = b خاصة بمعامل الإسناد الذي تحدثنا عنه في المقال السابق). عدم المساواة: يُعبر عنه في الرياضيات بالرمز≠، ولكن يُعبِّر عنه برمجيًّا في JavaScript بإشارة يساوي منفردة مع إشارة تعجب قبلها بالشكل: a != b. النتيجة عبارة عن قيمة منطقية مثل كل المعاملات الأخرى، تُرجِع معاملات الموازنة قيمة منطقية دومًا. true - تعني «نعم» أو «صواب» أو «الموازنة مُحقَّقة». false - تعني «لا» أو «خطأ» أو «الموازنة غير مُحقَّقة». إليك أمثلة عن هذه المعاملات: alert( 2 > 1 ); // true (الموازنة صحيحة) alert( 2 == 1 ); // false (الموازنة خطأ) alert( 2 != 1 ); // true (الموازنة صحيحة) يمكن إسناد نتيجة معامل الموازنة لمتغير تمامًا مثل أي قيمة: let result = 5 > 4; // إسناد الناتج من عملية الموازنة لمتغير alert( result ); // true موازنة السلاسل النصية لمعرفة ما إذا كانت السلسلة النصية أكبر من الأخرى، تستخدم JavaScript ما يسمى بترتيب القاموس (dictionary) أو ترتيب المعجم (lexicographical). بمعنى آخر، تتم موازنة السلاسل النصية حرفًا تلو الآخر. على سبيل المثال: alert( 'Z' > 'A' ); // true alert( 'Glow' > 'Glee' ); // true alert( 'Bee' > 'Be' ); // true خوارزمية موازنة سلسلتين نصيتين بسيطة: موازنة الحرف الأول من كلا السلسلتين. إذا كان الحرف الأول من السلسلة النصية الأولى أكبر (أو أقل) من السلسلة النصية الأخرى، حينها تكون الأولى أكبر (أو أقل) من الثانية وبذلك تنتهي عملية الموازنة. لكن إذا كانت الأحرف الأولى في كلا السلسلتين متماثلة، وازن الأحرف الثانية بالطريقة نفسها. كرر حتى نهاية أي سلسلة. إذا انتهت كلا السلسلتين بالطول نفسه، فهما متساويتان. خلاف ذلك يكون السلسلة النصية الأطول هو الأكبر. في الأمثلة أعلاه، تحصل الموازنة 'Z' > 'A' على النتيجة في الخطوة الأولى بينما تُوازن السلسلتين النصيتين "Glow" و "Glee" حرفًا تلو الآخر حتى الحرف الأخير بالتسلسل التالي: G هو نفسه G. l هو نفسه l. o أكبر من e. توقف هنا، السلسلة الأولى أكبر. ليس قاموس حقيقي، ولكنه ترتيب ترميز اليونيكود إنَّ خوارزمية الموازنة المذكورة أعلاه تعادل تقريبًا تلك المستخدمة في القواميس ودفاتر الهاتف، ولكن مع اختلاف بسيط يتعلق بحالة الحرف. فالحرف الكبير "A" لا يساوي الحرف الصغير "a" كما تعتقد فأيهما أكبر برأيك؟ الحرف الصغير "a" طبعًا. أسمعك تسأل لماذا؟ لأنَّ الحرف الصغير له رقم تسلسلي أكبر في جدول الترميز المقابل له في جدول اليونيكود المستخدم في JavaScript، حسنًا، سكفي إلى هنا وسنعاود الحديث عن هذا الموضوع والنتائج المترتبة عليه في فصل السلاسل النصية. موازنة بين أنواع البيانات المختلفة عند موازنة أنواع بيانات مختلفة، تحوّل JavaScript القيم إلى أعداد. على سبيل المثال: alert( '2' > 1 ); // true, تُحوَّل السلسلة النصية '2' إلى العدد 2 alert( '01' == 1 ); // true, تُحوَّل السلسلة النصية '01' إلى العدد 1 بالنسبة للقيم المنطقية، true تصبح 1 و false تصبح 0. على سبيل المثال: alert( true == 1 ); // true alert( false == 0 ); // true نتيجة مضحكة أليس كذلك؟! من الممكن في الوقت نفسه أن تكون القيمتان: متساويتين. واحدة منهما true والأخرى false. إليك مثال عن حالة مماثلة: let a = 0; alert( Boolean(a) ); // false let b = "0"; alert( Boolean(b) ); // true alert(a == b); // true! من وجهة نظر JavaScript، هذه النتيجة طبيعية تمامًا. تُحوّل عملية التحقق من المساواة القيم باستخدام التحويل العددي (وبالتالي السلسلة النصية "0" تساوي القيمة 0)، بينما يستخدم التحويل الصريح Boolean مجموعة أخرى من القواعد. معامل المساواة الصارمة معامل المساواة العادية == لديه مشكلة، وهي عدم قدرته على التمييز بين 0 و false: alert( 0 == false ); // true يحدث الشيء نفسه مع السلسلة النصية الفارغة: alert( '' == false ); // true يحدث هذا لأنَّ الأنواع المختلفة من العاملات تُحول إلى أعداد بواسطة معامل المساواة ==. السلسلة النصية الفارغة مثل false، تصبح صفرًا. ماذا نفعل إذا كنا نرغب في التمييز بين 0 و false؟ معامل المساواة الصارمة === يتحقق من المساواة دون تحويل القيم. بمعنى آخر، إذا كان a وb قيمتين من نوعين مختلفين، فسوف تُرجع المعادلة a === b على الفور false دون محاولة تحويلها، وبذلك يتحقق المعامل === من تساوي النوع أولًا ثم تساوي القيمة. دعنا نجرب: alert( 0 === false ); // false, لأن القيمتين من نوعين مختلفين هناك أيضًا معامل عدم مساواة صارم ‎!==‎ مماثل للمعامل ‎!=‎. يعد معامل المساواة الصارمة أطول قليلاً في الكتابة، ولكنه أكثر وضوحًا ويترك مساحة أقل للأخطاء. الموازنة مع قيمة فارغة null وغير مُعرّفة undefined دعنا نرى المزيد من الحالات الخاصة. تسلك عملية الموازنة سلوكًا غير منطقي عند موازنة قيمة فارغة null أو غير مُعرّفة undefined مع قيم أخرى، إذ هاتان القيمتان من نوعين مختلفين. تذكر أنَّه للتحقق من المساواة بصرامة، استعمل المعامل === دومًا: alert( null === undefined ); // false للتحقق من المساواة دون أخذ النوع بالحسبان، استعمل المعامل ==. بمعنى أنَّ القيمتين null و undefined متساويتان فقط مع بعضهما ولكن ليستا متساويتان مع أي قيمة أخرى. alert( null == undefined ); // true الرياضيات ومعاملات الموازنة الأخرى ‎< > <= >=‎ القيمة الفارغة وغير المُعرّفة null و undefined يتم تحويلها إلى عدد: null تصبح 0، بينما undefined تصبح NaN. الآن دعنا نرى بعض الأشياء المضحكة التي تحدث عندما نطبق هذه القواعد، والأهم من ذلك هو كيفية تجنب هذا الفخ. نتيجة غريبة: null مقابل 0 دعنا نوازن null مع 0: alert( null > 0 ); // (1) false alert( null == 0 ); // (2) false alert( null >= 0 ); // (3) true رياضيًّا، هذا غريب، إذ تشير النتيجة الأخيرة إلى أنَّ «null أكبر من أو تساوي 0»، لذلك يجب في إحدى الموازنات أعلاه أن تكون صحيحة true، لكن كلاهما خطأ. السبب هو أنَّ التحقق من المساواة == ومعاملات الموازنة ‎> < >= <=‎ تعمل بشكل مختلف. تحول الموازنات القيمة الفارغة null إلى عدد، وتعاملها على أنها 0 ولهذا السبب، نتيجة الموازنة في المثال السابق في السطر (3) null >= 0 مُحقَّقة أي true وفي السطر (1) خطأ null > 0. من ناحية أخرى، يتم التحقق من المساواة == على undefinedوnull بدون أي تحويل، حيث أنه يساوي كل منهما الآخر ولا يساويها أي قيمة أخرى. لهذا السبب، نتيجة الموازنة في المثال السابق في السطر (2) null == 0 غير محقَّقة. القيمة غير المُعرّفة undefined غير قابلة للموازنة أي لا ينبغي موازنتها undefined مع القيم الأخرى: alert( undefined > 0 ); // false (1) alert( undefined < 0 ); // false (2) alert( undefined == 0 ); // false (3) لماذا يُكره الصفر كثيرًا؟ دائما خطأ! حصلنا على هذه النتائج لأن: معاملات الموازنة في السطر (1) و(2) في المثال السابق تُرجع false، لأن القيمة undefined تُحوّل إلى NaN وهي عبارة عن قيمة رقمية خاصة تعيد false لكافة الموازنات. تُرجع عملية التحقق من المساواة في السطر(3) القيمة false، لأن القيمة غير المُعرّفة undefined تساوي فقط القيمة null و undefined، ولا تساوي قيم أخرى. تجنب المشاكل لماذا ذكرنا هذه الأمثلة؟ هل يجب أن نتذكر هذه الخصائص في كل وقت؟ نعم، ولا فلا توجد إجابة دقيقة. على أي حال، ستصبح هذه الأشياء التي تراها صعبة ومعقدة تدريجيًا مألوفة وبديهية، ومع ذلك هناك طريقة جيدة للتهرب من مثل هذه الأمثلة: تعامل بالصورة المألوفة مع أي موازنة بها undefined/null، باستثناء المساواة الصارمة === فهي تحتاج معاملة استثنائية. لا تستخدم معاملات الموازنة ‎>= > < <=‎ مع متغير قد يأخذ إحدى القيمتين null/undefined، إلا إذا كنت متأكدًا حقًا مما تفعله. إذا كان المتغير عبارة عن هاتين القيمتين، فتحقق منها بشكل منفصل. الخلاصة تُرجع معاملات الموازنة قيمة منطقية. تُوازن السلاسل النصية حرفًا تلو الآخر حسب ترتيب القاموس (dictionary). عند موازنة قيم من أنواع بيانات مختلفة، يتم تحويلها إلى أرقام (باستثناء التحقق من المساواة الصارمة). القيم الفارغة null وغير المعرّفة undefined تساوي == بعضها بعضًا، ولا تساوي أي قيمة أخرى. كن حذرًا عند استخدام معاملات موازنة مثل> أو < مع متغيرات يمكن أن تكون فارغة null أو غير معرّفة undefined ويفضل التحقق من ذلك بشكل منفصل. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } تمارين الموازنات الأهمية: 5 ماذا ستكون نتيجة هذه الموازنات 5 > 4 "apple" > "pineapple" "2" > "12" undefined == null undefined === null null == "\n0\n" null === +"\n0\n" الحل 5 > 4 ← true "apple" > "pineapple"‏ ← false "2" > "12" ← true undefined == null‏ ← true undefined === null‏ ← false null == "\n0\n"‏ ← false null === +"\n0\n"‏ ← false بعض الأسباب: نتيجة الموازنة واضحة، صحيحة. حسب موازنة القاموس، النتيجة خاطئة. حسب موازنة القاموس مرة أُخرى، الحرف الأول من القيمة"2" أكبر من الحرف الأول من القيمة"1". القيم null و undefined تساويان بعضهما فقط. المساواة الصارمة صارمة فيما يخص نوع المتغير، وعند اختلاف نوع طرفي المعامل فإن النتيجة تصبح خاطئة. انظر للنقطة رقم (4). مساواة صارمة لنوعان مختلفان من القيم. ترجمة -وبتصرف- للفصل comparisons من كتاب The JavaScript Language اقرأ أيضًا المقال التالي: الدوال التفاعلية: alert ،prompt ،confirm المقال السابق: المعاملات في JavaScript كامل مقالات دليل تعلم جافاسكربت
  12. تعرَّفنا على العديد من المعاملات الحسابية في المدرسة، مثل الجمع +، الضرب *، الطرح – وما إلى ذلك. في هذا المقال، سنشرح معاملات أخرى لم نتعرف عليها في المدرسة بالإضافة إلى تلك المعاملات. مصطلحات جديدة: أحادي، ثنائي، عامل قبل أن نمضي قدمًا في هذا المقال، دعنا نتعرَّف على بعض المصطلحات الجديدة. العوامل (operands): هي العناصر (القيمة) التي تُنفَّذ عملية المعاملات (operators) عليها. على سبيل المثال، في عملية الضرب 5 * 2، يوجد عاملان؛ العامل الأيسر هو 2 والأيمن هو 5. يطلق عليها البعض أحيانًا الاسم «وسائط» (arguments) بدلًا من «عوامل» (operands). المعامل الأحادي: يكون المعامل أحاديًّا (unary) إذا كان لديه عامل (operand) واحد فقط. على سبيل المثال، عملية النفي هي عملية أحادية، إذ تعكس اشارة العدد، فيصبح سالبًا - إذا كان موجبًا + والعكس صحيح: let x = 1; x = -x; alert( x ); // -1, تم تطبيق النفي الأحادي المعامل الثنائي: يكون المعامل ثنائيًّا (binary) إذا كان لديه عاملان (operands). يوضح المثال التالي إشارة السالب التي في المثال السابق عندما تكون في المعامل الثنائي: let x = 1, y = 3; alert( y - x ); // 2, معامل الطرح الثنائي يطرح القيم تقنيًّا، نتحدث هنا عن نوعين من المعاملات لإشارة واحدة - هما: النفي الأحادي (العامل الفردي: عكس الإشارة) والطرح الثنائي (عاملان: عملية طرح)، لذا يجب التفريق بينهما. وصل سلاسل نصية عبر المعامل + الآن، سنتعرَّف على ميِّزات خاصة بمعاملات JavaScript تتجاوز تلك التي تعلمناها في المدرسة. المتعارف عليه أنَّ معامل الجمع + يجمع الأرقام. لكن إذا تم تطبيق معامل الجمع + على سلاسل نصية، فإنه يُوصل هذه السلاسل النصية مع بعضها بعضًا ويضعها في سلسلة نصية واحدة. let s = "my" + "string"; alert(s); // mystring لاحظ أنه يتم تحويل العدد إلى سلسلة نصية عن طريق وضعه داخل علامات الاقتباس ("") ويعامل آنذاك معاملة السلسلة النصية. ولاحظ أيضًا إذا كان أحد العاملين (operands) عبارة عن سلسلة نصية، فسيُحوَّل الآخر تلقائيًا إلى سلسلة نصية. إليك المثال التالي: alert( '1' + 2 ); // "12" alert( 2 + '1' ); // "21" كما رأيت، لا يهم ما إذا كان العامل الأول عبارة عن سلسلة نصية أم الثاني. القاعدة بسيطة: إذا كان أحد العاملين عبارة عن سلسلة نصية، فسيُحوَّل الآخر إلى سلسلة نصية أيضًا. لكن انتبه! المعاملات تبدأ من اليسار إلى اليمين. فإذا كان هناك عددان متبوعان بسلسلة نصية، فستُجمَع الأعداد قبل تحويلها إلى سلسلة نصية: alert(2 + 2 + '1' ); // "أي أن الناتج "41" وليس "221 خاصية وصل السلاسل النصية وجمعها في سلسلة واحدة (String concatenation) ميزة خاصة بمعامل الجمع +، بينما تعمل المعاملات الحسابية الأخرى مع الأعداد فقط. فعلى سبيل المثال، الطرح والقسمة: alert( 2 - '1' ); // 1 alert( '6' / '2' ); // 3 خاصية التحويل العددي يوجد لمعامل الجمع + صيغتان: الأول معامل الجمع الثنائي كما في خاصية دمج السلاسل النصية، والثاني معامل الجمع الأحادي كما في هذه الخاصية. معامل الجمع الأحادي أو بمعنى آخر، معامل الجمع + المطبق على قيمة واحدة، لا يُحدث تغييرًا على الأعداد. ولكن إذا لم يكن العامل عددًا، فإن عملية الجمع الأحادي تحوّله إلى عدد. اطلع على المثال التالي: // لا تأثير على الأعداد let x = 1; alert( +x ); // 1 let y = -2; alert( +y ); // -2 // تحويل غير الأعداد alert( +true ); // 1 alert( +"" ); // 0 يقوم هذا المعامل هنا مقام الدالة Number‎، لكنه أقصر وأبسط. تنشأ الحاجة لتحويل السلاسل النصية إلى أعداد في كثير من الأحيان. على سبيل المثال، إذا كنا نحصل على القيم من قالب HTML، فهي غالبًا ما تكون عبارة عن سلاسل نصية. ماذا لو أردنا دمجها؟ معامل الجمع الثنائي سيضيفها كسلاسل نصية: let apples = "2"; let oranges = "3"; alert( apples + oranges ); // "23", معامل الجمع الثنائي يدمج السلاسل النصية إذا كنا نريد أن نعاملها كأعداد، نحتاج إلى تحويلها ثم دمجها: let apples = "2"; let oranges = "3"; // كلا القيمتين حُولتا إلى أعداد قبل تطبيق معامل الجمع الثنائي alert( +apples + +oranges ); // 5 // بصيغة أطول // alert( Number(apples) + Number(oranges) ); // 5 من وجهة نظر عالم رياضيات، تبدو كثرة معاملات الجمع غريبة. ولكن من وجهة نظر المبرمج، لا يوجد شيء مميز: يتم تطبيق معاملات الجمع الأحادي أولاً لتحويل السلسلة النصية إلى عدد، ثم يجمع معامل الجمع الثنائي العددين الناتجين. لماذا يتم تطبيق معاملات الجمع الأحادية على القيم قبل الثنائية؟ هذا يعود للأولوية الأعلى في التنفيذ كما سنرى بعد قليل. ترتيب أولوية المعاملات إذا كان في المعادلة أكثر من معامل واحد، يتم تحديد ترتيب التنفيذ حسب أولوية هذه المعاملات. في المدرسة، تعلمنا جميعًا أنه يجب حساب الضرب في المعادلة1 + 2 * 2 قبل الجمع، هذا تحديدًا ما يسمى أولوية العملية الرياضية. أي أن عملية الضرب لها أولوية أعلى من الجمع. تتخطى الأقواس أولوية أي معامل، لذلك إذا لم نكن راضين عن أولوية المعاملات (operations) في المعادلة، يمكننا استخدام الأقواس لتغييرها. على سبيل المثال: (1 + 2) * 2. هناك العديد من المعاملات في JavaScript. كل معامل لديه رقم في ترتيب الأولوية، والمعامل صاحب الأولوية الأعلى يُنفَّذ أولًا. إذا كانت الأولوية لمجموعة معاملات في نفس الترتيب، يكون التنفيذ من اليسار إلى اليمين. إليك مقتطف من جدول الأولوية (لست بحاجة إلى حفظ ذلك، لكن لاحظ أن المعاملات الأحادية أعلى أولوية من المعاملات الثنائية المقابلة). الأولوية اسم المعامل إشارة المعامل ... ... ... 16 الجمع الأحادي + 16 النفي الأحادي - 14 الضرب * 14 القسمة / 13 الجمع الثنائي + 13 الطرح الثنائي - ... ... ... 3 الإسناد = ... ... ... كما ترى، معامل الجمع الأحادي له الأولوية 16 وهي أعلى من 13 لمعامل الجمع الثنائي. لهذا السبب، يُنفَّذ معامل الجمع الأحادي أولًا في المعادلة +apples + +oranges الذي يمثِّل عملية التحويل قبل عملية الجمع وهذا هو المطلوب. معامل الإسناد = لاحظ أنَّ معامل الإسناد = مدرجٌ في جدول الأولوية الخاص بالمعاملات مع أولوية منخفضة للغاية وهي 3. لهذا السبب، عندما نسند قيمة لمتغير، مثل x = 2 * 2 + 1، تُنفَّذ العمليات الحسابية حسب أولويتها ثمَّ تُسنَد القيمة الناتجة (5) إلى المتغيِّر x وتُخزَّن فيه. let x = 2 * 2 + 1; alert( x ); // 5 من الممكن سَلسَلة معاملات الإسناد: let a, b, c; a = b = c = 2 + 2; alert( a ); // 4 alert( b ); // 4 alert( c ); // 4 يتم تنفيذ سَلسَلة معاملات الإسناد من اليمين إلى اليسار. أولاً، يُقيَّم التعبير 2 + 2 الموجود في أقصى اليمين ثم تُسنَد القيمة الناتجة عنه إلى المتغيرات الموجودة على اليسار:c، وb و a. في النهاية، تشترك جميع تلك المتغيرات في القيمة النهائية الناتجة. معامل الإسناد = يُرجع قيمة المعامل دائما يرجع قيمة. هذا واضح في معامل الجمع + أو الضرب *، ويتبع معامل الإسناد = أيضًا هذه القاعدة. فالاستدعاء x = Value يُخزِّن القيمة في x ثم يرجعها. يوضح المثال التالي كيفية الاستفادة من هذه الميزة باستخدام القيمة الناتجة عن عملية الإسناد في معادلة أخرى أكثر تعقيدًا، وبذلك نضرب عصفورين بحجر واحد: let a = 1; let b = 2; let c = 3 - (a = b + 1); alert( a ); // 3 alert( c ); // 0 في المثال أعلاه، تكون نتيجة a = b + 1 هي القيمة التي جرى إسنادها إلىa أي 3، ثم يتم طرح من هذه القيمة 3. شيفرة مضحكة، أليس كذلك؟! لكن يجب أن نفهم كيف تعمل لأنه في بعض الأحيان نراها في مكتبات خارجية كتبها آخرون، ولا يُفضل إطلاقًا أن نكتب مثل هذه الشيفرات، لأنَّ هذه الحيل لا تجعل من الشيفرة أكثر وضوحًا أو أكثر قابلية للقراءة. معامل باقي القسمة % معامل باقي القسمة %، لا يرتبط بالنسب المئوية. الناتج من هذه المعادلة a % b هو العدد المتبقي من قسمة العدد الصحيح a على العدد الصحيحb. إليك مثال عنه: alert( 5 % 2 ); // 1 (هو الباقي من قسمة 5 على 2) alert( 8 % 3 ); // 2 (هو الباقي من قسمة 8 على 3) alert( 6 % 3 ); // 0 (هو الباقي من قسمة 6 على 3) معامل القوة ** معامل القوة ** هو إضافة حديثة لـ JavaScript. ليكن لدينا الرقم الطبيعي b، فيكون ناتج العملية a ** b هو الرقم a مضروبًا في نفسه عدد b من المرات: alert( 2 ** 2 ); // 4 (2 * 2) alert( 2 ** 3 ); // 8 (2 * 2 * 2) alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2) يعمل معامل القوة مع الأعداد غير الصحيحة أيضًا مثل: alert( 4 ** (1/2) ); // 2 (معامل القوة ½ هو نفس الجذر التربيعي) alert( 8 ** (1/3) ); // 2 (معامل القوة ⅓ هو نفس الجذر التكعيبي) معاملات الزيادة ++ والنقصان -- تُعدُّ الزيادة على العدد أو إنقاصه بمقدار 1 من أكثر العمليات العددية شيوعًا. لذلك، هناك معاملات خاصة لهذا الغرض: معامل الزيادة ++ يزيد على قيمة المتغير مقدار 1: let counter = 2; counter++; // ببساطة counter = counter + 1 هو اختصار للعملية alert( counter ); // 3 معامل النقصان -- ينقص من قيمة المتغير مقدار 1: let counter = 2; counter--; // counter = counter - 1 هو اختصار للعملية alert( counter ); // 1 تنبيه: معاملات الزيادة / النقصان خاصة بالمتغيرات فقط، ومحاولة تطبيقها على قيم عددية مثل ‎5++‎ سيعطي خطأ. يمكن وضع هذين المعاملين ++، -- قبل المتغير أو بعده. عندما يتبع المعامل المتغير، هذا ما يسمى «النموذج اللاحق» (postfix form) مثل counter++. وبالمثل، «النموذج السابق» (prefix form)، عندما يأتي المعامل قبل المتغير مثل ++counter. كلا النموذجين يقومان بالعمل نفسه وهو الزيادة على قيمة المتغير counter بمقدار 1. إذن، هل هناك فرق؟ نعم، ولكن لا يمكننا رؤيته إلا إذا استخدمنا القيمة المعادة من العملية تلك. دعني أوضح لك أكثر. كما نعلم، جميع المعاملات ترجع قيمة وكذلك أيضًا معاملات الزيادة/النقصان. يعيد النموذج السابق القيمة الجديدة بينما يعيد النموذج اللاحق القيمة القديمة (قبل الزيادة / النقصان). إليك مثال على ذلك ليتضح كل الفرق وضوح الشمس: let counter = 1; let a = ++counter; // (*) alert(a); // 2 في السطر المُميّز بالنجمة (*)، يزيد النموذج السابق ++counter قيمة المتغير counter ويعيد القيمة الجديدة الناتجة وهي 2. لذا، تُظهر الدالة alert القيمة 2. الآن، دعني استخدم النموذج اللاحق بدلًا من النموذج السابق لمعرفة الفرق: let counter = 1; let a = counter++; // (*) alert(a); // 1 في السطر المُميّز بالنجمة (*)، يزيد النموذج اللاحق counter++ قيمة المتغير counter لكنه يعيد القيمة القديمة (قبل الزيادة). لذا، تُظهِر الدالة alert القيمة 1. مختصر القول: إذا لم يتم استخدام القيمة الناتجة من معاملي الزيادة والانقاص، فلا يوجد فرق في النموذج الذي يتم استخدامه سواءً سابق أو لاحق: let counter = 0; counter++; ++counter; alert( counter ); // 2, كلا السطرين يؤديان نفس الغرض أمَّا إذا كنت ترغب في زيادة قيمة المتغير واستخدامها مباشرةً، فأنت بحاجة إلى استعمال النموذج السابق: let counter = 0; alert( ++counter ); // 1 وإذا كنت ترغب في زيادة قيمة المتغير ولكنك تريد استخدام قيمته السابقة، فاستعمل النموذج اللاحق: let counter = 0; alert( counter++ ); // 0 معاملات زيادة / نقصان داخل معاملات اخرى: يمكن استخدام معاملات زيادة / نقصان (++ / --) داخل المعادلات أيضًا، وأولويتها في التنفيذ أعلى من معظم المعاملات الحسابية الأخرى. إليك المثال التالي: let counter = 1; alert( 2 * ++counter ); // 4 وازنه مع المثال التالي: let counter = 1; alert( 2 * counter++ ); // 2, لأن counter++ يرجع القيمة القديمة رغم أنَّه مقبول من الناحية التقنية، إلا أنَّ هذه الشيفرة عادة ما تجعل الشيفرة الكلية صعبة القراءة. اتباع نموذج «سطر واحد يفعل أشياء متعددة» ليس جيدًا ويولد شيفرة صعبة القراءة والتنقيح. أثناء تَصفُّح الشيفرة سريعًا يمكن أن لا تنتبه على شيء مثل counter++، أي لن تلاحظ أنَّ قيمة المتغير قد زادت. فتخيل ما سيحصل إن حدث خطأ ما مع وجود مئات مثل تلك الأسطر التي تنجز عدة أشياء سويةً! أنصحك باستخدام أسلوب «سطر واحد - إجراء واحد» (one line – one action): let counter = 1; alert( 2 * counter ); counter++; المعاملات الثنائية تتعامل المعاملات الثنائية (Bitwise operators) مع العوامل كأعداد صحيحة بحجم 32 بت وتعمل على مستوى تمثيلها الثنائي (binary representation). هذه العوامل ليست خاصة بـ JavaScript، وهي مدعومة في معظم لغات البرمجة. إليك قائمة بالمعاملات الثنائية (Bitwise operators): المعامل AND (&) المعامل OR (|) المعامل XOR (^) المعامل NOT (~) معامل الإزاحة نحو اليسار LEFT SHIFT (>>) معامل الإزاحة نحو اليمين RIGHT SHIFT (<<) معامل الإزاحة نحو اليمين مع إدخال أصفار ZERO-FILL RIGHT SHIFT (<<<) نادرًا ما تحتاج إلى استخدام مثل هذه المعاملات. وتحتاج لفهمها إلى الخوض في التمثيل الثنائي للأعداد (الصيغة الثنائية المقابلة للصيغة العشرية المفهومة للبشر) وليس من الجيد القيام بذلك الآن، خاصة أننا لسنا بحاجة إلى ذلك في هذا الوقت. إذا كنت فضولي يمكنك قراءة توثيق المعاملات الثنائية في JavaScript في موسوعة حسوب، وهذا كافيًّا الآن. وسنشرحها عمليًّا عندما تنشأ حاجة حقيقية لها في مراحل متقدمة. معاملات التعديل المباشر على المتغير (Modify-in-place) غالبًا ما نحتاج إلى تطبيق معامل على متغير ما وتخزين القيمة الجديدة في المتغير نفسه. انظر مثلًا إلى المثال التالي: let n = 2; n = n + 5; n = n * 2; يمكن اختصار الشيفرة السابقة باستخدام ‎+=‎ و ‎*=‎: let n = 2; n += 5; // (n = n + 5 وهذا يماثل) n = 7 أي تصبح n *= 2; // (n = n * 2 وهذا يماثل) n = 14 تصبح الآن alert( n ); // 14 معاملات التعديل والإسناد (modify-and-assign) متاحة لجميع المعاملات الحسابية والثنائية مثل: ‎/=‎ و ‎-=‎ وتملك هذه المعاملات الأولوية نفسها التي لدى معامل الإسناد العادي =، لذا فهي تُنفَّذ بعد معظم المعاملات الأخرى: let n = 2; n *= 3 + 5; alert( n ); // 16 (n *= 8 القسم الأيمن يُقيَّم أولًا وهذا يماثل) معامل الفاصلة , معامل فاصلة (Comma) , هو واحد من أندر المعاملات وأكثرها غرابة. في بعض الأحيان يتم استخدامه لكتابة شيفرة أقصر، لذلك نحن بحاجة إلى التعرف عليه لفهم ذلك. يتيح لنا معامل الفاصلة تقييم العديد من المعادلات عند فصلها بفاصلة ,. يجري تقييم كل قسم منها ولكن يتم إرجاع نتيجة آخر واحد فقط. تفحَّص المثال التالي: let a = (1 + 2, 3 + 4); alert( a ); // 7 (الناتج من جمع 3+4) هنا، يُقيَّم القسم الأول من المعادلة 1+2 ويُتجاهَل قيمته ثم بعد ذلك يُقيَّم القسم الثاني 3+4 وتعاد قيمته كنتيجة نهائية. معامل الفاصلة له أولوية منخفضة جدًا يرجى ملاحظة أن معامل الفاصلة له أولوية منخفضة للغاية، أقل من معامل الإسناد =، لذلك الأقواس مهمة في المثال أعلاه. بدون الأقواس: a = 1 + 2 ، 3 + 4 يُنفَّذ معامل الجمع + أولاً، فتصبح المعادلة a= 3,7، ثم يُنفَّذ معامل الإسناد = فتصبح a=3، وأخيرًا لا يُطبَّق أي معامل على العدد الموجود بعد الفاصلة، أي يُتجاهَل العدد7. لماذا نحتاج إلى معامل يتجاهل كل القيم باستثناء قيمة الجزء الأخير؟ في بعض الأحيان، يستخدمه المبرمجون في بنيات أكثر تعقيدًا لوضع العديد من الإجراءات في سطر واحد. قد تكون أبسط تلك البنيات المعقدة هي بنية الحلقية التكرارية التالية: // ثلاث معاملات في سطر واحد for (a = 1, b = 3, c = a * b; a < 10; a++) { ... } تُستخدَم هذه الحيل في العديد من أطر JavaScript. لكنها في العادة لا تحسّن قابلية القراءة للشيفرة، لذلك يجب عليك التفكير جيدًا قبل استخدامها. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } تمارين النموذج السابق والنموذج اللاحق الأهمية: 5 ما هي القيم النهائية للمتغيرات a ،b ،c ،d بعد تنفيذ الشيفرة التالية؟ let a = 1, b = 1; let c = ++a; // ? let d = b++; // ? الحل: الإجابة هي: a = 2 b = 2 c = 2 d = 1 let a = 1, b = 1; alert( ++a ); // 2, النموذج السابق يُرجع القيمة الجديدة alert( b++ ); // 1, النموذج اللاحق يُرجع القيمة القديمة alert( a ); // 2, تتم الزيادة مرة واحدة alert( b ); // 2, تتم الزيادة مرة واحدة نتيجة عملية الإسناد الأهمية: 3 ما هي قيم المتغيرين a و x بعد تنفيذ الشيفرة التالية؟ let a = 2; let x = 1 + (a *= 2); الحل الإجابة هي: a = 4 (مضروبًا في العدد 2) x = 5 (عبارة عن مجموع العدد 1 والعدد 4) ترجمة -وبتصرف- للفصل operators من كتاب The JavaScript Language انظر أيضًا المقال التالي: معاملات الموازنة المقال السابق: التحويل بين الأنواع كامل مقالات دليل تعلم جافاسكربت
  13. يمكن أن يحتوي المتغير على أي نوع من أنواع البيانات في JavaScript. أي من الممكن أن يكون متغير من نوع سلسلة نصية في وقت ما، ثم يتغير محتواه إلى قيمة عددية وهكذا دواليك. // لا يوجد أي خطأ let message = "hello"; message = 123456; تسمى اللغات البرمجية التي تسمح بتغيير نوع القيم المسندة إلى المتغير بلغات برمجة «ديناميكية النوع» (dynamically typed)، ومعنى ذلك أنه توجد أنواع للبيانات ولكن لا يتم ربط المتغير بنوع معين منها. هنالك سبعة أنواع أساسية في لغة JavaScript. سنذكرها الآن بشكل عام، وسنتحدث في الفصول القادمة عن كل نوع منها بالتفصيل. الأعداد let n=123; n=12.345; يمكن تمثيل الأعداد، بما فيها الأعداد الصحيحة (integers) والعشرية (floating point)، في JavaScript عبر النوع number. هنالك العديد من العمليات التي يمكن تنفيذها على المتغيرات العددية، مثل، الضرب *، والقسمة /، والجمع +، والطرح -، وغيرها من العمليات الرياضية. كما أن هناك «قيم عددية خاصة»، بالإضافة إلى الأعداد العادية، والتي تنتمي أيضًا إلى هذا النوع من البيانات مثل: Infinity، و ‎-Infinity، و NaN. وهاك شرحها: Infinity: وتمثل قيمة اللانهاية الرياضية، وهي قيمة خاصة أكبر من أي عدد موجب آخر. ومن الممكن أن نحصل عليها في عدة حالات منها قسمة عدد على الصفر: alert (1/0); // Infinity أو بالإمكان الإشارة إليها بشكل مباشر: alert (Infinity); // Infinity NaN: هذه القيمة ناتجة عن اختصارالعبارة "Not a Number" (ليس عددًا) وتمثل خطأً حسابيًّا، أو حالة عدم تعيين. وهي نتيجة عملية رياضية خاطئة أو غير معروفة. إليك المثال التالي: alert( "not a number"/2 ); // NaN, لا يمكن إجراء مثل عملية القسمة هذه وتتصف القيمة NaN بأنها «لاصقة» (sticky)، أي بمعنى عندما ُتنفَّذ أي عملية على NaN، فالقيمة الناتجة هي NaN أيضًا. alert ("not a number" / 2+5); // NaN لذلك عند وجود القيمة NaN في أي مكان من التعابير الرياضية، فستطغى على كامل النتيحة. وتنتمي القيم العددية الخاصة شكليًا فقط إلى نوع القيم العددية، لأنها في واقع الأمر لا تعبر عن أعداد بمفهومها الشائع. سنتكلم لاحقًا عن التعامل مع الأعداد في فصل (الأعداد). العمليات الرياضية «آمنة» ممارسة الرياضيات آمنة في JavaScript، وبإمكانك القيام بأي عملية حسابية، مثل: القسمة على صفر، والتعامل مع السلاسل الغير عددية كأعداد، وغيرها من العمليات. ثق تمامًا أن السكربت لن ينتهي بخطأ فادح (أو يتوقف عن العمل). أسوأ ما في الأمر أنك ستحصل على النتيجة NaN. السلاسل النصية (النصوص) تمثَّل النصوص (سنطلق عليها «سلاسل نصية» من الآن وصاعدًا) عبر النوع string. يجب أن تُحاط السلسلة النصية في JavaScript بإشارتي تنصيص. let str = "Hello"; let str2 = 'Single quotes are ok too'; let phrase = `can embed ${str}`; يوجد في لغة JavaScript ثلاثة أنواع من إشارات التنصيص وهي: المزدوجة: مثل "Hello". المفردة: مثل 'Hello'. المائلة: مثل Hello (إضافة الإشارة). إشارتي التنصيص المفرد والمزدوج هما عبارة عن إشارات تنصيص تتصف بأنها «بسيطة» ولا يوجد أي فرق بينها في JavaScript. أما إشارة التنصيص المائلة، فهي عبارة عن إشارات تنصيص لها «وظيفة إضافية»، إذ تسمح لك بإضافة متغيرات وتعابير إلى السلسلة النصية بعد إحاطتها بـ ‎${...}‎. فمثلًا: let name = "أحمد"; // تضمين متغير alert (`مرحبًا، ${name}!`); // مرحبًا، أحمد! // تضمين تعبير alert (`the result is ${1+2}`); // النتيجة هي 3 يُحسب التعبير داخل ‎${...}‎ وتصبح نتيجته جزءًا من السلسلة النصية. بإمكاننا وضع أي شيء هناك مثل: المتغير name أو التعبير الرياضي 1 + 2 أو حتى تعابير برمجية أخرى أكثر تعقيدًا. ولكن أبقِ في ذهنك أن هذا مقتصر فقط على إشارات التنصيص المائلة. ولا تستطيع إشارات التنصيص الأخرى القيام بمثل هذا العمل! alert (" the result is ${1+2}" ); // ${1+2} النتيجة هي // فلا تفعل إشارات التنصيص المزدوجة أي شيء إذا كنت تشعر بصعوبة الأمر، لا تقلق. سنتحدث عنه لاحقًا بشكل موسع في فصل السلاسل النصية. المحارف ليس لها نوع مخصص للأسف يوجد في بعض اللغات البرمجية نوعًا خاصًّا من البيانات للمحرف المفرد (character) ويسمى char في لغة C و Java مثلًا. لا يوجد مثل هذا النوع في JavaScript. ويوجد فقط نوع واحد وهو string (سلسلة نصية) الذي من الممكن أن يحتوي على محرف واحد أو أكثر. النوع المنطقي (البولياني) يأخذ النوع boolean إحدى القيمتين: true (صح) أو false (خطأ) وتدعى هاتان القيمتان بالقيم المنطقية. من الشائع استخدام هذا النوع لحفظ البيانات التي لها القيم (نعم / لا): true تعني "نعم، صحيح"، و false تعني "لا، خطأ". إليك المثال التالي: let nameFieldChecked = true; // name نعم، جرى تحديد الحقل let ageFieldChecked = false; // غير محدَّد age لا، الحقل أضف إلى ذلك أن القيم المنطقية تعبِّر عن ناتج عمليات الموازنة: let isGraeter = 4>1; alert(isGreater); // true إذ نتيجة الموازنة محقَّقة أي القيمة الخالية: null لا تنتمي القيمة الخالية null إلى أي نوع من أنواع البيانات المذكورة سابقًا ولكن لها نوع خاص. يملك هذا النوع الخاص قيمة واحدة هي null: let age = null; في لغة JavaScript، لا يعبِّر النوع null عن مرجع لكائن غير موجود أو مؤشر خالي كما في لغات برمجية أخرى. ولكنه عبارة عن قيمة خاصة تمثِّل "لا شيء"، أو "فارغ"، أو "قيمة غير معلومة". فمثلًا، الشيفرة البرمجية السابقة تعدُّ المتغير age غير معلوم أو فارغ لسبب ما. القيمة الغير معرفة: undefined يمكن تمييز نوع آخر أيضًا للبيانات وهو «غير مُعرَّف» الذي يمثله النوع undefined. ويشكل هذا النوع نوعًا خاصًّا قائمًا بنفسه تمامًا مثل النوع null. أما معنى «غير معرّف» أي أنه لم تُسنَد أية قيمة للمتغير بعد: let x; alert (x); // "undefined" تُعرَض القيمة تقنيًا، من الممكن إسناد القيمة undefined لأي متغير: let x=123; x = undefined; alert(x); // "undefined" ولكن لا أنصحك بالقيام بذلك. نستخدم عادةً القيمة null لإسناد القيمة الخالية أو غير المعروفة لمتغير، ونستخدم undefined للتحقق من أن إذا كان للمتغير قيمة أو لا. الكائنات والرموز النوع object (كائن) هو نوعٌ خاصٌّ. تسمى جميع الأنواع السابقة بالأنواع «الأساسية» (primitive) لأن قيمها تحتوي على شيء واحد فقط (سلسلة نصية أو عدد أو أي شيء آخر). ولكن تُستخدَم الكائنات لتخزين مجموعة من البيانات والكيانات (entity) الأكثر تعقيدًا. سنتعامل معها في فصل الكائنات بعد الانتهاء من دراسة الأنواع الأساسية. يستخدم النوع symbol (رمز) لتشكيل معرّف (identifier) مُميَّز للكائنات. يُفضَّل دراسة هذا النوع بعد الانتهاء من الكائنات ولكن ذكره هنا ضروري لاستكمال جميع أنواع البيانات. المعامل typeof يُحدِّد المعامل typeof نوع الشيء المُمرَّر إليه. ويكون مفيدًا عندما تحتاج إلى معالجة القيم بطرق مختلفة حسب نوعها، أو فقط القيام بفحص سريع لنوع البيانات. ويمكن كتابة الصياغة بطريقتين: معامل: typeof x. تابع: typeof(x)‎. بمعنى آخر، من الممكن استخدامه مع أو بدون الأقواس، وتكون النتيجة واحدة. نتيجة استدعاء typeof x هو سلسلة نصية تمثل اسم النوع: typeof undefined // "undefined" typeof 0 // "number" typeof true // "boolean" typeof "foo" // "string" typeof Symbol("id") // "symbol" typeof Math // "object" (1) typeof null // "object" (2) typeof alert // "function" (3) قد تحتاج لتوضيح نتائج الأسطر الثلاث الأخيرة: Math هو كائن مضمّن في JavaScript ويوفر العمليات الرياضية. سنتحدث عنه لاحقًا في فصل الأعداد، وذكرناه هنا فقط كمثال عن نوع الكائن. نتيجة typeof null هي "object" أي كائن. أليس هذا خطأ؟! بالتأكيد. وقد تم الاعتراف بذلك رسميًا، ولكن بقيت هذه النتيجة من أجل المحافظة على التوافقية. القيمة "null" ليست كائنًا بل هي قيمة لنوع منفصل بحد ذاته. لذلك، مرةً أخرى، نؤكد أنّ هذا خطأ من اللغة نفسها. نتيجة typeof alert هي "function" (دالة)، لأن alert هو دالة في لغة JavaScript. سنتحدث لاحقًا عن التوابع في الفصول القادمة وستلاحظ أنه لا يوجد نوع خاص اسمه "function" في لغة JavaScript. تنتمي التوابع إلى نوع الكائنات ولكن لها معاملة خاصة في typeof. شكليًا، هذا خطأ، ولكنه مفيد جدًا عند التطبيق العملي. الخلاصة هناك سبعة أنواع أساسية من أنواع البيانات في JavaScript هي: number: يمثِّل الأعداد بكل أنوعها: الصحيحة والعشرية. string: يمثِّل السلاسل النصية. ويمكن أن تحتوي السلسلة النصية على محرف واحد أو أكثر، ولكن لا يوجد نوع خاص من أجل المحرف الواحد. boolean: يمثِّل القيم المنطقية (صح / خطأ). null: يمثِّل القيم الخاوية. وهو نوع خاص يملك قيمة وحيدة وهي القيمة null. undefined: قيمة غير معرَّفة تكون قيمةً للمتغيرات التي لم تسند قيمة محدَّدة لها بعد. وهو نوع خاص يملك قيمة وحيدة وهي undefined. object: كائن من أجل بنى البيانات الأكثر تعقيدًا. symbol: رمز للمعرفات الخاصة. ويسمح المعامل typeof بمعرفة أنوع القيم المخزنة في المتغيرات: له شكلين: typeof x أو typeof(x)‎. يعيد سلسلة نصية تحوي اسم النوع، مثل "string". من أجل null يعطي القيمة "object" وهذا خطأ في اللغة، فالقيمة الخالية ليست من نوع الكائن. سنركز في الفصل القادم على القيم الأساسية ثم ننتقل إلى الحديث عن الكائنات. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } تمارين 1. إشارات تنصيص السلاسل النصية الأهمية: 5 ما هو خرج هذا السكربت؟ let name " Ilya"; alert (`hello ${1}`); //? alert (`hello ${"name"}`); //? alert (`hello ${name}`); //? الحل تُضمن الفاصلة العليا الخلفية التعابير الموجودة داخل ‎${…}‎ في السلسلة النصية: let name " Ilya"; // قيمة التعبير هو 1 alert (`hello ${1}`); // hello1 // "name" قيمة التعبير هي السلسلة النصية alert (`hello ${"name"}`); // hello name // name قيمة التعبير هو ما ضُمِّن بالمتغير alert (`hello ${name}`); // hello Ilya ترجمة -وبتصرف- للفصل Data types من كتاب The JavaScript Language انظر أيضًا المقال التالي: التحويل بين الأنواع المقال السابق: المتغيرات كامل مقالات دليل تعلم جافاسكربت
  14. تُحوِّل الدوال والمعاملات، في أغلب الأحيان، القيم التي تصلها إلى النوع الذي يناسبها وهذا ما يسمى «تحويل النوع». تُحوِّل الدالة alert مثلًا أي قيمة إلى سلسلة نصية تلقائيًا لإظهارها، وتُحوِّل والعمليات الرياضية القيم التي تستعملها إلى أعداد. كما توجد العديد من الحالات التي نكون فيها بحاجة لتحويل قيمة بصريح العبارة إلى النوع المطلوب. لاحظ أنَّ هذا الفصل لا يتحدث عن الكائنات. سندرس الأنواع الأساسية أولًا، وبعدها ننتقل إلى الكائنات، ثم سنتعلم طريقة تحويل الكائن في فصل التحويل من كائن إلى أساسي. التحويل إلى سلسلة نصية يحدث التحويل إلى سلسلة نصية عندما نحتاج إلى الشكل النصي لقيمة ما. مثلًا، تُحوِّل الدالة alert(value)‎ القيمة value المُمرَّرة إليها إلى سلسلة نصية لإظهارها. ويمكن أيضًا استدعاء الدالة String(value)‎ لتحويل أي قيمة value إلى سلسلة نصية: let value=true; alert(typeof value); // Boolean value = String(value); // "true" أصبحت الآن سلسلة نصية قيمتها alert(tyoeof value); // string يكون ناتج التحويل إلى سلسلة نصية عادةً واضحًا، فالقيمة المنطقية false تصبح "false"، والقيمة الخالية null تصبح "null" وهكذا. التحويل إلى عدد يحدث التحويل العددي في الدوال والتعابير الرياضية تلقائيًا. كما في حالة تطبيق عملية القسمة / على قيمتين غير عدديتين: alert("6"/"2"); // الناتج: 3، إذ تحول السلاسل النصية إلى أعداد بإمكاننا استخدام الدالة ‎Number(value)‎ لتحويل القيمة value المُمرَّرة إليها بشكل صريح إلى عدد: let str = "123"; alert(typeof str); // string let num = Number(str); // 123 يتحول إلى العدد alert(typeof num); // number يكون التحويل الصريح ضروريًّا عندما نقرأ القيمة من مصدر نصي مثل العنصر text في النموذج <form> ويكون المطلوب إدخال عدد. ويكون ناتج التحويل NaN (اختصارًا للعبارة Not a Number)، إذا كان من غير الممكن تشكيل عدد من السلسلة النصية. إليك مثلًا: let age = Number("an arbitrary string instead of a number"); alert(age); // NaN, فشلت عملية التحويل table { width: 100%; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; } tr:nth-child(even) { background-color: #dddddd; } .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } code { background-color: rgb(250, 250, 250); border-radius: 3px; } قواعد التحويل العددي: القيمة ناتج التحويل Undefined NaN null 0 false و true 0 و 1 string (سلسلة نصية) يتم إزالة المسافات الفارغة من البداية والنهاية. إذا كانت السلسلة النصية المتبقية فارغة، تكون النتيجة 0، أو تتم قراءة العدد من السلسلة النصية. والخطأ يعطي النتيجة NaN. أمثلة: alert (Number(" 123 ")); // 123 alert(Number("123z")); // NaN (error reading a number at "z") alert(Number(true)); // 1 alert(Number(false)); //0 لاحظ أنَّ القيمتين null و undefined لهما خرج مختلف هنا: قيمة null تصبح 0، بينما undefined تصبح NaN. جمع السلاسل النصية عبر المعامل + تُحوَّل أغلب العمليات الرياضية القيم إلى أعداد. ولكن هناك استثناء وحيد يحدث عن الجمع عبر المعامل "+" وكون إحدى القيم المطلوب جمعها سلسلة نصية. يجري آنذاك تحويل القيمة الأخرى إلى سلسلة نصية أيضًا ثمَّ تُضاف إلى القيمة الآخرى وتُجمعان معًا في سلسلة نصية واحدة. إليك المثال التالي الذي يوضح ما سبق: alert(1+'2'); // '12' (سلسلة نصية على اليمين) alert('1'+2); // '12' (سلسلة نصية على اليسار) يحدث هذا فقط عندما يكون أحد المعاملات على الأقل سلسلة نصية. أمَّا في الحالات الأخرى، تُحوَّل القيم إلى أعداد. التحويل إلى قيمة منطقية التحويل المنطقي وهو الأسهل من بين جميع عمليات التحويل. يحدث في العمليات المنطقية (لاحقًا سنتعرف على التحقق من الشروط وغيرها من العمليات المماثلة)، ولكن من الممكن تطبيقه أيضًا بشكل صريح عن طريق استدعاء Boolean(value)‎. قاعدة التحويل: القيم التي تكون فارغة، مثل 0، والسلسلة النصية الفارغة، والقيمة undefined، والقيمة NaN تُحوَّل جميعها إلى false. أما ما تبقى، فيُحوَّل إلى القيمة true. مثلًا: alert(Boolean(1)); // true alert(Boolean(0)); // false alert(Boolean("hello")); // true alert(Boolean ("")); // false لاحظ أنَّ السلسلة العددية التي تحتوي على 0 تصبح true. ولكن هناك بعض اللغات البرمجية (مثل PHP) تحول "0" إلى القيمة false. ولكن في لغة JavaScript، تُحوَّل السلسلة النصية الغير فارغة دائمًا إلى القيمة true: alert(Boolean("0"); // true alert(Boolean(" "); // true تحول المسافات (وأي سلسلة نصية غير فارغة) إلى الخلاصة التحويلات الثلاث الأكثر انتشارًا للأنواع هي التحويل إلى سلسلة نصية، أو عدد، أو قيمة منطقية. التحويل إلى سلسلة نصية: يحدث عندما نريد إظهار خرج معين ويمكن تنفيذه عن طريق String(value)‎. ويكون خرج التحويل إلى سلسلة نصية واضحًا بالنسبة للأنواع الأساسية. التحويل إلى عدد: يحدث في العمليات الرياضية ومن الممكن تنفيذه باستخدام Number(value)‎. ويتبع هذا التحويل القواعد التالية: القيمة ناتج التحويل Undefined NaN null 0 false و true 0 و 1 string يتم قراءة السلسلة النصية كما هي، وتجاهل الفراغات من البداية والنهاية. السلسلة النصية الفارغة تصبح 0. وعند وجود خطأ في نتيجة التحويل تكون النتيجة NaN. التحويل إلى قيم منطقية: يحدث في العمليات المنطقية ويمكن تنفيذه باستخدام Boolean(value)‎. ويتبع هذا التحويل القواعد التالية: القيمة ناتج التحويل 0, null, undefined, NaN, "" false القيم الأخرى true معظم هذه القواعد سهلة الفهم والحفظ. ولكن هناك بعض الاستثناءات أو الأخطاء الشائعة مثل: القيمة undefined كعدد هي NaN وليست 0. "0" والسلاسل النصية المحتوي على فراغات فقط مثل " " تُحوَّل إلى القيمة المنطقية true. تذكر أننا لم ندرس الكائنات بعد. سنعود إليها لاحقًا في فصل التحويل من الكائنات إلى الأنواع الأساسية المخصص للكائنات ولكن بعد تعلم المزيد من مبادئ JavaScript. تمارين 1. تحويلات الأنواع الأهمية: 5 ما هي نتيجة التعابير التالية؟ ""+1+0 ""-1+0 true+false 6/"3" "2"*"3" 4+5+"px" "$"+4+5 "4"-2 "4px"-2 7/0 " -9 "+5 " -9 "-5 null+1 undefined+1 فكر جيدًا، ثم اكتب حلك وقارنه مع الإجابة. الحل: الجمع مع سلسلة نصية مثل ‎""+ 1 يحول العدد 1 إلى سلسلة نصية: ‎""+1="1"‎، ثم لدينا "1"+0 التي تنطبق عليها القاعدة نفسها. الطرح - (مثل معظم العمليات الرياضية) يعمل فقط مع القيم العددية، وبالتالي يحول السلسلة النصية الفارغة "" إلى 0. الجمع مع سلسلة نصية يضم العدد 5 إلى السلسلة النصية. تحول عملية الطرح دائمًا القيم إلى أعداد. لذلك يحول القيمة " ‎-9 " إلى ‎-9 ( ويتجاهل الفراغات حولها). تصبح قيمة null هي 0 بعد تحويلها إلى عدد. القيمة الغير معرفة undefined تصبح NaN بعد تحويلها إلى عدد. ""+1+0 ="10" // (1) ""-1+0 = -1 //(2) true+false 6/"3" =2 "2"*"3" = 6 4+5+"px" = "9px" "$"+4+5 = "$45" "4"-2 = 2 "4px"-2 = NaN 7/0 = Infinity " -9 "+5 = " -9 5" //(3) " -9 "-5 = -14 //(4) null+1 = 1 //(5) undefined+1 = NaN //(6) ترجمة -وبتصرف- للفصل Type conversions من كتاب The JavaScript Language انظر أيضًا المقال التالي: المعاملات في JavaScript المقال السابق: أنواع البيانات كامل مقالات دليل تعلم جافاسكربت
  15. سوف تحتاج، بالتأكيد، في مرحلة ما من عملك على تطوير التطبيقات في لغة JavaScript للتعامل مع المعلومات. فمثلًا، بإمكانك تخيل العمل على التطبيقين التاليين: متجر إلكتروني – يمكن أن تتضمن المعلومات البضائع التي تباع وسلة التسوق. تطبيق محادثة فورية – يمكن أن تتضمن المعلومات، والمستخدمين، والرسائل وغيرها. إن أردنا تخزين هذه المعلومات، نحتاج إلى شيء يُخزِّنها ويحفظها لنا، وهذا ما تمثله المتغيرات تمامًا. فمثل المتغيرات كمثل الأوعية والآنية التي تحفظ وتُخزِّن كل ما يوضع فيها. المتغير المتغير هو «مَخزَن مُسمَى» (named storage) للبيانات، وهذا المخزن يقع في الذاكرة. بإمكانك استخدام المتغيرات لتخزين البضائع، والزوار وغيرها من البيانات. إن أردت إنشاء متغير جديد في لغة JavaScript، استخدم الكلمة المفتاحية let. تنشئ العبارة البرمجية التالية (أو بمعنى آخر تصرّح عن، أو تُعرِّف) متغيرًا جديدًا باسم "message": let message; الآن، نستطيع وضع بعض البيانات فيه باستخدام معامل الإسناد =: let message; message='Hello'; // 'Hello' خزِّن السلسلة النصية بعد تنفيذ هذه الشيفرة، تُحفَظ السلسلة النصية السابقة في منطقة من الذاكرة مرتبطة بهذا المتغير. ويمكننا الوصول إليها باستخدام اسم المتغير: let message mesaage = 'Hello!'; alert(message); // اظهار محتوى المتغير وللاختصار، من الممكن الدمج بين التصريح عن المتغير وإسناد قيمة معينة له في نفس السطر بالشكل التالي: let message = 'Hello!'; // التصريح عن المتغير وإسناد قيمة له alert(message); // Hello! كما من الممكن أيضًا التصريح عن عدة متغيرات في نفس السطر: let user = 'John', age = 25, message = 'Hello'; يبدو هذا أقصر، ولكن لا أنصحك باستخدام هذه الطريقة. استخدم سطرًا مستقلًا لكل متغير لتسهيل قراءة الشيفرة البرمجية. تبدو المتغيرات المتعددة الأسطر (المكتوبة على أسطر منفصلة) أطول قليلًا، ولكنها أسهل عند القراءة: let user = 'John'; let age =25; let message = 'Hello'; يصرِّح بعض المبرمجين عن عدة متغيرات بأسلوب الأسطر المتعددة بالشكل التالي: let user = 'John', age = 25, message = 'Hello'; أو حتى من الممكن استخدام أسلوب «الفاصلة أولًا» (comma-first): let user = 'John' , age = 25 , message = 'Hello'; برمجيًا، جميع هذه الحالات متماثلة وظيفيًّا. لذلك، يعود الأمر لك لتختار الأنسب والأفضل. ملاحظة حول استخدام var بدلًا من let: في السكربتات القديمة، من الممكن أن تلاحظ استخدام الكلمة المفتاحية var بدلًا من let للتصريح عن المتغيرات: var message = 'Hello'; وللكلمة المفتاحية var نفس عمل let تقريبًا. فهي تصرح أيضًا عن المتغيرات ولكنها تختلف عنها قليلًا، ومن الممكن أن تعتبرها، الطريقة «التقليدية» (old-school) للتصريح عن المتغيرات. هناك فروق دقيقة بين let و var، لكنها حاليًا غير مهمة بالنسبة لك. سندرس هذه الفروق بالتفصيل في فصل المتغير القديم. مثال واقعي للمتغيرات إن أردت فهم مبدأ عمل المتغيرات فهمًا جيدًا، فتخيل «صندوقًا»، وعليه لُصاقة باسم مميز. فمثلًا، بإمكانك تخيل المتغير message بصندوق مسمى بالاسم "message"، وداخل هذا الصندوق القيمة "Hello!‎" بإمكانك وضع أي قيمة في الصندوق، كما بإمكانك تغيير هذه القيمة مرارًا وتكرارًا: let message; message = 'Hello!'; message = 'World!'; // value changed alert(message); ولكن لاحظ أنه عندما تتغير القيمة، تُحذَف البيانات السابقة من المتغير. بإمكانك أيضًا التصريح عن متغيرين ونسخ البيانات من أحدهما إلى الآخر. let hello = 'Hello world!'; let message; // message إلى المتغير hello من المتغير 'Hello world' نسخ القيمة message = hello; // يملك المتغيران الآن البيانات نفسها alert(hello); //Hello world! alert(message); // Hello world! اللغات الوظيفية من الأمور التي تجدر ملاحظتها أيضًا أن اللغات البرمجية الوظيفية، مثل Scala أو Erlang، تمنع تغيير قيم المتغيرات بعد إسنادها إليها. أي عندما تخزن القيمة في الصندوق، في مثل تلك اللغات، فإنها تبقى فيه للأبد. وعند الحاجة إلى تخزين بيانات أخرى، تجبرك اللغة على خلق صندوق آخر (التصريح عن متغير جديد)، ولا يمكنك آنذاك إعادة استخدام الصندوق السابق ولا حتى إعادة الصندوق الحالي الذي أنشأته. هل يمكنك تخيل كومة الصناديق التي ستتراكم بعد فترة من الزمن؟! يبدو الأمر غريبًا وصعبًا قليلًا، ولكن فعليًا هذه اللغات قادرة على إجراء تطويرات جادة ومهمة. كما أنَ بعض المجالات تستغل هذه المحدودية وتعتبرها فائدة كما في الحوسبة المتوازية مثلًا. دراسة مثل هذه اللغات جيد لتوسيع مداركك وآفاقك (حتى لو كنت لا تخطط لاستخدامها قريبًا). تسمية المتغيرات هنالك شرطان لأسماء المتغيرات في JavaScript: يجب ألا يحتوي اسم المتغير إلا على حروف، و أرقام، و الرمزين $ و _ فقط. يمنع استخدام رقم في أول حرف من الاسم، أي يجب ألا يبدأ اسم المتغير برقم. أمثلة عن التسمية الصحيحة للمتغيرات: let userName; let test123; عندما يتألف الاسم من عدة كلمات، يستخدم عادةً أسلوب «سنام الجمل» (camelCase). حيث تكتب الكلمات متتالية دون أي فاصل، وتبدأ كل كلمة بحرف كبير: myVeryLongName. الأمر المميز الآخر هو أنه من الممكن استخدام رمز الدولار $ والشرطة التحتية _ في أسماء المتغيرات. وهما عبارة عن رمزين عاديين، كأي حرف آخر، وليس لهما معنًى خاص. أمثلة عن بعض الأسماء المسموحة: let $=1; // "$" التصريح عن متغير اسمه let _=2; // "_" والتصريح عن متغير آخر اسمه alert($ + _); // 3 وأمثلة عن بعض أسماء المتغيرات المكتوبة خطأً: let 1a; // لا يمكن بدء اسم المتغير برقم let my-name; // لا يسمح باستعمال الشرطة '-' في أسماء المتغيرات ملاحظة: حالة الأحرف مهمة فالمتغير الذي اسمه apple مختلفٌ تمامًا عن المتغير الذي اسمه AppLE. ملاحظة: استخدام أحرف من لغات أخرى غير الإنجليزية من الممكن استخدام أحرف من أي لغة غير الإنجليزية وحتى الأحرف العربية أو السيريلية أو الهيروغليفية، ولكن لا ننصحك بذلك. فمثلًا: let имя = '…'; let 我 = '…'; let رسالة = '…'; برمجيًا، لا توجد أية أخطاء هنا، ومثل هذه الأسماء ممكنة، ولكن هناك تقليد عالمي باستخدام أسماء متغيرات إنجليزية. فحتى لو كنت تكتب سكربتًا صغيرًا ولكن من الممكن أن يبقى فترة طويلة ويصل إلى مبرمجين من بلدان أخرى في وقت ما. الأسماء المحجوزة هناك قائمة من الكلمات المحجوزة، والتي ليس ممكنًا استخدامها كأسماء للمتغيرات لأنها مستخدمة من قبل اللغة نفسها. مثلًا، الكلمات التالية: let، و class، و return، و function محجوزة. والشيفرة البرمجية التالية سينتج عنها خطأ في الصياغة: let let = 5; // "let" خطأ! لا يمكن تسمية المتغير بالاسم let return = 5; // أيضًا "return" خطأ! لا يمكن تسمية المتغير بالاسم الإسناد بعيدًا عن الوضع الصارم نحتاج عادةً إلى التصريح عن متغير قبل استخدامه. ولكن سابقًا، كان من المسموح برمجيًا خلق المتغير فقط بإسناد قيمة له دون استخدام let أو أي كلمة مفتاحية أخرى. ما زال هذا ممكنًا، ولكن بشرط عدم استخدام الموجه "use strict" في السكربت وذلك لضمان استمرارية التوافق مع السكربتات السابقة. // هنا "use strict" انتبه إلى عدم استخدام الموجه num = 5; // إن لم يكن موجودًا "num" سيُنشَأ المتغير alert(num); // 5 ولكن أبقِ في ذهنك أنها طريقة سيئة، وسينتج عنها خطأ عند عملك ضمن الوضع الصارم (strict mode): "use strict" num = 5; // error: num is not defined الثوابت للتصريح عن ثابت، وهو المتغير الذي لا تتبدل قيمته، استخدم const بدلًا من let: const myBirthday = '18.04.1982'; المتغيرات التي يُصرَّح عنها باستخدام const تُسمَّى «الثوابت» ولا يمكن أن تتغير قيمتها؛ وأي محاولة للقيام بذلك ينتج عنها خطأ عند تنفيذ الشيفرة: const myBirthday = '18.0401982'; myBirthday = '01.01.2001'; // خطأ! لا يمكن إعادة إسناد قيمة لثابت يستخدم المبرمج الثابت عندما يكون متأكدًا أن قيمة المتغير لن تتبدل أو لا يجب أن تتبدل ضمن البرنامج، ويصرح عنه باستخدام const. أما الفائدة من ذلك فهي ضمان والتحقق من وصول هذه الحقيقة إلى الجميع. متى تستخدم الأحرف الكبيرة في تسمية الثوابت؟ هنالك عرف منتشر في استخدام الثوابت كأسماء مستعارة أو مرادفات للقيم صعبة الحفظ أو القيم العددية الثابتة المعروفة قبل التنفيذ. تسمى مثل هذه الثوابت باستخدام الأحرف الكبيرة والشرطة السفلية (_). كما في هذا المثال: const COLOR_RED = "#F00"; const COLOR_GREEN = '#0F0"; const COLOR_BLUE = '#00F"; const COLOR_ORANGE = '#FF7F00"; // .. عندما نريد انتقاء لون let color = COLOR_ORANGE; alert(color); // #FF7F00 والفائدة من ذلك: حفظ وتَذكُّر COLOR_ORANGE أسهل بكثير من "‎#FF7F00". احتمال ارتكاب خطأ بكتابة "‎#FF7F00" أكبر بكثير من كتابة COLOR_ORANGE. قراءة الاسم COLOR_ORANGE في الشيفرة البرمجية له معنًى جلي خلافًا لقراءة ‎#FF7F00. ربما تتساءل الآن وأنت في حيرة، متى تستخدم الأحرف الكبيرة لتسمية الثوابت ومتى نسميها بالشكل الاعتيادي؟ حسنًا، سنوضح ذلك الآن. عندما تصرِّح عن ثابت، فهذا يعني أن قيمة هذا المتغير لن تتبدل أبدًا. ولكن ميز بين نوعين من الثوابت: الأول هو الثوابت التي تكون قيمتها معروفة قبل التنفيذ (كما في المثال السابق، فالقيمة الست عشرية للون الأحمر معروفة سابقًا وثابتة). أما النوع الثاني، فهو الثوابت التي تحسب قيمتها أثناء عمل السكربت، وخلال التنفيذ، ولكنها لا تتغير بعد إسناد قيمة لها. مثال على ذلك: const pageLoadTime = /* الوقت الذي تستغرقه الصفحة للتحميل */; قيمة الثابت pageLoadTime تكون غير معروفة قبل تحميل الصفحة، لذلك يتم تسمية الثابت بشكل عادي. ولكنه ثابت لأنَّ قيمته لا تتغير بعد إسنادها له. بمعنًى آخر، الثوابت المسماة بأحرف كبيرة تُستخدَم فقط كمرادفات للقيم صعبة الكتابة في الشيفرة البرمجية (hard-coded). تسمية الأشياء تسميةً صحيحةً بما أننا نتكلم عن المتغيرات، لابدّ أن نذكر أمرًا مهمًّا للغاية متعلق بتسمية المتغيرات. أرجوك أن تختار أسماء منطقية للمتغيرات، وأن تأخذ الوقت الكافي للتفكير بالأسماء المناسبة لها. لا تُعدُّ مهمة تسمية المتغيرات مهمةً بسيطةً، فهي واحدة من المهارات الأكثر أهمية وتعقيدًا في البرمجة. و بإمكانك بنظرة سريعة على أسماء المتغيرات المستخدمة في الشيفرة البرمجية معرفة مدى خبرة المطور الذي كتبه. في الواقع، وعند العمل على أي مشروع، فإن أغلب الوقت يمضي في تعديل وتوسيع الشيفرة البرمجية الموجودة بدلًا من كتابة شيفرة برمجية جديدة من الصفر. لذلك، عندما تعود إلى شيفرتك البرمجية بعد القيام بأشياء أخرى لفترة، يكون سهلًا بالنسبة إليك إيجاد المعلومات المسماة بشكل جيد. أو بمعنى آخر، عندما تملك المتغيرات أسماء جيدة. رجائي لك، مرةً أخرى، أن تمضي بعض الوقت في التفكير في الأسماء الصحيحة للمتغيرات قبل التصريح عنها. القيام بذلك سيعود عليك بالنفع بشكل رائع. وإليك بعض النصائح الجيدة لاتباعها في تسمية المتغيرات: استخدم أسماء يمكن للآخرين قرائتها وفهمها مثل userName أو shoppingCart. ابتعد عن الاختصارات أو الأسماء القصيرة مثل a، أو b، أو c إلا إذا كان العمل واضحًا لك بشكل جيد. اجعل أسماء المتغيرات تصف محتواها ولكن لا تبالغ كثيرًا، بل حاول أن تكون مختصرة أيضًا. وكمثال عن الأسماء السيئة data و value. مثل هذه الأسماء لا تعبر عن أي شيء. ومن الممكن استخدامها فقط إذا كان سياق الشيفرة البرمجية، يوضح بشكل بديهي واستثنائي، أي بيانات أو قيم يشير إليها المتغير. اتفق على شروط معينة ضمن فريقك أو حتى مع نفسك في تسمية المتغيرات. فإذا كان اسم زائر الموقع هو user، فإن اسم المتغيرات المرتبطة به تكون أسماؤها currentUser أو newUser عوضًا عن currentVisitor أو NewManInTown. يبدو ذلك بسيطًا، أليس كذلك؟ بالتأكيد، إنها عملية بسيطة. ولكن اختيار أسماء جيدة ومختصرة ليس بالأمر السهل في الحياة العملية، ولكن ننصحك بإيلاء أسماء المتغيرات أهمية كبيرة واختيارها بعناية مطلقة. هل تعيد استخدام المتغير أم تخلق متغيرًا جديدًا؟ أخيرًا وليس آخرًا، هناك بعض المبرمجين الكسالى الذين يفضلون إعادة استخدام المتغيرات الموجودة عوضًا عن خلق متغيرات جديدة. وبالنتيجة، تصبح متغيراتهم مثل الصناديق التي يرمي فيها الناس أشياء مختلفة من دون تغيير مسمياتهم. ماذا يوجد الآن داخل الصندوق؟ لا أحد يعلم. نحتاج إلى الاقتراب والتحقق من محتواه. قد يوفر هؤلاء المبرمجون بعض الوقت لعدم تصريحهم عن متغيرات جديدة. ولكنهم يخسرون عشرة أضعاف هذا الوقت عند استكشاف الأخطاء. المتغير الإضافي هو أمر جيد وليس سيء. وتعمل «مصغرات» (minifiers) لغة JavaScript الحالية والمتصفح على تحسين الشيفرة البرمجية بشكل جيد، وذلك لتفادي أية مشاكل في أدائها. كما أن استخدام المتغيرات المختلفة للقيم المختلفة يساعد المحرك أيضًا في تحسين شيفرتك البرمجية. الخلاصة بإمكاننا التصريح عن المتغيرات لتخزين البيانات باستخدام الكلمات المفتاحية var، أو let، أو const. let: تستخدم للتصريح عن المتغيرات في النسخة الحالية. كما يجب استخدام الوضع الصارم (strict mode) لاستخدام let في V8. var: هي الطريقة التقليدية للتصريح عن المتغيرات. عادةً، لا نستخدم هذه الطريقة على الإطلاق، ولكن سنتعرف على الفروقات الدقيقة بينها وبين let في فصل المتغير القديم var، وهذا أمر ضروري في حال اضطررت لاستخدامهم. const: مثل let، ولكن قيمة المتغير لا يمكن تعديلها. كما يجب تسمية المتغيرات بطريقة تسمح لنا بفهم القيم المسندة إليها بسهولة. .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } تمارين 1. العمل مع المتغيرات الأهمية: 2 صرّح عن المتغيرين: admin و name. أسند القيمة "John" إلى المتغير name. انسخ قيمة name إلى المتغير admin. اعرض قيمة المتغير admin باستخدام التنبيه alert (يجب أن يكون الخرج "John"). الحل: في الشيفرة البرمجية التالية، كل سطر يمثل أحد الأمور المطلوبة في قائمة المهام: let admin, name; // يمكننا التصريح عن متغيرين في الوقت نفسه name = "Mohammad"; admin= name; alert (admin); // "Mohammad" 2. اختيار الأسماء المناسبة الأهمية: 3 أنشئ متغيرًا وليكن اسمه كوكبنا (our planet). كيف يمكنك تسمية هذا المتغير؟ أنشئ متغيرًا لتخزين اسم الزائر الحالي للموقع. كيف بإمكانك اختيار اسم هذا المتغير؟ الحل: أولًا: المتغير باسم كوكبنا، هذا أمر سهل: let ourPlanetName = "Earth"; لاحظ أنه بالإمكان استخدام اسم أقصر من ذلك مثل planet، ولكن ذلك قد لا يكون واضحًا بالشكل الكافي، أي كوكب نقصد؟ لذلك من الجيد أن تكون أكثر تفصيلًا. على الأقل عندما يكون اسم المتغير ليس طويلًا isNotTooLong. ثانيًا: اسم المتغير الذي يحوي بيانات الزائر الحالي للموقع: let currnetUserName = "John"; مرةً أخرى، بالإمكان اختصار هذا الاسم إلى username، إذا كنت متأكدًا أن هذا الزائر هو الزائر الحالي. المحررات الحالية فيها ميزة الإكمال التلقائي والتي تجعل من السهل كتابة أسماء المتغيرات الطويلة. لا تختصر في اختيار أسماء المتغيرات، فأي اسم يتألف من 3 كلمات هو أمر عادي ومقبول. وإذا كانت ميزة الإكمال التلقائي في محررك سيئة أو غير مناسبة، لا تتردد في تغييره. 3. الثوابت بالأحرف الكبيرة؟ الأهمية: 4 تفحص الشيفرة البرمجية التالية: const birthday = '18.04.1982'; const age = someCode(birthday); هنا لدينا الثابت birthday الذي يحوي تاريخًا والثابت age الذي سيُحسَب من birthday عن طريق الشيفرة البرمجية someCode (لم يتم كتابتها للاختصار، ولأن هذه التفاصيل غير مهمة الآن). هل يكون صحيحًا استخدام الأحرف الكبيرة لتسمية الثابت birthday أو الثابت age أو كلاهما؟ const BIRTHDAY = '18.04.1982'; // هل يُكتَب اسم المتغير بالأحرف الكبيرة؟ const AGE = someCode(BIRTHDAY); // هل يُكتَب اسم المتغير بالأحرف الكبيرة؟ الحل: نستخدم عادةً الأحرف الكبيرة لتسمية الثوابت كمرادفات للقيم صعبة الكتابة في الشيفرة البرمجية (hard-coded). أو بمعنى آخر، عندما تكون قيمة الثابت معروفة قبل التنفيذ ومكتوبة بشكل مباشر في الشيفرة البرمجية. في هذه الشيفرة البرمجية، يمثل الثابت birthday هذه الحالة تمامًا. لذلك نستطيع تسميته بأحرف كبيرة. وبشكل معاكس، فإن الثابت age يُحسَب أثناء عمل السكربت، اليوم يكون لنا عمر معين ولكن يختلف عمرنا في السنة القادمة، هو ثابت من ناحية عدم تغير قيمته أثناء تنفيذ الشيفرة البرمجية ولكنه أقل ثباتًا من الثابت birthday. وبما أننا نقوم بحسابه، لذلك يفضل إبقاء اسمه بالأحرف الصغيرة. ترجمة -وبتصرف- للفصل Variables من كتاب The JavaScript Language انظر أيضًا المقال التالي: أنواع البيانات المقال السابق: الوضع الصارم: النمط الحديث لكتابة الشيفرات كامل مقالات دليل تعلم جافاسكربت
  16. تطورت لغة JavaScript خلال فترة طويلة دون أي مشاكل في التوافق. وجرى التحقق من استمرار عمل جميع وظائفها في كل مرة تضاف إليها خصائص جديدة. وكان لذلك فائدة كبيرة في استمرار عمل الشيفرات البرمجية الموجودة دون تعطلها. ولكن كان له ناحية سلبية أيضًا، هي أنّ الأخطاء والخصائص التي أضافها مطورو اللغة بقيت مع اللغة إلى الأبد. واستمرت هذا الحال حتى ظهور ECMAScript 5 (اختصارًا ES5) في عام 2009. التي أضافت خصائص جديدة إلى اللغة وعدّلت على بعض الخصائص الموجودة سابقًا. ولضمان استمرار عمل الشيفرات البرمجية السابقة، فإنّ معظم هذه التعديلات كانت غير فعّالة بشكل افتراضي؛ ولاستخدامها، عليك تفعيلها بشكل صريح باستخدام الموجه الخاص: "use strict". الموجه "use strict" يبدو الموجه كسلسلة نصية: "use-strict" أو 'use strict'. وعندما يوضع في بداية السكربت، يُنفَّذ السكربت وفق الطريقة والمعايير الحديثة. إليك المثال التالي: "use strict"; //هذه الشيفرة البرمجية تعمل في الوضع الصارم … قريبًا، سندرس الدوال (طريقة لجمع الأوامر). ولكن في نطرة استباقية للموضوع، لاحظ أنه بالإمكان وضع "use strict" في بداية معظم أنواع الدوال عوضًا عن كامل السكربت. وبذلك يتم تفعيل الوضع الصارم (strict mode) ضمن الدوال فقط. لكن عادةً يستخدم المبرمجون هذا الوضع لكامل السكربت. تأكد من وضع "use script" في بداية السكربتات، وإلا فإنه من الممكن ألا يُفعَّل الوضع الصارم. فمثلًا، الوضع الصارم غير مفعّل في المثال التالي: alert("some code"); // إن لم يُستعمَل في أول سطر "use strict" سيجري تجاهل الموجه "use strict" // الوضع الصارم غير مُفعَّل بإمكانك كتابة التعليقات فقط قبل استخدام الموجه "use strict". لاحظ أيضًا أنه ليس هناك طريقة لإلغاء "use strict"، أي لا يوجد موجه آخر مثل "no use strict" والذي يثني المحرك عن عمله ويلغي تفعيل الوضع الصارم. لذلك عند بدء استخدام الوضع الصارم، لا توجد طريقة لإلغائه والعودة إلى الوضع الافتراضي. طرفية المتصفح أبقِ في ذهنك أن طرفية المتصفح (console) لا تستخدم الوضع الصارم لاختبار وتنفيذ الشيفرات المكتوبة فيها؛ أي أنها لا تستخدم "use strict" افتراضيًا. عندما يوجد هناك اختلاف بين "use strict" والنمط الافتراضي في بعض الأحيان، قد تحصل على ناتج خطأ. ولن تنجح محاولتك في تفعيل الوضع الصارم (strict) بالضغط على الاختصار Shift+Enter لإدخال عدة أسطر، ثم استخدام "use strict" في البداية وذلك بسبب طريقة معالجة الطرفية للشيفرة البرمجية داخليًا. الطريقة الأفضل للتأكد من عمل "use strict" هو كتابة الشيفرة البرمجية ضمن الطرفية كما يلي: (function() { 'use strict'; // … ضع شيفرتك هنا … })() استخدم "use strict" دومًا سنتعلم لاحقًا الاختلافات بين الوضع الصارم والوضع الافتراضي الذي تعمل ضمنه الشيفرات. ففي الفصل القادم، ستلاحظ الاختلافات بين هذين الوضعين أثناء تعلمك للخصائص الجديدة. من الجيد أنه لا يوجد الكثير من هذه الاختلافات، وفي حال وجودها فهي لتحسين عمل الشيفرة البرمجية. ولكن في الوقت الحالي تكفي معرفتك بهذه الأمور العامة: الموجه "use strict" يحول المحرك إلى النمط الحديث (modern)، مما يغير من طريقة تعامله مع بعض الخصائص الموجودة سابقًا. سندرس ذلك بالتفصيل في الأجزاء القادمة من هذه السلسلة التعليمية. يتم تفعيل الوضع الصارم بوضع الموجه "use strict" في بداية السكربت أو الدالة. العديد من خصائص اللغة، مثل الأصناف (classes) والوحدات (modules)، ُتفعِّل الوضع الصارم (strict) تلقائيًا. الوضع الصارم (strict) مدعوم من قبل جميع المتصفحات الحالية. أنصحك دائمًا بوضع "use strict" في بداية السكربت. جميع الأمثلة في هذه السلسلة التعليمية تفترض العمل ضمن الوضع الصارم إلا إذا ذُكر غير ذلك وهذا في بعض الحالات النادرة. ترجمة -وبتصرف- للفصل "Modern mode, "use strict من كتاب The JavaScript Language انظر أيضًا المقال التالي: المتغيرات المقال السابق: بنية الشيفرة البرمجية كامل مقالات دليل تعلم جافاسكربت
  17. في هذا المقال، سنبدأ بتعلم أساسيات كتابة الشيفرة البرمجية في لغة JavaScript. هل أنت مستعد؟ لننطلق! التعابير البرمجية التعابير البرمجية هي صياغة التراكيب والأوامر التي تنفذ الأعمال في السكربت. رأيت سابقًا التعبير البرمجي alert('Hello, world!')‎ الذي يُظهر الرسالة "Hello, world!‎". بإمكانك استخدام تعابير بقدر ما تريد في شيفرتك البرمجية. ويمكن فصل التعابير البرمجية عن بعضها بالفاصلة المنقوطة (;). مثلًا، بإمكاننا فصل "Hello World" إلى تنبيهين منفصلين بالشكل التالي: alert('Hello'); alert('World'); تُكتَب التعابير البرمجية عادةً على أسطر منفصلة لتسهيل قراءة الشيفرة البرمجية: alert('Hello'); alert('World'); الفاصلة المنقوطة يمكن حذف الفاصلة المنقوطة من آخر التعابير البرمجية في معظم الحالات عند الانتقال إلى سطر جديد. أي أن الشيفرة البرمجية التالية ستعمل أيضًا بشكل صحيح: alert('Hello') alert('World') تُفسِّر لغة JavaScript الانتقال إلى سطر جديد بفاصلة منقوطة (ضمنيًا). وهذا ما يُسمى (الاضافة التلقائية للفاصلة المنقوطة [Automatic semicolon insertion]). في معظم الحالات، يتضمن السطر جديد فاصلةً منقوطةً افتراضية في آخره ولكن هذا لا يشمل جميع الحالات. هناك حالات لا يحوي فيها السطر الجديد بالضرورة فاصلةً منقوطةً افتراضية. إليك مثلًا: alert(3+ 1 +2); خرج الشيفرة البرمجية السابقة هو 6 لأنَّ لغة JavaScript لا تضيف الفاصلة المنقوطة في هذه الحالة. فمن البديهي أنه عند انتهاء السطر بإشارة "+" يكون التعبير ناقصًا (incomplete expression)، وبالتالي لا يتطلب وجود فاصلة منقوطة. لذلك، تعمل الشيفرة البرمجية كما هو مطلوب. لا تعتمد دائمًا على JavaScript لإضافة الفاصلة المنقوطة لأنه توجد حالات تفشل فيها لغة JavaScript بإضافة الفاصلة المنقوطة عندما تكون مطلوبة. والأخطاء التي تحدث نتيجة هذه الحالات صعبة الإيجاد والإصلاح (ستكتشف ذلك قريبًا خلال الشيفرات التي ستكتبها). إذا كنت ترغب في الاطلاع على مثال واقعي عن هذه الحالة، إليك الشيفرة البرمجية التالية: [1, 2].foreach(alert) لا تقلق إن كانت هذه الشيفرة صعبة الفهم عليك، فليس هناك حاجة الآن لفهم معنى الأقواس المعقوفة [] أو التعبير البرمجي forEach، فسندرسهم لاحقًا. ولكن أبقِ في ذهنك أن خرج هذه الشيفرة البرمجية هو إظهار 1 ثم 2. سنضيف الآن تنبيهًا (alert) قبل الشيفرة البرمجية السابقة دون وضع الفاصلة المنقوطة في نهاية العبارة البرمجية: alert("There will be an error") [1 ,2].forEach(alert) عند تنفيذ الشيفرة البرمجية آنذاك، سيتم إظهار التنبيه الأول فقط ثم سنحصل على خطأ. تعود الشيفرة البرمجية للعمل بشكل صحيح مرة أخرى عند إضافة الفاصلة المنقوطة بعد التنبيه الأول: alert("All fine now"); [1 ,2].forEach(alert) تظهر لدينا الآن الرسالة "All fine now"، ثم 1 و 2. الخطأ الذي حدث في الحالة الأولى (حالة عدم إضافة الفاصلة المنقوطة) سببه أنَّ لغة JavaScript لا تضيف الفاصلة المنقوطة تلقائيًا عند الانتقال إلى سطر جديد في حال وجود الأقواس المعقوفة […] في بداية هذا السطر. وبالتالي لن تُضاف الفاصلة المنقوطة تلقائيًا في الحالة الأولى وسيتم التعامل مع الشيفرة البرمجية كعبارة برمجية واحدة. أي سيراها المحرك بالشكل التالي: alert("There will be an error")[1, 2].forEach(alert) ولكنهما عبارتين برمجيتين منفصلتين وليستا عبارة واحدة، وبالتالي عملية الدمج في هذه الحالة خطأ. من الممكن أن تتكرر هذه الحالة ضمن شروط أخرى. بناءً على ما سبق، ننصحك بإضافة الفاصلة المنقوطة بين التعابير البرمجيَّة حتى لو قمت بفصلها بأسطر جديدة. وهذه هي القاعدة الأكثر اتباعًا بين مستخدمي JavaScript. لنراجع سويةً ما ذُكر سابقًا، من الممكن الاستغناء عن الفاصلة المنقوطة في معظم الحالات، ولكن من الأفضل إضافتها في آخر العبارة البرمجية - وخاصةً بالنسبة للمبتدئين - تجنبًا للوقوع في أخطاء عصية التنقيح والتصحيح أنت بغنًى عنها. التعليقات تصبح البرامج أكثر تعقيدًا مع مرور الوقت. ويكون ضروريًا إضافة التعليقات لشرح عمل الشيفرة البرمجية. يمكن وضع التعليقات في أي مكان ضمن السكربت دون أن تؤثر على تنفيذه، لأنَّ المحرك ببساطة يتجاهل جميع التعليقات. التعليقات المكتوبة على سطر واحد تبدأ بخطين مائلين (forward slash) بالشكل //. ويكون الجزء التالي للخطين المائلين على نفس السطر تعليقًا. ومن الممكن أن يشغل التعليق سطرًا كاملًا أو يأتي التعليق بعد العبارة البرمجية. إليك المثال التالي الذي يشرح ما سبق: // يمتد هذا التعليق على كامل السطر فقط alert('Hello'); alert('World'); // هذا تعليق يلي تعبيرًا برمجيًّا وإن أردت كتابة تعليقات تمتد على عدَّة أسطر، فابدأ التعليق متعدد الأسطر بخط مائل يليه رمز النجمة (أي ‎/*‎) ، وأنهِ التعليق برمز النجمة ثم الخط المائل (أي ‎*/‎) . إليك المثال التالي: /* يُظهر هذا المثال تنبيهين وهذا التعليق متعدد الأسطر */ alert('Hello'); alert('World'); يجري تجاهل كل ما يقع داخل التعليق متعدد الأسطر، وبالتالي لا تُنفَّذ أية شيفرة برمجية موجودة داخل /*…*/. أحيانًا، يكون من المفيد إلغاء تفعيل جزء من الشيفرة البرمجية مؤقتًا أثناء تنقيح الأخطاء: /* تعليق جزء من الشيفرة alert('Hello'); */ alert('World'); استخدام الاختصارات من الممكن تعليق سطر واحد من الشيفرة البرمجية بالضغط على الاختصار Ctrl+/‎ في معظم المُحرِّرات وبيئات التطوير. ومن أجل التعليقات متعددة الأسطر من الممكن استخدام الاختصار Ctrl+Shift+/، (عليك أن تحدد جزءًا من الشيفرة البرمجية ثم الضغط على الاختصار). في الأجهزة التي تعمل على الماك، جرّب Cmd عوضًا عن Ctrl. تحذير: التعليقات المتداخلة متعددة الأسطر غير مدعومة. أي لا يمكنك وضع /*…*/ داخل /*…*/ آخر، إذ ستنتهي هذه الشيفرة البرمجية بخطأ: /* التعليق الخارجي /*تعليق متشعب*/ لاحظ لوني الأسود */ alert('World'); لا تتردد أبدًا في وضع التعليقات ضمن شيفرتك البرمجية وشرح ما الذي يجري فيها لأنك عندما تعود إليها لاحقًا، ستجد غالبًا أنك نسيت وظيفة كل جزء من شيفرتك. قد تزيد التعليقات من حجم الشيفرة ولكن هذه ليست بمشكلة. هناك العديد من الأدوات التي تُقلِّص الشيفرة البرمجية قبل نشرها على خادم الإنتاج، إذ تَحذِف التعليقات من السكربت نهائيًّا ولا يبقَ لها بذلك أي أثر. في تلك الحالة، لا يكون للتعليقات أي آثار سلبية عند الإنتاج. لا تقلق مرة أخرى إن لم تتضح لك الصورة ولم تفهم بعض المصطلحات (مثل ما الذي يقصد بالإنتاج) فكل شيء سيتضح تدريجيًّا. لاحقًا في هذه السلسلة التعليمية، سيكون هناك فصل عن جودة الشيفرة البرمجية والذي يشرح طريقة كتابة التعليقات بشكل أفضل. ترجمة -وبتصرف- للفصل Code structure من كتاب The JavaScript Language انظر أيضًا المقال التالي: الوضع الصارم: النمط الحديث لكتابة الشيفرات المقال السابق: المثال الأول: أهلًا بالعالم! كامل مقالات دليل تعلم جافاسكربت
  18. أبقِ في ذهنك أنَ هذه السلسلة التعليمية هي عن أساس لغة جافاسكربت (core JavaScript) المستقلة عن أي منصة. سنتعرف لاحقًا على Node.JS والمنصات التي تستخدمها. الآن، نحن بحاجة إلى بيئة للعمل وتشغيل السكربتات التي سنكتبها، ويبدو المتصفح خيارًا جيدًا بما أنك تتابع هذه السلسلة التعليمية عبر الإنترنت. سنقلل من استعمال التعليمات التي تعمل فقط على المتصفح (مثل alert) لكي لا يضيع مجهودك بالتركيز عليها خاصةً إن كنت تنوي العمل في بيئة أخرى (مثل Node.JS). وسنركِّز على لغة JavaScript ضمن المتصفح في الجزء القادم من هذه السلسلة التعليمية. في البداية، سنتعرَّف على طريقة إضافة السكربت إلى صفحة الويب. من أجل البيئات التي تعمل على الخادم (مثل Node.JS)، بإمكانك تنفيذ السكربت عن طريق أمرٍ مثل node my.js. الوسم <script> من الممكن تضمين برامج لغة JavaScript في أي جزء من مستند HTML باستخدام الوسم <script>. على سبيل المثال: <!DOCTYPE HTML> <html> <body> <p>Before the script… </p> <script> alert(‘Hello, world!’); </script> <p>...After the script.</p> </body> </html> يحتوي الوسم <script> على شيفرة JavaScript تُنفَّذ تلقائيًا عندما يعالج المتصفح الوسم. الترميز الحالي يملك الوسم <script> عددًا من الخاصيات، ونادرًا ما يتم استخدامها الآن، ولكن ما زال بإمكانك رؤيتها في الشيفرات البرمجية القديمة. الخاصية type <script type=...> تتطلب معايير لغة HTML القديمة، HTML4، إسناد قيمة للخاصية type في الوسم <script>، وعادةً ما تكون type="text/javascript"‎، لكن هذا الأمر لم يعد ضروريًا. كما أنَ معنى هذه الخاصية تغيَر بشكل كامل وفق معايير لغة HTML بنسختها الحالية، HTML5. وأصبحت تستخدم مع وحدات (modules) لغة JavaScript. ولكنه موضوع متقدم سنتحدث عنه لاحقًا في جزء آخر من هذه السلسلة التعليمية. الخاصية language <script language=...> تُستخدَم هذه الخاصية لتحديد اللغة المكتوب بها السكربت، ولكنها لم تعد مهمة الآن، لأن لغة JavaScript أصبحت هي اللغة الافتراضية، ولم تعد هناك حاجة لاستخدام هذه الخاصيَّة. التعليقات قبل وبعد السكربتات من الممكن أن تجد تعليقات ضمن الوسم <script> في كتب ومراجع لغة JavaScript القديمة، مثل: <script type=”text/javascript”><!-- … //--></script> تعمل هذه التعليقات على إخفاء الشيفرة البرمجية عن المتصفحات القديمة والتي لا تعرف معالجة الوسم <script> ولم تعد مستخدمة في لغة JavaScript بنسختها الحالية. وبما أن نسخ المتصفحات في السنوات الخمسة عشر الماضية لا تملك هذه المشكلة، فإنً هذا النوع من التعليقات يمكًنك من تحديد الشيفرات البرمجية القديمة للغة JavaScript. ما هو الاستعمال الحديث للعنصر <script>؟ عندما تكون الشيفرة البرمجية للغة JavaScript طويلة، قد ترغب في وضعها في ملف مستقل. ويتم ربط ملف السكربت إلى HTML عن طريق الخاصية src: <script src=”/path/to/script.js”></script> هنا المسار path/to/script.js/ هو المسار الكامل لملف السكربت (ابتداءً من جذر الموقع). بإمكانك أيضًا إدخال المسار نسبةً للصفحة الحالية. مثلًا، المسار ("src="script.js) معناه أنً الملف script.js موجود في نفس المجلد الذي توجد فيه الصفحة. بالإمكان أيضًا استخدام عنوان الموقع كاملًا، كما في المثال التالي: <script src=”https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js”></script> وإن أردت ربط عدة سكربتات مع نفس الصفحة، فاستخدم الوسم <script> لكل سكربت على حدة: <script src=”/js/script1.js”></script> <script src=”/js/script2.js”></script> ملاحظة: يمكنك العمل بالقاعدة التالية، «السكربتات البسيطة فقط تُضمَّن مع HTML والسكربتات الأكثر تعقيدًا تُكتَب في ملفات مستقلة». والفائدة من وجود ملف مستقل هي امكانية تنزيل المتصفح لهذا السكربت مرة واحدة وتخزينه في ذاكرة مؤقتة (cache). وبذلك، تتمكن الصفحات الأخرى المرتبطة بنفس السكربت من طلبه وجلبه من الذاكرة المؤقتة عوضًا عن تنزيله مرةً أخرى. وبذلك يتم فعليًا تنزيله مرة واحدة، مما يقلل حركة البيانات عبر الإنترنت ويُسرِّع تحميل ومعالجة صفحات الويب. تحذير: انتبه إلى أنه يجري تجاهل السكربت المكتوب ضمن الوسم <script> في حال ربطه مع ملف مستقل أي عند إسناد قيمة أو مسار معين للخاصية src. بعبارة أخرى، ليس بإمكان الوسم <script> أن يُنفِّذ شيفرتين برمجيَّتين في آن واحد: شيفرة قادمة من الخاصية src وشيفرة برمجية مكتوبة بداخله. فمثلًا، لن تُنفَّذ التعليمة البرمجية alert(1)‎ المكتوبة ضمن الوسم في هذا المثال: <script src=”file.js”> alert(1); // src سيجري تجاهل أي محتوى مكتوب ضمن الوسم بسبب ضبط الخاصية </script> يجب أن تختار أحد الأمرين: ملف خارجي مستقل للسكربت <script src="..."‎>، أو وسم <script> عادي يحوي ضمنه على الشيفرة البرمجية المراد تنفيذها. بالإمكان فصل السكربت في المثال السابق إلى وسمين <script> منفصلين ليعمل بشكل صحيح: <script src=”file.js”></script> <script> alert(1); </script> الخلاصة نستخدم الوسم <script> لتضمين الشيفرة البرمجية للغة JavaScript في صفحة الويب. في الوقت الحالي، لم نعد نستعمل الخاصيتين type و language. يمكن ربط السكربت الموجود في ملف خارجي باستخدام الخاصية src. هنالك الكثير لتعلمه عن السكربتات الممكن تنفيذها في المتصفح وتأثيرها على صفحات الويب. ولكن أبقِ في ذهنك أن هذا الجزء من السلسلة التعليمية يُركِّز على لغة JavaScript ويجب ألا نشوش أنفسنها بالتركيز على معايير تنفيذ السكربتات ضمن كل متصفح بشكل خاص. سنستخدم المتصفح وسيلةً لتنفيذ سكربتات JavaScript لأنه مناسب في حالتنا للتعلم عن طريق الإنترنت، ولكنه إحدى الطرق الكثيرة لتشغيل JavaScript. تمارين 1. إظهار تنبيه .task__importance { color: #999; margin-left: 30px; } .task__answer { border: 3px solid #f7f6ea; margin: 20px 0 14px; position: relative; display: block; padding: 25px 30px; } الأهمية: 5 أنشئ صفحةً تُظهِر الرسالة "I`m JavaScript". بإمكانك تنفيذها في صفحة تجريبية (sandbox)، أو على قرصك الصلب. لا يهم ولكن تأكد من أنها تعمل بالشكل الصحيح. بعد كتابة شيفرتك، نفِّذها ثم تأكد من الإجابة المرفقة. الحل: عرض توضيحي فتح الحل في بيئة تجريبية حية 2. إظهار تنبيه باستخدام سكربت خارجي الأهمية: 5 استخدم الحل في التمرين السابق (إظهار تنبيه)، وعدله لاستخراج الشيفرة البرمجية المراد تنفيذها إلى سكربت خارجي باسم "alert.js" موجود في نفس المجلد. افتح الصفحة وتأكد من عمل التنبيه. الحل: الشيفرة البرمجية لمستند HTML: <!DOCTYPE html> <html> <body> <script src="alert.js"></script> </body> </html> ويحتوي الملف alert.js في نفس المجلد على الشيفرة البرمجية التالية: alert("I'm JavaScript!"); ترجمة -وبتصرف- للفصل !Hello, world من كتاب The JavaScript Language انظر أيضًا المقال التالي: بنية الشيفرة البرمجية المقال السابق: محررات الشيفرة البرمجية كامل مقالات دليل تعلم جافاسكربت
  19. يقضي المبرمجون معظم وقتهم في العمل على أحد محررات الشيفرة البرمجية لكتابة وتطوير برامجهم. سنساعدك في هذا المقال على اختيار المحرر الأنسب لك. بدايةً، يوجد نوعان رئيسيان لمحررات الشيفرة البرمجية: «بيئات التطوير المتكاملة» (Integrated Development Environments وتدعى اختصارًا IDE) و «المحررات البسيطة» (lightweight editors). يستخدم العديد من المبرمجين محرِّرًا واحدًا من كل نوع. في بداية تعلمك للبرمجة، يمكنك استعمال المحررات البسيطة مثل المفكرة (notepad) في ويندوز والمحرر vim أو kate في لينكس ولكن ستحتاج مع تقدمك وتطور وكبر شيفرتك إلى بيئة تطوير متكاملة. فما هي بيئة التطوير المتكاملة؟ بيئة التطوير المتكاملة بيئة التطوير المتكاملة «IDE» عبارة عن محرر شيفرة برمجية قوي، ومزود بمزايا كثيرة ومتنوعة تكفي للعمل عادةً على كامل المشروع. وكما هو واضح من اسمها، فهي ليست كأي محرر عادي، ولكن «بيئة تطوير» شاملة. يمكنك تحميل المشروع بجميع ملفاته إلى بيئة التطوير (IDE)، مما يسمح بالتنقل بينها بسهولة، كما توفر هذه البيئة (IDE) ميزة الإكمال التلقائي أثناء كتابتك للشيفرة البرمجية كالتعليمات وأسماء المتغيرات بالنسبة لكامل المشروع (وليس ضمن الملف المفتوح فقط). وتتيح الاندماج مع نظام التحكم في النسخ مثل git، وبيئة الاختبار، وغيرها من الأمور التي تُنفذ على مستوى المشروع. بإمكانك الاطلاع على خيارات بيئات التطوير (IDE) التالية، لمساعدتك على اختيار واحدة منها للعمل عليها إن لم تحدد واحدة بعد: WebStorm: تستخدم لتطوير الواجهات الأمامية (Front-end development). توفر الشركة محررات أخرى للعديد من اللغات البرمجية ولكنها مدفوعة. NetBeans: بيئة متكاملة ومجانية وتعمل على مختلف أنظمة التشغيل. Visual Studio: خاص بنظام التشغيل «ويندوز»، (ميّز بينه وبين "Visual Studio Code"). وهو محرر قوي ومدفوع، ومناسب جدًا للعمل على منصة «NET.»، ويوجد نسخة مجانية منه تُسمى Visual Studio Community. أغلب بيئات التطوير المتكاملة متعددة المنصات، وتعمل على أنظمة تشغيل مختلفة ولكنها مدفوعة (لها فترة تجريبية مجانية)، وكلفتها متواضعة بالنسبة لدخل المطور المؤهل، لذلك ننصحك باختيار الأفضل والأنسب لك، دون القلق من كلفتها. المحررات البسيطة المحررات البسيطة (Lightweight editors) ليست قوية مثل بيئات التطوير المتكاملة، لكنها سريعة، وأنيقة، وبسيطة الاستخدام. تُستخدَم بشكل أساسي لفتح وتعديل الملف بطريقة سريعة وآنية. الاختلاف الرئيسي بين المحرر البسيط وبيئة التطوير المتكاملة، هو أنّ بيئة التطوير المتكاملة تعمل على «مستوى المشروع»، لذلك تُحمِّل الكثير من البيانات في البداية، وتكون قادرة على تحليل بنية المشروع عند الحاجة وغيرها من المهام. أمّا المحرر البسيط، يكون أسرع بشكل كبير عند الحاجة للعمل على ملف واحد فقط من المشروع. عمليًا، يمكن أن تملك المحررات البسيطة العديد من الإضافات التي تتضمن محللات ومدققات الصياغة على مستوى المشروع وميزة الإكمال التلقائي، وبالتالي لا توجد هناك حدود واضحة بين المحرر الخفيف وبيئة التطوير المتكاملة. لذلك وقبل أن تحسم رأيك باستخدام بيئة تطوير متكاملة، ننصحك بالاطلاع على الخيارات التالية للمحررات البسيطة: Visual Studio Code: مجاني ومتعدد المنصات ويملك العديد من المزايا المشابهة لمزايا بيئة التطوير المتكاملة. Atom: مجاني الاستخدام ومتعدد المنصات. Sublime Text: برنامج تجريبي ومتعدد المنصات ويدعم العديد من اللغات البرمجية (programming languages) واللغات التوصيفية (markup languages). Brackets: محرِّر مجاني ومفتوح المصدر وعابر للمنصات ومخصَّص بشكل عام لمطوري الويب وبشكل خاص لمصممي الويب ومطوري الواجهات الأمامية. ++Notepad: محرِّر مجاني وخاص بنظام التشغيل ويندوز. Vim و Emacs: محِّرران بسيطان، ويعدَّان خيارًا جيدًا إذا كنت تعرف كيفية استخدامهما. ما هو محرري المفضل الذي سأستخدمه؟ نصيحة مني لك هو تجريب الخيارين السابقين كلاهما؛ استخدم بيئة التطوير المتكاملة عند العمل على كامل المشروع، وأحد المحررات الخفيفة من أجل التعديلات السريعة والطفيفة في ملفات المشروع. يمكن في البداية استعمال المحررات الخفيفة (مثل VS code) التي تفي بالغرض في أغلب المشاريع ثمَّ الانتقال إلى البيئات المتكاملة (سواءً المجانية أو المدفوعة) في المستويات المتقدمة عندما تتولد الحاجة إليها. أسمعك تسألني ما الذي استخدمه؟ حسنًا، أنا استخدم: استعمل WebStorm - بيئة التطوير المتكاملة - عند العمل بلغة JavaScript، واستعمل خيارًا آخر من JetBrains مثل IntelliJ IDEA عند العمل بلغات برمجية مختلفة. واستخدم Sublime أو Atom كمحرر خفيف. قبل أن تختلف معي بشأن القائمة السابقة، فإن هذه المحررات هي إما محررات استخدمتها بنفسي أو استخدمها أحد أصدقائي من المطورين الجيدين لوقت طويل وكانوا سعداء باستخدامهم لها. هناك العديد من المحررات الجيدة أيضًا في عالمنا الكبير. لذلك، ننصحك بالعمل على المحرر المفضل لديك. فاختيار المحرر، مثل أي أداة أخرى، هو قرار خاص، ومعتمد على مشروعك، وعاداتك، وتفضيلاتك الشخصية. ولكي تعرف ما هو المحرر المفضل لديك، لا بد أن تجرب جميع المحرِّرات. ترجمة -وبتصرف- للفصل Code Editors من كتاب The JavaScript Language انظر أيضًا المقال التالي: المثال الأول: أهلًا بالعالم! المقال السابق: أدوات المطور كامل مقالات دليل تعلم جافاسكربت
  20. تستخدَم لغة JavaScript لإنشاء صفحات ويب تفاعلية، وهي منتشرة بشكل كبير ومُستعمَلة من أغلبية المواقع، وتدعمها جميع المتصفحات تقريبًا دون الحاجة إلى إضافات خارجية. يشرح هذا المقال مزايا لغة JavaScript، وما يمكنك تحقيقه باستخدامها، وأفضل التقنيات التي تعمل معها بشكل جيد. ما هي لغة JavaScript؟ ظهرت لغة JavaScript لإنشاء صفحات ويب حيوية وتفاعلية. تسمى البرامج المكتوبة بلغة JavaScript بالسكربتات (scripts)، ويمكن كتابتها بشكل مباشر ضمن شيفرة HTML لصفحة الويب ليتم تنفيذها تلقائيًا عند تحميل الصفحة. ولا تحتاج هذه السكربتات إلى تحضير خاص أو تصريف مسبق وإنما تتم كتابتها ثم تنفيذها كنص عادي. هذا الأمر يميز لغة JavaScript عن لغة برمجية أخرى تشبهها بالاسم تدعى Java. لماذا سميت JavaScript؟ عندما ظهرت لغة JavaScript، كان لها اسم مختلف وهو «LiveScript» ولكن الشعبية الكبيرة للغة Java في تلك الفترة دفعت إلى تغيير اسم اللغة إلى JavaScript بهدف إظهارها بصورة «الأخ الصغير» للغة Java واكتساب بعض الشهرة منها. ومع تطور لغة JavaScript، أصبحت لغةً مستقلة بشكل كامل ولها مواصفات ومعايير خاصة بها تُسمى «ECMAScript»، ولم يعد لها أي علاقة بلغة Java. في الوقت الحالي لا يقتصر تنفيذ لغة JavaScript على المتصفح وإنما من الممكن تنفيذها ضمن الخادوم أيضًا، أو أي جهاز يحتوي على برنامج خاص يسمى محرك JavaScript. يحتوي المتصفح على محرك مدمج ضمنه عادةً ما يسمى بآلة JavaScript الافتراضية وله أسماء تختلف باختلاف المتصفحات. فمثلًا يسمى المحرك باسم: V8 في متصفحي Chrome و Opera وباسم SpiderMonkey في المتصفح FireFox. Trident و Chakra في إصدارات مختلفة من IE، ويسمى ChakraCore في MicroSoftEdge، ويسمى Nitro و SquirrelFish في متصفح Safari …إلخ. احفظ هذه الأسماء في ذهنك جيدًا لأنها مستخدمة بشكل كبير في مقالات المطورين عبر الإنترنت وسنأتي على ذكرها لاحقًا في مقالاتنا. إذا قرأت مثلًا الجملة التالية: «الخاصية X مدعومة من قبل V8»، فيجب أن تعرف أنَ هذه الخاصية تعمل على الأغلب ضمن المتصفحين Opera و Chrome. كيف تعمل محركات JavaScript؟ طريقة عمل المحركات معقدَة وتتألف من ثلاث مراحل أساسية: بدايةً، يقرأ المحرك (إذا كان التنفيذ يتم عن طريق المتصفح فالمحرك مدمج ضمنه) أو يفسر (بمعنى أدق) السكربت، ثم يحول السكربت إلى لغة الآلة (عملية التصريف)، ثم تُنفَّذ شيفرات الآلة بشكل سريع. يحسّن المحرك كل مرحلة من هذه العملية عن طريق مراقبة السكربت المترجم أثناء تنفيذه، وتحليل تدفق البيانات وفق السكربت ثم يقوم بتحسين شيفرة الآلة الناتجة وفقًا للمعرفة التي قام باكتسابها مما يساعد على زيادة سرعة تنفيذ السكربت. ما الذي يمكن أن تفعله لغة JavaScript ضمن المتصفح؟ لغة JavaScript بإصدارها الحالي هي لغة برمجية «آمنة». ويعود ذلك إلى توجيه هذه اللغة للعمل ضمن المتصفحات لذلك فهي لا تحتوي على تعليمات للوصول إلى طبقة أدنى من العتاد مثل المعالج أو الذاكرة لأن عملها لا يتطلب ذلك. وتعتمد امكانيات لغة JavaScript بشكل كبير على البيئة التي تعمل ضمنها؛ فمثلًا تدعم بيئة Node.Js (التي تنفِّذ شيفرة JavaScript خارج بيئة المتصفح) التوابع التي تسمح بالقراءة من أو الكتابة على الملفات، وتنفِّذ طلبات الشبكة، وغيرها من المهام التي لا يمكن تنفيذها في بيئة المتصفح. أما ضمن المتصفح، فيمكن للغة JavaScript القيام بجميع الأعمال المتعلقة بمعالجة صفحات الويب، والتحكم بالتفاعل بين المستخدم وخادم الويب. يمكن للغة JavaScript ضمن المتصفح تنفيذ العديد من المهام نذكر منها: إضافة عناصر HTML جديدة إلى الصفحة، وتغيير المحتوى الحالي لها، وتعديل التنسيقات. التفاعل مع المستخدم وتنفيذ أعمال معينة عند النقر على الفأرة وتحريك المؤشر والضغط على أزرار لوحة المفاتيح. إرسال الطلبات عبر الإنترنت للخوادم البعيدة، بالإضافة إلى تحميل وتنزيل الملفات (كما في تقنية AJAX و COMET). الحصول على معلومات من «ملفات تعريف الارتباط» (Cookies، أو الكعكات) والتعديل عليها، وطرح الأسئلة على زوار الموقع وإظهار الرسائل والاشعارات. يمكن استخدامها لتخزين البيانات من جهة المستخدم أي ذاكرةً محليةً. ما المهام التي لا يمكن للغة JavaScript القيام بها ضمن المتصفح؟ الحد من إمكانيات JavaScript ضمن المتصفح أمرٌ بالغ الضرورة وذلك لضمان أمان المستخدم. أي أن الهدف من ذلك هو منع صفحات الويب الخطيرة من الوصول إلى معلومات سرية أو التلاعب ببيانات المستخدم ...إلخ. يوجد العديد من القيود على عمل JavaScript ضمن المتصفح نذكر منها: لا يمكن للغة JavaScript ضمن المتصفح قراءة الملفات الموجودة على القرص الصلب للمستخدم، أو نسخها، أو التعديل عليها. كما لا يمكنها تشغيل البرامج على جهاز المستخدم أو الوصول إلى ملفات نظام التشغيل. تسمح المتصفحات في الوقت الحالي للغة JavaScript بالتعامل مع الملفات في حالة سماح المستخدم بذلك. مثال على ذلك هو تحميل المستخدم ملفًا ما إلى موقع ويب طلب ذلك إما عبر السحب والإفلات في مربع محدَّد أو اختيار ملف معين في الوسم <input>. وتوجد طرق تتيح التعامل مع الأجهزة الملحقة مثل الكاميرا أو مسجل الصوت ولكنها تتطلب تقديم صلاحيات صريحة من قبل المستخدم وأخذ موافقته على أداء مهمة معينة فقط. بمعنى أن الصفحة التي جرى تفعيل JavaScript لا يعني بالضرورة تفعيل كاميرات الويب والوصول إليها الأمر الذي يتيح مراقبة المستخدم ومحيطه وإرسال المعلومات عبر الإنترنت إلى وكالات الأمن القومي كما يظن البعض. في الحالة العامة، الألسن والنوافذ المختلفة في المتصفح لا يمكنها تبادل البيانات فيما بينها أو معرفة أية تفاصيل عن بعضها بعضًا إلا عندما تستخدم صفحة JavaScript لفتح صفحة أخرى؛ وحتى في هذه الحالة، لا يمكن لصفحة تبادل البيانات مع صفحة فتحتها إذا كانت هاتين الصفحتين من موقعين مختلفين (نطاق مختلف، برتوكول أو منفذ). يسمى ذلك «سياسة المصدر الواحد» ويمكن تجاوزها عند الحاجة ولكن يجب أن تحتوي كلا الصفحتين على سكربت JavaScript خاص يتحكم بتبادل البيانات بينهما. هذه المحدودية - مرةً أخرى - ضرورية لضمان أمان المستخدم. فلا يجب على أي صفحة من الموقع http://anysite.com مثلًا والتي فتحها المستخدم أن يكون باستطاعتها الوصول وسرقة بيانات صفحة أخرى من الموقع http://gmail.com مثلًا مفتوحة في لسان آخر من نفس المتصفح. بإمكان لغة JavaScript تبادل البيانات عبر الإنترنت بسهولة بين صفحة الويب والخادم الخاص بها ولكن إمكانيتها على استقبال البيانات من مواقع ونطاقات أخرى محدودة إلا في حالة السماح بذلك بتصريح صريح (يُذكَر ضمن ترويسة HTTP) للطرف الآخر البعيد. هذه المحدودية مرةً أخرى ضرورية لأمان المستخدم. يجب التنويه أنَ المحدوديات السابقة توجد فقط عند استخدام لغة JavaScript ضمن المتصفح ولا توجد عند استخدامها ضمن بيئة مختلفة مثل الخادم. كما أنّ المتصفحات في الوقت الحالي توفر ملحقات/إضافات تسأل المستخدم عن الرغبة بإعطاء صلاحيات أكبر للغة JavaScript المنفذة في المتصفح. ما المميز في لغة JavaScript؟ هنالك على الأقل ثلاثة أمور تميَز لغة JavaScript وهي: الاندماج الكامل مع HTML و CSS. سهولة تنفيذ الأمور البسيطة. مدعومة من قبل أغلبية المتصفحات ومفعًلة تلقائيًا. لغة JavaScript هي اللغة الوحيدة التي تتوفر فيها هذه المزايا الثلاث. وهذا ما يجعلها الأداة الأكثر انتشارًا لبناء صفحات الويب. قبل البدء بتعلم لغة JavaScript، من المفيد الاطلاع على الآفاق المستقبلية لها والتقنيات الحديثة التي تنافسها من لغات برمجية جديدة وتحديثات في عمل المتصفحات. اللغات المعتمدة على لغة JavaScript لا تناسب قواعد كتابة لغة JavaScript الجميع. ويختلف المطورون في حاجاتهم إلى مزايا مختلفة، وهذا بالطبع أمر متوقع لاختلاف مشاريعهم ومتطلباتهم. لذلك، ظهرت مؤخرًا مجموعة من اللغات البرمجية الجديدة والتي تُحوَّل (transpiled) إلى لغة JavaScript قبل تنفيذها في المتصفح. ومع تطور هذه الأدوات، أصبحت عملية التحويل (transpilation) سريعة. الأمر الذي سمح للمطورين بكتابة الشيفرات بلغة برمجية أخرى ليتم تحويلها تلقائيًا دون أي اختلاف إلى لغة JavaScript وكأنها مكتوبة من الأصل بهذه اللغة. بعض الأمثلة عن مثل هذه اللغات هي: CoffeeScript: تعدُّ لغةً ذات صياغة تجميلية للغة JavaScript. توفر صياغة أقصر مما يسمح بكتابة شيفرة برمجية أوضح وأكثر دقةً. لها شعبية بين مطوري لغة Ruby . TypeScript: تركز على إضافة تعريف لأنواع المعطيات لتسهيل دعم وتطوير الأنظمة المعقدة. طُوِّرت هذه اللغة من قبل شركة Microsoft. Dart: هي لغة مستقلة ولها محرك خاص وتعمل على بيئات مختلفة غير المتصفح (مثل تطبيقات الموبايل). قدمتها شركة Google كبديل عن لغة JavaScript، ولكن تعمل المتصفحات في الوقت الحالي على تحويلها إلى لغة JavaScript كما هو حال اللغات السابقة. هنالك المزيد من اللغات ولكن تعلمك واستخدامك لإحداها لا يلغي ضرورة تعلمك للغة JavaScript، إذ يجب تعلم وفهم هذه اللغة لمعرفة كيفية تنفيذ العمليات والوظائف المختلفة بشكل فعلي. على أي حال، تبقى لغة JavaScript هي الأشهر والأقوى والأسرع تطورًا بين اللغات الشبيهة والمنافسة لها. الخلاصة أُوجدت لغة JavaScript في البداية كلغة يتم تنفيذها فقط من قبل المتصفح ثم تطورت وأصبح من الممكن تنفيذها ضمن بيئات مختلفة عبر Node.js. اليوم، تملك لغة JavaScript مكانة كبيرة وخاصة بسبب دعمها من قبل معظم المتصفحات واندماجها الكامل مع HTML و CSS. هنالك العديد من اللغات التي تُحول إلى JavaScript وتوفر مزايا خاصة. لذا ننصحك بالاطلاع عليها ولو بشكل سريع بعد تمكنك من لغة JavaScript. ترجمة -وبتصرف- للفصل An Introduction to JavaScript من كتاب The JavaScript Language انظر أيضًا المقال التالي: أدوات المطور محررات الشيفرة البرمجية كامل مقالات دليل تعلم جافاسكربت
  21. أبقِ في بالك أن الشيفرة البرمجية عرضةً لاحتواء الكثير من الأخطاء، إذ احتمال ارتكابك الأخطاء كبير أثناء كتابة الشيفرة البرمجية، لا بل وقوعك فيها هو أمر حتمي طالما أنك إنسان ولست رجلًا آليًا. هل تصدق أنه حتى المبرمجين المتمرسين وذوي الخبرة الطويلة يرتكبون الكثير من الأخطاء في الشيفرة التي يكتبونها! لا تقلق فهذا أمر طبيعي. على أي حال، لا تُظهِر المتصفِّحات الأخطاء البرمجية تلقائيًا للمستخدمين. وعند وجود مشكلة ما في السكربت، ولا يمكنك آنذاك تحديد مكانها، وبالتالي لا يمكن إصلاحها. لذلك، أُضيفت «أدوات المطوّر» (developer tools) إلى المتصفِّحات لاسكتشاف الأخطاء وتوفير معلومات مفيدة عن السكربت لتحسينه. يفضل معظم المطورون العمل على متصفِّحي Chrome أو FireFox لاحتوائهما على أفضل أدوات المطوّر. توفر المتصفِّحات الأخرى أيضًا مجموعة أدوات للمطوّر والتي من الممكن أن تحتوي على مزايا خاصة. لكن عادةً ما تحاول اللحاق بمتصفِّحي Chrome و FireFox الأفضل من هذه الناحية. يفضل المطوّرون بشكل عام العمل على متصفِّح واحد وينتقلون إلى متصفِّح آخر عندما تكون المشكلة التي يعملون عليها محدَّدة بهذا المتصفِّح. بناءً على ذلك، نجد أنَّ أدوات المطور مهمة للغاية لما تمتلكه من مزايا تساعدك أثناء مسيرتك في تطوير الويب عبر JavaScript. سنتعلم في البداية طريقة فتحها، واستخدامها لاستكشاف الأخطاء، وتشغيل تعليمات JavaScript ضمنها. أدوات المطور في متصفح Google Chrome قم بفتح الصفحة bug.html. يوجد خطأ في الشيفرة المكتوبة بلغة JavaScript غير ظاهر للزائر العادي، لذا سنستخدم أدوات المطوّر لاكتشافه. اضغط على F12 (أو الاختصار Cmd+Opt+J إذا كنت تستخدم نظام التشغيل «ماك») وسيفتح ذلك تلقائيًا أدوات المطوّر على لسان «الطرفية» (Console). وتظهر أدوات المطوّر تقريبًا بهذا الشكل: يعتمد شكل أدوات المطوّر على إصدار متصفِّح Chrome الذي تستخدمه، إذ يختلف بشكل بسيط من إصدار إلى آخر. بإمكانك رؤية رسالة الخطأ باللون الأحمر. معنى الرسالة أن السكربت يحتوي على أمر غير معروف هو "lalala". لاحظ في أقصى اليمين وجود رابط إلى المصدر bug.html:12 مع رقم سطر الخطأ في الشيفرة. يظهر تحت رسالة الخطأ الرمز < باللون الأزرق، ويحدد «سطر الأوامر» (command line) الذي سنكتب عنده أوامر وتعليمات JavaScript. اضغط زر الإدخال Enter لتنفيذ الأمر بعد كتابته (أو Shift+Enter للانتقال إلى السطر التالي عند إدخال أمر متعدد الأسطر). الآن أصبح بإمكانك استكشاف الأخطاء، وهذا يكفي كبداية وسنعود لاحقًا إلى أدوات المطوَر لندرس استكشاف الأخطاء وإصلاحها أو ما يعرف debugging في فصل تنقيح الأخطاء في المتصفِّح Chrome. أدوات المطور في متصفحي FireFox و Safari وغيرهما تَستخدِم معظم المتصفِّحات الاختصار F12 لفتح أدوات المطوَر. بشكل عام تتشابه هذه الأدوات في الشكل والمضمون. وبمجرد تعلمك العمل على إحدى هذه الأدوات (بإمكانك البدء مع Chrome)، يمكنك بسهولة الانتقال للعمل على الأدوات الأخرى. أدوات المطور في متصفح Safari يختلف المتصفِّح Safari (متصفِّح خاص بنظام التشغيل «ماك» وغير مدعوم من قبل «ويندوز» أو «لينكس») في طريقة فتح أدوات المطور. نحتاج في البداية لتفعيل « قائمة المطوّر» (Develop menu). لفعل ذلك، افتح «التفضيلات» (Preferences) ثم اختر قائمة «متقدم» (Advanced). يوجد في الأسفل مربع اختيار لإظهار قائمة المطوَر في شريط القائمة. حدده لتفعيل القائمة: بإمكانك استعمال الاختصار Cmd+opt+C لإظهار وإخفاء الطرفية من أدوات المطور. ويمكنك ملاحظة ظهور قائمة جديدة في الأعلى اسمها «Develop». تملك هذه القائمة العديد من الخيارات والأوامر الخاصة بالمطور. الإدخال متعدد الأسطر عادةً يتم تنفيذ الشيفرة البرمجية في نافذة الأوامر سطرًا تلو الآخر عند الضغط على زر الإدخال Enter. ولكتابة أمر متعدد الأسطر، اضغط على Shift+Enter (يُمكِّنك ذلك من الانتقال إلى السطر التالي دون تنفيذ السطر الحالي، وعند الانتهاء من إدخال كامل الأمر، اضغط على Enter فقط لتنفيذ هذا الأمر المتعدد الأسطر). الخلاصة توفِّر أدوات المطوّر لك عدة أدوات متطورة تساعدك على تنقيح الأخطاء، وتنفيذ الأوامر، وفحص المتغيرات، وغيرها من المهام. يمكن الوصول إليها باستخدام الاختصار F12 في معظم متصفِّحات نظام Windows. أمَا في نظام Mac، استخدم الاختصار Cmd+Opt+J لمتصفِّح Chrome، واستخدم Cmd+Opt+C لمتصفِّح Safari (ولكن عليك أن تفعَل قائمة المطوّر في البداية). الآن أصبحت البيئة جاهزة لديك. استعد للتعمق في لغة JavaScript في الفصل التالي. ترجمة -وبتصرف- للفصل Developer Console من كتاب The JavaScript Language انظر أيضًا المقال التالي: محررات الشيفرة البرمجية المقال السابق: مقدمة إلى لغة JavaScript كامل مقالات دليل تعلم جافاسكربت