سجى الحاج
الأعضاء-
المساهمات
13 -
تاريخ الانضمام
-
تاريخ آخر زيارة
إنجازات سجى الحاج
عضو مساهم (2/3)
15
السمعة بالموقع
-
يلخِّص هذا الدرس مُميِّزات 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 المقال السابق: تعابير الدوال والدوال السهمية
-
الدالة ليست بنية سحرية تُبنَى بها الشيفرة في 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
-
قد تحتاج أحيانًا إلى تنفيذ إجراء مماثل في أكثر من موضع في السكربت مثل عرض رسالةٍ جميلةٍ للمستخدم عند تسجيل الدخول وتسجيل الخروج وربما في مكان آخر أيضًا. الدوال (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
-
يمكن للتعليمة 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
-
نحتاج في بعض الأحيان لتكرار إجراء ما مثل إخراج قيم من قائمة واحدة تلو الأخرى أو تشغيل نفس الشيفرة للأعداد من 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 المقال السابق: المعاملات المنطقية
-
هناك ثلاثة معاملات منطقية في 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 المقال السابق: المعاملات الشرطية
-
نحتاج في بعض الأحيان إلى تنفيذ إجراءات مختلفة بناءً على شروط مختلفة. للقيام بذلك، يمكنك استخدام التعبير الشرطي 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
-
في هذا الجزء من المقالات التعليمية، نتطرَّق إلى لغة 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 اقرأ أيضًا المقال التالي: المعاملات الشرطية المقال السابق: معاملات الموازنة كامل مقالات دليل تعلم جافاسكربت
-
لا شك أنَّك تعرَّفت مسبقًا على معاملات الموازنة من أحد دروس الرياضيات في المدرسة وهي: أكبر/أصغر من: 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 كامل مقالات دليل تعلم جافاسكربت
-
تعرَّفنا على العديد من المعاملات الحسابية في المدرسة، مثل الجمع +، الضرب *، الطرح – وما إلى ذلك. في هذا المقال، سنشرح معاملات أخرى لم نتعرف عليها في المدرسة بالإضافة إلى تلك المعاملات. مصطلحات جديدة: أحادي، ثنائي، عامل قبل أن نمضي قدمًا في هذا المقال، دعنا نتعرَّف على بعض المصطلحات الجديدة. العوامل (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 انظر أيضًا المقال التالي: معاملات الموازنة المقال السابق: التحويل بين الأنواع كامل مقالات دليل تعلم جافاسكربت
-
هذا المقال تمت كتابته بتعاون بين Joe Richardson و Robin Rendle ومجموعة من فريق موقع CSS-Tricks. أراد Joe إضافة مقال بخصوص BEM التي أحببناها وكان لدى كل منا أفكارًا وآراءً حولها، لذلك قررنا أن نتعاون سويًا لكتابة هذا المقال. تُعدُّ منهجية BEM (وهي اختصار لهذه الكلمات معًا Block و Element و Modifier) مصطلح تسمية شائع يستخدم من أجل الأصناف في HTML و CSS تم تطويره بواسطة فريق في Yandex؛ هدفها هو مساعدة المطورين في فهم العلاقة بين HTML و CSS في مشروع معين بشكل أفضل. فيما يلي مثال حول ما يكتبه مطور CSS في نمط BEM: /* مكون كتلة */ .btn {} /* عنصر يعتمد على كتلة */ .btn__price {} /* مُعدِّل يعدِّل تنسيق الكتلة */ .btn--orange {} .btn--big {} في شيفرة CSS السابقة، الكتلة (Block) عبارة عن عنصر جديد في أعلى مستوى من التجريد (top-level abstraction) على سبيل المثال زر: .btn{}. يجب اعتبار الجملة البرمجية هذه بمثابة أحد الأبوين، إذ يمكن وضع العناصر الأبناء (elements) في الداخل ويتم الإشارة إليها بواسطة شرطتين سفليتين يتبع اسم الكتلة أو الحاوية البرمجية تلك مثل .btn__price{}. أخيرًا، يمكن للمُعدلات (modifiers) معالجة الحاوية البرمجية (Block) بحيث يمكننا تمييز أو تصميم ذلك المكون المحدد دون إحداث تغييرات على بقية الحاويات البرمجية الأخرى يتم ذلك عن طريق إلحاق شرطتين باسم الحاوية البرمجية Block تمامًا مثل btn--orange. تبدو الشيفرة كما في المثال التالي: <a class="btn btn--big btn--orange" href="http://css-tricks.com"> <span class="btn__price">$9.99</span> <span class="btn__text">Subscribe</span> </a> لو كتب مطور آخر هذه الشيفرة ولم يكن لدينا خبرة في CSS، فيجب أن نأخذ فكرة جيدة عن أي الأصناف ومسؤوليتها وكيف تعتمد على بعضها بعضًا. يستطيع المطورون آنذاك بناء مكوناتهم الخاصة وتعديل الحاويات (الكتل) الموجودة إلى المحتوى الذي يريدونه. يحتمل بدون كتاب مثل شيفرة CSS هذه أن يتمكن المطورون من كتابة عدة مجموعات مختلفة من الأزرار ببساطة عبر تعيير صنف في الشيفرة: في البداية، قد تبدو هذه الصياغة أبطأ من بناء صنف جديد لكل نوع من أنواع الأزرار، ولكن هذا غير صحيح لعدة أسباب سنذكرها. لماذا علينا أخذ BEM في الحسبان؟ إذا كنا نريد إنشاء تصميم جديد لعنصر معين، يمكننا أولًا النظر في الحاويات البرمجية لمعرفة أي المُعدِّلات، والعناصرالفرعية موجودة بالفعل. ربما ندرك أننا لسنا بحاجة لكتابة أي تعبير CSS برمجي آخر لأن هناك معدّل موجود مسبقًا يفي بالغرض. إذا كنا نقرأ الوسوم بدلاً من تعابير CSS البرمجية، يجب أن نكون قادرين على الحصول بسرعة على فكرة حول العنصر الذي يعتمد على عنصر آخر؛ في المثال السابق، يمكننا رؤية أن العنصر .btn__price يعتمد على العنصر .btn، حتى لو لم نكن على دراية بعمل أي من تلك العناصر. يمكن للمصممين والمطورين تسمية العناصر لتسهيل الاتصال بين فريق المطورين بمعنى آخر، يوفر BEM لكل مطور في المشروع تسمية صيغة إعلانية للعناصر يمكنه مشاركتها بحيث تكون في نفس الصفحة. حدد Harry Roberts فائدة رئيسية أخرى لاستخدام صيغة برمجية مثل BEM حين كتب التالي عن تحسين ثقة المطور: "هذا هو السبب الرئيسي الذي يجعلنا ننتهي من قواعد الشفرة المتضخمة المليئة بشيفرات برمجية قديمة وغير قابلة للتعديل من CSS. نحن نفتقر إلى الثقة في أن نكون قادرين على العمل مع الأنماط الحالية وتعديلها لأننا نخشى عواقب أن تعمل CSS عالميًا، إذ هي مشهورة بحد ذاتها. تتلخص كل المشكلات تقريبًا المتعلقة بـ CSS على نطاق واسع في الثقة (أو عدم وجودها)، لذلك لا يقومون بإجراء تغييرات لأنهم لا يعرفون ما هي الآثار لهذه التغييرات على المدى البعيد." وبالمثل، يناقش Philip Walton حل لهذه المشكلة وهو إلتزام عدد كاف من المطورين بمبادئ BEM: "على الرغم من أن الشيفرة القابلة للتنبؤ بنسبة 100٪ غير متوافرة، من المهم فهم المفاضلات التي تجريها مع الطريقة المتبعة في كتابة التعابير البرمجية التي تختارها. إذا كنت تتبع طريقة BEM الصارمة، فستكون قادرًا على تحديث تعابير CSS وإضافة الشيفرات البرمجية الخاصة بك إليها في المستقبل بثقة تامة بأن هذه التغيرات لن يكون لها أي آثار جانبية." لذلك، إذا كان بإمكان المطورين العمل في مشروع بطريقة أكثر ثقة، فإنهم على يقين من اتخاذ قرارات أكثر ذكاءً حول كيفية استخدام هذه العناصر المرئية. قد لا تكون هذه الطريقة حلًا مثاليًا لجميع هذه المشاكل، لكنها بالتأكيد تمنح المطورين معيارًا لكتابة جمل برمجية أفضل وأكثر قابلية للصيانة في المستقبل. هناك جانب آخر جيد في منهجية BEM، وهو أن كل مجموعة جمل برمجية خاصة بعنصر معين توجد داخل حاوية خاصة به ولا يوجد شيء متداخل مما يجعل خصوصية CSS سلسة جدًا ومنخفضة وهذا هو المطلوب. أي أنك لن تجهد نفسك فيما يتعلق بخصوصية تعابير CSS البرمجية. دعونا نلقي نظرة على بعض المشاكل مع BEM. مشاكل مع BEM CSS لن يقوم أحد بمعاقبتك بالطبع إذا خرجت عن قواعد BEM. لا يزال بإمكانك كتابة محدد CSS كالتالي: .nav .nav__listItem .btn--orange { background-color: green; } يبدو أن المثال السابق يحتوي أجزاء من منهجية BEM، لكنه ليس كذلك فهو يحتوي على محددات متداخلة، ولا يصف المُعدِّل (modifier) بدقة ما يحصل في هذه الجمل البرمجية. إذا فعلنا ذلك، فسنكون فشلنا في تحقيق مبدأ الخصوصية وهذا مفيد جدا لمنهجية BEM. يجب ألا تَبطل كتلةٌ (مثل .nav) عمل كتلة أخرى أو معدل آخر (مثل .btn --orange) وإلا فإن هذا سيجعل من المستحيل تقريبًا قراءة ملف HTML وفهم عمل هذا العنصر. في هذه العملية، نحن ملزمون الى حد كبير أن نكون أهلًا لثقة مطور آخر في البرنامج، هذا ينطبق على HTML أيضًا. ماذا تتوقع إذا رأيت الشيفرة التالية؟ <a class="btn" href="http://css-tricks.com"> <div class="nav__listItem">Item one</div> <div class="nav__listItem">Item two</div> </a> ربما ما يحدث في المثال السابق، هو أن عنصرًا في الحاوية البرمجية block يشتمل على الشيفرة التي يحتاجها المطور، لكن العناصر الأبناء لا تحتاج إلى الصنف .nav على أنه عنصر أب. هذه المشكلة تجعل البرنامج استثنائيًّا وغير متسق ومتناقض مع نفسه وينبغي تجنبها بأي ثمن. لذلك، يمكننا تلخيص هذه المشكلات في: عدم استخدام المُعدلات في الحاويات البرمجية block غير المترابطة. تجنب صنع عناصر رئيسية غير ضرورية عندما يكون العنصر الفرعي موجودًا بشكل جيد بدون أي مشاكل. المزيد من الأمثلة العملية على BEM قائمة قابلة للطي: في هذا المثال، هناك حاوية برمجية block واحدة وعنصران ومُعدِّل واحد. يمكننا هنا إنشاء مُعدِّل مثل .accordion__copy-open يتيح لنا معرفة أنه يجب ألا نستخدمه في حاوية برمجية block أو عنصر آخر. قائمة تنقل: يحتوي هذا المثال على كتلة block واحدة و 6 عناصر ومُعدِّل واحد. من الجيد تمامًا إنشاء حاويات برمجية blocks بدون معدلات على الإطلاق. يمكن للمطور في مرحلة ما في المستقبل أن يربط هذه الحاوية بمعدلات جديدة طالما بقيت ثابتة. عيوب BEM ربما لا تحب استخدام شرطة مزدوجة. حسنًا، استخدم شيئًا آخر فريدًا تستخدمه باستمرار. هنا رأي آخر: هذه الثلاثة محددات الأخيرة جميعها لها مستويات خصوصية مختلفة. تحتمل أن يكون لها عنصر رئيسي أو لا بدون أي قواعد معمول بها، هل من الممكن أن يكون هذا المثال الصغير جيد بالنسبة لك؟ ربما. ولكن كلما زاد عدد تعابير CSS البرمجية في المشروع، زاد عدد الأشياء الصغيرة مثل هذه، وبالتالي زادت مشاكل الخصوصية والتعقيد. ليس بالضرورة اختيار رأي صموئيل هنا، ولكن آراءه شاركها الكثير من الناس لذلك فهو مثال جيد. بخصوص من يرفضون BEM تمامًا فلا بأس بذلك، لكنني أعتقد أنه سيكون من الصعب القول بأن وجود مجموعة من القواعد التي تساعد في الفهم والحفاظ على CSS قابل للتعديل هو فكرة سيئة. في منهجية SMACSS، من المحتمل أن تجد اسم صنف CSS متكون من ثلاثة أحرف. تتبع المُعدِّلات بعدئذٍ اسم الوحدة النمطية باستخدام شرطة: /* مثال عن وحدة */ .btn { } /* btn معدِّل الصنف */ .btn-primary { } /* مع حالة Btn الوحدة */ .btn.is-collapsed { } هذه مجرد طريقة تسمية مختلفة لنفس المشكلة. إنه مشابه إلى حد ما، لكن تكون أكثر تحديدًا بشأن التبعيات والحفاظ على خصوصية التفاصيل. في OOCSS، الحاويات البرمجية هي عامة بالغالب. /* مثال عن وحدة */ .mod { } /* جزء من الوحدة */ .inner { } /* Talk الوحدة */ .talk { } /* تغيير جزء داخل الوحدة */ .talk .inner { } لذلك، يمكنك استخدام أصناف متعددة في HTML للحالات المختلفة. لم يتم تسمية الجزء الداخلي مثل التابع له، لذلك فهو أقل وضوحًا ولكنه قابل لإعادة استخدامه. ستقوم BEM بعمل .mod__inner و .mod--talk و mod-talk__inner.. هذه مجرد اختلافات في المنهجية. تذكر أن لا أحد يجبرك على استخدامها، فهذه قواعد مفروضة ذاتيًا حيث تأتي القيمة من متابعتها. BEM و Sass لأولئك الذين يستخدمون Sass ويستمتعون بتشعيب (nesting) العناصر كوسيلة لتحديد النطاقات لتنسيقات، لازال بإمكانك أن تكون المسؤول عن الصيغة المتشعبة ولكن يمكنك الحصول على CSS غير متشعب، باستخدام @at-root: ملف Scss: .block { @at-root #{&}__element { } @at-root #{&}--modifier { } } يولد ملف CSS التالي: .block { } .block__element { } .block--modifier { } ويمكنك الحصول على ملخص كما تريد! تحقق من منشئ BEM الذي يخص Danield Guillan أو Anders Schmidt Hansen من أجل BEM التعبيرية. الخلاصة أعتقد أنه من الإنصاف القول إنه على الرغم من أن BEM لن يحل جميع مشاكلنا، إلا أنه مفيد بشكل كبير في بناء واجهات قابلة للتطوير والصيانة حيث يجب أن يكون لدى كل فرد في الفريق فكرة واضحة عن كيفية تطوير تلك الأشياء. ذلك لأن الكثير من التطوير في الواجهة الأمامية لا يتعلق فقط بالخدع اللطيفة التي تحل مشكلة صغيرة واحدة على المدى القصير؛ نحتاج إلى اتفاقات وعقود ملزمة بين المطورين بحيث يمكن لبرنامجنا التكيف مع مرور الوقت وعلى المدى البعيد. بشكل عام، أود أن أفكر في BEM كإجابة على سؤال نيكولاس غالاغر: ترجمة -وبتصرف- للمقال BEM 101 لصاحبها Robin Rendle.
-
المرة الأولى التي صنعت فيها صورة متجاوبة (responsive)، كانت بسيطة مثل ترميز هذه الأسطر الأربعة. img { max-width: 100%; height auto; /* default */ } رغم أن ذلك كان مناسبًا لي كمطور، إلا أنه لم يكن الأفضل للمستخدمين. ماذا يحدث إذا كانت الصورة في الخاصية src ثقيلة؟ على الأجهزة المتطورة (مثل الجهاز الذي يحتوي على ذاكرة وصول عشوائي بسعة 16 جيجابايت)، قد تحدث مشكلة بسيطة في الأداء أو لا تحدث، ولكن ماذا عن الأجهزة متدنية الأداء؟ إنها قضية أخرى. الرسم التوضيحي أعلاه غير مفصل بما فيه الكفاية. أنا من نيجيريا، وإذا كان منتجك يعمل في إفريقيا فعليك ألا تنظر إلى ذلك الرسم، انظر إلى هذا الرسم البياني بدلًا من ذلك. في الوقت الحاضر، iPhone الأقل سعرًا يُباع في المتوسط بسعر 300 دولار أمريكي، ولا يستطيع المواطن الأفريقي العادي تحمل تكاليفه على الرغم من أن iPhone يمثل عتبة لقياس الأجهزة السريعة. هذا هو كل تحليل الأعمال الذي تحتاجه لفهم أن نطاق CSS لا يقلل من الصور المتجاوبة. على حال، هل تعرف حقًا ما هي الصور؟ الفروق الدقيقة في الصور تُعدُّ الصور جاذبة للمستخدمين ولكنها تمثل تحديًا صعبًا للمطورين حيث يجب عليهم مراعاة النقاط التالية: الشكل حجم القرص تجسيد البعد (عرض المخطط والارتفاع في المتصفح) الأبعاد الأصلية (العرض والارتفاع الأصلي) نسبة العرض إلى الارتفاع لذا، كيف نختار المعاملات الصحيحة ونطبقها بمهارة لتقديم تجربة مثالية لجمهور المستخدمين؟ تعتمد الإجابة بدورها، على إجابات هذه الأسئلة: هل الصور التي تم إنشاؤها ديناميكيًا بواسطة المستخدم أم بشكل ثابت بواسطة فريق تصميم؟ إذا تم تغيير عرض الصورة وارتفاعها بشكل غير متناسب، فهل سيؤثر ذلك على جودة الصورة؟ هل يتم عرض جميع الصور عند تقديمها بنفس العرض والارتفاع؟ هل يجب أن يكون لديهم نسبة عرض إلى ارتفاع محددة أو نسبة مختلفة تمامًا؟ ما الذي يجب مراعاته عند تقديم الصور بطُرق عرض مختلفة؟ دون إجاباتك على هذه الأسئلة فهي لن تساعدك فقط في فهم صورك ومصادرها ومتطلباتها الفنية وما إلى ذلك، ولكن ستمكنك أيضًا من اتخاذ الخيارات الصحيحة في توفيرها وعرضها على المستخدم النهائي. الاستراتيجيات المشروطة لتوصيل الصور وعرضها على المستخدم تطور تسليم الصور من إضافة بسيطة لعناوين URL إلى خاصية src الخاصة بالصور إلى سيناريوهات معقدة. قبل الخوض فيها، دعنا نتحدث عن الخيارات المتعددة لتقديم الصور بحيث يمكنك ابتكار إستراتيجية حول كيفية تسليم الصور خاصتك وتصييرها وعرضها على المستخدم النهائي. أولاً، حدِّد مصادر الصور، إذ يمكن بهذه الطريقة تقليل عدد حالات الغموض واللبس ويمكنك معالجة الصور بأكبر قدر ممكن من الكفاءة. بشكل عام، الصور إما أن تكون: ديناميكية: يتم تحميل الصور الديناميكية من قبل المستخدمين، بعد أن تم إنشاؤها بواسطة أحداث أخرى في النظام. ثابتة: يتم إنشاء هذه الصور من قبل مصور، أو مصمم، أو أنت (المطور) لموقع الويب. دعنا نتعرف إلى إستراتيجية كلًا هذين النوعين من الصور. استراتيجية الصور الديناميكية الصور الثابتة سهلة الاستخدام إلى حد ما؛ من ناحية أخرى، الصور الديناميكية معقدة وعرضةً للمشاكل. ما الذي يمكن فعله للتخفيف من حدة طبيعتها الديناميكية وجعلها أكثر قابلية للتنبؤ بها مثل الصور الثابتة؟ هناك شيئان هما: التحقق من الصحة (validation) والاقتصاص الذكي للصور (intelligent cropping). التحقق من الصحة ضع بعض القواعد للمستخدمين حول ما هو مقبول وما هو غير مقبول. في الوقت الحاضر يمكننا التحقق من صحة جميع خصائص الصورة، هذه الخصائص تتلخص بالنقاط التالية: الصيغة حجم القرص الأبعاد نسبة العرض إلى الارتفاع ملاحظة: يتم تحديد حجم الصورة أثناء التصيير (rendering)، وبالتالي لا يتم التحقق من صحة الصور من جانبنا. بعد التحقق من الصحة، ستظهر مجموعة يمكن التنبؤ بها من الصور التي يسهل استخدامها. الاقتصاص الذكي للصور تتمثل الاستراتيجية الأخرى للتعامل مع الصور الديناميكية في اقتصاصها بذكاء لتجنب حذف محتوى مهم وإعادة التركيز على المحتوى الأساسي. هذا صعب ومع ذلك يمكنك الاستفادة من الذكاء الاصطناعي الذي توفره أدوات مفتوحة المصدر أو شركات SaaS التي تتخصص في إدارة الصور. بمجرد أن يتم الانتهاء من تنفيذ هذه الإستراتيجية للصور الديناميكية، أنشئ جدول قواعد به جميع خيارات التخطيط للصور. أدناه مثال يجدر بك النظر في تحليلاته لتحديد أهم الأجهزة وأحجام الإطار المعروض (viewport). الحد الأدنى (شبه الأمثل) الآن، ضع جانبًا تعقيدات الاستجابة وانظر للمثال التالي - شيفرة HTML بسيطة مع CSS ذا أقصى عرض. تعرض الشيفرة التالية بعض الصور: <main> <figure> <img src="https://res.cloudinary.com/...w700/ps4-slim.jpg" alt="PS4 Slim"> </figure> <figure> <img src="https://res.cloudinary.com/...w700/x-box-one-s.jpg" alt="X Box One S"> </figure> <!-- More images --> <figure> <img src="https://res.cloudinary.com/...w700/tv.jpg" alt="Tv"> </figure> </main> ملاحظة: علامة الحذف (…) التي تمثل استراتيجية الاقتصاص في عنوان URL الخاص بالصورة أو المجلد. يوجد الكثير من التفاصيل التي يجب تضمينها، لكن الاقتصاص للتركيز هو ما يهم الآن. للحصول على الإصدار الكامل، انظر مثال CodePen أدناه. هذه هي أقصر شيفرة CSS موجودة على الإنترنت تجعل الصور سريعة الاستجابة: /* الحاوية الأب */ main { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); } img { max-width: 100%; } إذا لم يكن للصور عرض وارتفاع متساويان، بدلِّ object-fit مكان max-width واضبط القيمة إلى cover. يشرح منشور مدونة Jo Franchetti عن التنسيقات الشائعة المتجاوبة مع تخطيط CSS كيف أن قيمة grid-template-columns تجعل التخطيط بأكمله متجاوبًا. ما سبق ليس ما نبحث عنه، لأن: حجم الصورة ووزنها متماثلان في كل من الأجهزة المتطورة وغير المتطورة. قد نرغب في أن نكون أكثر صرامة مع عرض الصورة بدلًا من تعيينها على 250 ويتغير حجمها فيما بعد. حسنًا، يغطي هذا القسم «الحد الأدنى»، لذلك هذا كل شيء. اختلافات التخطيط أسوأ شيء يمكن أن يحدث لتخطيط الصور هو سوء تدبير وإدارة التوقعات. نظرًا لأن الصور قد يكون لها أبعاد مختلفة (العرض والارتفاع)، يجب أن نحدِّد كيفية تقديم وعرض هذه الصور. هل يجب علينا اقتصاص جميع الصور بذكاء لتصبح ذات أبعاد منتظمة؟ هل يجب أن نحتفظ بنسبة العرض إلى الارتفاع لجهة العرض ونغير النسبة لشيء آخر؟ الخيار لنا. في حالة وجود صور في شبكة، مثل تلك الموجودة في المثال أعلاه مع نسب أبعاد مختلفة، يمكننا تطبيق تقنية الاتجاه الفني (art direction) لتصيير وعرض الصور، إذ يمكن أن يساعد في تحقيق شيء مثل هذا: انظر المثال أدناه: <main> <figure> <picture> <source media="(min-width: 900px)" srcset="https://res.cloudinary.com/.../c_fill,g_auto,h_1400,w_700/camera-lens.jpg"> <img src="https://res.cloudinary.com/.../c_fill,g_auto,h_700,w_700/camera-lens.jpg" alt="Camera lens"> </picture> </figure> <figure> <picture> <source media="(min-width: 700px)" srcset="https://res.cloudinary.com/.../c_fill,g_auto,h_1000,w_1000/ps4-pro.jpg"></source> </picture> <img src="https://res.cloudinary.com/.../c_fill,g_auto,h_700,w_700/ps4-pro.jpg" alt="PS4 Pro"> </figure> </main> بدلاً من تقديم صورة واحدة بعرض 700 بكسل، نعرضها بشكل 700 بكسل × 700 بكسل فقط في حال إذا تجاوز عرض منفذ العرض 700 بكسل. إذا كان حجم العرض أكبر، فسيحدث التالي: يتم تصيير صور عدسة الكاميرا كصورة عرضية تبلغ 700 بكسل وعرضها 1000 بكسل في الارتفاع (700 بكسل × 1000 بكسل). يتم عرض صور PS4 Pro بدقة 1000 بكسل × 1000 بكسل. الاتجاه الفني (Art direction) قد نحذف المحتوى الأساسي عن غير قصد عن طريق اقتصاص الصور لجعلها أكثر استجابة. كما ذُكر سابقًا، يمكن أن تساعد أدوات الذكاء الصنعي AI مفتوحة المصدر على الاقتصاص بذكاء وإعادة التركيز على الكائنات الأساسية للصور، بالإضافة إلى ذلك يعد منشور Nadav Soferman عن الاقتصاص الذكي بمثابة دليل إرشادي مفيد. شبكة دقيقة وممتدة (Strict grid and spanning) المثال الأول على الصور سريعة الاستجابة في هذا المقال هو مثال رائع. عند عرض 300 بكسل كحد أدنى، تتدفق عناصر الشبكة تلقائيًا إلى مكانها وفقًا لعرض الإطار المعروض من النافذة (viewport). من ناحية أخرى، قد نرغب في تطبيق قاعدة أكثر صرامة على عناصر الشبكة بناءً على مواصفات التصميم. تكون استعلامات الوسائط في هذه الحالة مفيدة. بدلاً من ذلك، يمكننا الاستفادة من قدرة الشبكة الممتدة grid-span لإنشاء عناصر شبكية ذات طول وعرض مختلف. @media(min-width: 700px) { main { display: grid; grid-template-columns: repeat(2, 1fr); } } @media(min-width: 900px) { main { display: grid; grid-template-columns: repeat(3, 1fr) } figure:nth-child(3) { grid-row: span 2; } figure:nth-child(4) { grid-column: span 2; grid-row: span 2; } } بالنسبة لصورة تبلغ مساحتها 1000 بكسل × 1000 بكسل على منفذ عرض واسع، يمكننا توسيعها لالتقاط خليتين من الشبكات في كل من الصف والعمود. يمكن أن تأخذ الصورة التي تتغير إلى اتجاه عمودي (700 بكسل × 1000 بكسل) على حجم أوسع من الإطار المعروض خليتين على التوالي. التحسين التصاعدي (Progressive optimization) التحسين غير المدروس ليس ذو فائدة كما لو أنه لم يحصل أي تحسين. لا تركز على التحسين دون تحديد القياسات المناسبة، ولا تقوم به إذا لم يتم دعمه بالبيانات اللازمة. ومع ذلك، يوجد مجال واسع للتحسين في الأمثلة المذكورة أعلاه. لقد بدأنا بالحد الأدنى، وأظهرنا لك بعض الحيل الرائعة، السؤال التالي الذي يجب طرحه هو: «إذا كانت الصفحة تحتوي على 20-100 صورة، فما مدى جودة تجربة المستخدم؟» فيما يلي الإجابة، يجب أن نضمن أنه في حالة وجود العديد من الصور للعرض أن يكون حجمها يناسب الجهاز الذي يعرضها. لتحقيق ذلك، نحتاج إلى تحديد عناوين URL لعدة صور بدلًا من صورة واحدة. سيختار المتصفح الصورة الأنسب لعرض الجهاز منها (الصورة ذات العرض الأمثل) وفقًا للمعايير. وتسمى هذه التقنية تبديل الدقة (resolution switching) في الصور سريعة الاستجابة. انظر مثال هذه الشيفرة: <img srcset="https://res.cloudinary.com/.../h_300,w_300/v1548054527/ps4.jpg 300w, https://res.cloudinary.com/.../h_700,w_700/v1548054527/ps4.jpg 700w, https://res.cloudinary.com/.../h_1000,w_1000/v1548054527/ps4.jpg 1000w" sizes="(max-width: 700px) 100vw, (max-width: 900px) 50vw, 33vw" src="https://res.cloudinary.com/.../h_700,w_700/v1548054527/ps4.jpg 700w" alt="PS4 Slim"> تشرح تغريدة هاري روبرتس بشكل حدسي ما يحدث: عندما جربت تقنية تبديل الدقة لأول مرة، شعرت بالارتباك وغردت ب: رفعت القبعة إلى جيسون جريجسبي للتوضيح الذي أدلى به. بفضل تقنية تبديل الدقة، إذا تم تغيير عرض نافذة المتصفح، فإنه يقوم بتنزيل الصورة الصحيحة المناسبة لحجم الإطار المعروض آنذاك من النافذة؛ وبالتالي، تُستخدَم الصور الصغيرة للهواتف الصغيرة (مناسبة لوحدة المعالجة المركزية وذاكرة الوصول العشوائي) والصور الأكبر لعرض نافذة أكبر. يوضح الجدول أعلاه أن المتصفح يقوم بتنزيل نفس الصورة (المستطيل الأزرق) بأحجام مختلفة للقرص (المستطيل الأحمر). يعد تطبيق Respp Image Breakpoints Generator من Cloudinary المجاني والمفتوح المصدر مفيدًا للغاية في تكييف صور موقع الويب مع أحجام شاشات متعددة. ومع ذلك في كثير من الحالات، سيكون إعدادات srcset و sizes وحدها كافية. الخلاصة تهدف هذه المقالة إلى توفير إرشادات بسيطة وفعالة لإعداد صور وتخطيطات سريعة الاستجابة في ضوء العديد من الخيارات المتاحة وأحيانًا المربكة. تعرّف على CSS Grid والاتجاه الفني (Art Direction) وتبديل الدقة (Resolution Switching) وستكون مبدعًا في وقت قصير في مجال الصور المتجاوب. استمر بالتعلم! ترجمة -وبتصرف- للمقال Planning for Responsive Images لصاحبه Chris Nwamba
- 1 تعليق
-
- صور متجاوبة
- تحسين الصور
-
(و 2 أكثر)
موسوم في:
-
سجى الحاج اشترك بالأكاديمية
-
أنا متأكد تمامًا من أنك تفهم أهمية قائمة التنقل في موقع الويب وارتباطها بمشاركة الموقع فيما يلي عشر نصائح لجعل تجربة المستخدم UX سلسلة وأكثر قابلية للاستخدام. تعد إمكانية التنقل داخل موقعك جزءًا مهمًا لأي موقع ويب من أيًا كان مجاله أو نشاطه التجاري. يُعد التنقل هذا هو الطريقة الوحيدة للتعرف على منتجاتك وخدماتك. لن يعمل محتوى التنقل الخاص بموقعك جيدًا مع جميع روابط الصفحات، إذ يجب أن تكون حذرًا في الشكل الذي سيظهر به تنقلك وما الذي سيتضمنه. إذا كنت مرتبكًا أو تبحث عن بعض الاقتراحات حول ما يجب مراعاته عند تصميم قائمة التنقل في الموقع، فهناك ثماني نقاط لمساعدتك! 1. التنقل الثابت يعد وجود شريط تنقل ثابت في أعلى صفحة الموقع مفيدًا جدًا للزائرين حيث يمنحهم سهولة حقيقة عند التنقل في موقع الويب الخاص بك. بفرض أن موقعك يحتوي شريط تنقل أفقي، فسيمرر زائر الموقع وصولًا لنهاية الصفحة ثم قد يريد بعد ذلك إلقاء نظرة على صفحة «حول الموقع» (About Our Website)! سيحتاج آنذاك إلى التمرير طويلًا للأعلى للعثور على رابط الصفحة المنشودة .القائمة الثابتة في أعلى الصفحة تساعدك على حل هذه المشكلة. يرجى التأكد من ظهور عناصر قائمة التنقل الأفقية في أعلى الصفحة كما هي على جميع أحجام الشاشة المختلفة. أنا أتحدث عن مختلف أحجام شاشات الكمبيوتر والحواسيب المحمولة .يساعدك توحيد التنقل في موقعك على توفير تجربة متسقة على جميع الأجهزة. 2. الاهتمام بعملية التنقل على نسخة الهاتف المحمول لا يمكنك تجاهل زائري الجوال لموقعك. وفي عصر تصميم مواقع الويب للهواتف المحمولة أولًا، كانت الأطر سريعة الاستجابة مثل bootstrap توجهك لإيلاء الاهتمام المناسب لنظام التنقل في الأجهزة المحمولة لموقع الويب الخاص بك. الجوانب السلبية الوحيدة لنظام التنقل في موقع الويب خاصتك عبر الأجهزة المحمولة هي أنهم لا يحصلون على دعم الحومان (hover). لذلك، إذا كنت قد كتبت استعلام الوسائط (media query) بطريقة تعتمد على التنقل مع تأثيرات الحومان (hover)، قد تبدو موحدة على الأجهزة المحمولة ولكنها لن تؤدي نفس العمل. في حين أن إطار العمل bootstrap لديه دعمًا جيدًا لنظام التنقل لكل من عرض الهاتف المحمول والحاسوب. مع ذلك فإنه يحتوي على أنماط جامدة. تأكد أثناء بنائك نظام التنقل الخاص بموقعك عدم مبالغتك في استخدام شيفرة JavaScript التي قد تعطي للمستخدمين تأثيرًا مرئيًا رائعًا ولكنها تسبب صعوبة لبرامج الزحف (crawlers) لمثل Googlebot عند الزحف إلى نظام التنقل الخاص بموقعك وفهمه. إن جعل نظام التنقل الخاص بموقعك مستحيل الزحف هو أسوأ شيء يمكنك القيام به أثناء بنائه. على الرغم من أن تجربة المستخدم هي أهم أولوياتنا، إلا أننا بالتأكيد لا نستطيع تجاهل هذه القضية. 3. وضع شريط للبحث بقدر ما يتعلق الأمر بتجربة المستخدم، فإن جعل موقعك قابلاً للبحث هو أفضل شيء يمكنك القيام به على الإطلاق في موقعك. إذا كان موقعك يحتوي على الكثير من الصفحات، فيجب أن يكون في أولويتك أن تجعله قابلًا للبحث. كان هناك دائمًا تردد حول أين يجب أن أضع شريط البحث؟ كيف يمكنني ملاحظة مربع البحث الخاص بي دون تشتيت انتباه المستخدم أو التأثير على تجربة المستخدم؟ لقد قرأت مقالتين تقترحان إبقاء مربع البحث مخفيًا خلف أيقونة بارزة يجلب النقر عليها لك مربع البحث، وسيؤدي إدخال استعلام البحث وطلبه إلى إنجاز المهمة. وقرأت أيضًا بعض الدراسات حول إخفاء مربع البحث خلف أيقونة بارزة سيؤدي إلى إرباك المستخدمين حول كيفية البحث في موقع الويب هذا وماذا سيفعل هذا الرمز! بالإضافة إلى ذلك، ساهمت هذه القضية في عدد أقل من عمليات البحث في بعض مواقع الويب. لذا، أقترح أن يكون لديك مربع بحث مرئي في نظام التنقل الخاص بك، لا تخفيه في أي مكان لتوفير مساحة قليلة من البكسلات، وإذا أردت أن يكون مربع البحث الخاص بك ملفتًا للمستخدمين، يمكنك وضعه على الجانب الأيسر من التنقل، تمامًا مثل Facebook و YouTube! 4. قائمة أفضل تنظيما قد يكون لديك الكثير من المنتجات أو الخدمات لتضمينها في تصفح موقع الويب الخاص بك. سيكون من الجيد أن تتمكن من تنظيمها وفق ترتيب ما. إذا كان لديك عددًا كبيرًا من العناصر للتنقل بينها في موقع الويب الخاص بك، فيمكنك التفكير في بناء قائمة ضخمة لتجميع عناصر القائمة التي لها نفس الأصل معًا. كما لو كنت وكالة إنشاء تقدم خدمات تصميم متنوعة من الرسوم والويب وتطبيقات الهاتف المحمول وغيرها الكثير، ثم قد تفكر في تصميم الجرافيك كفئة رئيسية لك تحوي خدمات مثل تصميم الكتب، وتصميم المنشورات، وتصميم الشعارات . تجميع الأشياء تجعل القراءة السريعة للزائرين سهلة عند التنقل. بالإضافة إلى ذلك، يساعدهم في تحديد موقع العناصر المطلوبة فقط من خلال النظر في تصنيف المجموعة. هذا يجعل القراءة السريعة والبحث سهلًا بعض الشيء. أود أن أقترح عليك أن تتجنب العدد الكبير جدًا من عناصر القائمة الفرعية. إن امتلاك قائمة يصل عدد عناصرها الفرعية إلى سبعة عناصر يُسهِّل على الزائرين للموقع فهم العنصر (العناصر) المطلوبة والعثور عليها بسهولة. ضع في حسبانك قاعدة واحدة وهي أنَّ زوار موقعك سيهتمون بأول عنصر في القائمة وآخر عنصر فيها. تأكد من وضع الروابط على كلا موقع العنصريين. إن وجود زر التحقق / رابط الذي يميز نفسه مع عناصر التنقل الأخرى سيزيد من فرصة النقر عليه. 5. القائمة العمودية حسنًا، أعرف أن القائمة الرأسية هي طريقة المدرسة القديمة لتنظيم روابط التنقل. انتظر أنا لا أتحدث عن استبدال قائمتك الأفقية الرائعة بقائمة رأسية. أنا أقترح عليك أن يكون لديك قائمة رأسية لصفحاتك الداخلية حيث تحتاج إلى إظهار بعض الخيارات المتداخلة. هناك بعض الحالات التي تحتاج فيها إلى إظهار قائمة إضافية للصفحة. وجود قائمة أفقية أخرى سيؤدي بالتأكيد إلى زعزعة تجربة المستخدم الخاصة بك. هل أنت على دراية بتصاميم مواقع التجارة الإلكترونية؟ هل شاهدت تصميم الصفحة عندما تضغط على قسم الحاسوب؟ أنا أتحدث عن القائمة الرأسية نفسها للصفحات الداخلية حيث يمكنك عرض خيارات إضافية، وروابط، ومعلومات، وعناصر أخرى. يساعدك هذا على جعل التنقل في موقع الويب الخاص بك منظمًا وواضحًا. سيشاهد الزوار القائمة الرأسية فقط عند زيارة الصفحات الداخلية. 6. تقييم عناصر القائمة يوجد سوء فهم في أن جميع الزوار يأتون إلى موقع الويب الخاص بك من خلال الصفحة الرئيسية. تحقق من تحليلاتك، إذ لديك نقاط دخول متعددة للموقع. بمعنى أن عناصر التنقل ليست هي المكان الذي سيذهب منه زائرو موقعك إلى صفحات المنتج أو الخدمات الأكثر تفصيلًا. نتيجة البحث الأساسية هي من تنقلهم إلى تلك الصفحات. لذلك، ليس عليك دائمًا تضمين كل رابط / صفحة في التنقل الخاص بموقعك. احتفظ فقط بالعناصر المهمة حقًا لعملك والعناصر التي يُنقَرعليها عند التنقل. قد تكون الأداة Google Analytics أفضل طريقة لك لمعرفة روابط التنقل التي يتم النقر عليها. وترشِّح الأداة Goto Behavio Filter النتيجة التي تبدأ بـ / أو الصفحة الرئيسية. يساعدك ذلك على فهم ما هي عناصر القائمة التي يهتم بها زوار موقعك. طبّق نفس الشيء على الصفحات التي تتحكم في حركة سير موقع الويب الخاص بك للحصول على فكرة أفضل عما يجب الاحتفاظ به وما الذي يجب تغييره وتخطيه. بمجرد الانتهاء من هذا التدقيق، عدِّل قائمة التنقل. سيكون للتنقل الجديد عناصر أقل بكثير، وفوضى أقل، وقائمة تنقل أفضل تنظيمًا. 7. تجنب الكثير من التشعبات كما هو مذكور في النقطة أعلاه، احتفظ بالروابط المهمة فقط بناءً على سلوك الزوار. إنها تساعدك على جعل الأمور واضحة. بالإضافة إلى ذلك، تجنب وجود الكثير من تداخل التصنيفات والفئات الفرعية والعناصر. وجود مستوى واحد من التداخل مثل الصنف الأب -> العناصر، سوف يفي بالغرض بشكل جيد للغاية. 8. أشياء متنوعة بسيطة هناك بعض الأشياء المتنوعة التي يمكنك أخذها بالحسبان أثناء تصميم قائمة التنقل وهي: تجنب التصاميم الغريبة تجنب تصميم قائمة تنقل خاصة بموقعك تصميمًا غريبًا وفريدًا فقط من أجل أن تكون مختلفًا. إن إنشاء وتصميم التنقل في أضيق الحدود يختلف عن طريقة إنشاء التنقل غريب المظهر لكسب التميز. لا تفعل ذلك. قد تفهمها وتحبها، لكن زوار موقع الويب خاصتك لن يكونوا بنفس مستوى الإدراك هذا. أن تكون متسقة الاتساق يعطي طابع الانتظام .استخدم قائمة التنقل نفسها في جميع صفحات موقع الويب الخاص بك. سيجعل زائرك أكثر دراية به وسيجدون أنه من السهل تحديد العناصر المطلوبة. اجعلها واضحة تصويرية كن واضحًا عند اختيار اسم عنصر التنقل. عند النقر عليه، يجب أن تفتح الصفحة التي قصدتها في الاسم. تجنب استخدام الأسماء العامة مثل «منتجات وخدمات»، كما في النصيحة السادسة في هذه المقالة احصل على اسم وصفي للعناصر مثل تصميم الشعار، وإصلاح شاشة الجوال، وحلول تخطيط موارد المؤسسات، وربط بوابة الدفع. قم بتجريبها أعلم أنك تستخدم بعض إطارات العمل الموثوقة مثل bootstrap لتصميم موقع الويب وقائمة التنقل. لكن احتفظ ببعض الوقت واختبر قائمة التنقل على جميع متصفحات الويب الرئيسية. تحقق من الوظيفة على كل من إصدار سطح المكتب من المتصفح وكذلك إصدارات الأجهزة المحمولة. الخلاصة لا يتطلب الأمر سوى استخدام بيانات موقع الويب والأفكار الإبداعية وبعض الاهتمامات الشائعة لإنشاء قائمة تنقل قابلة للاستخدام بشكل جيد وجذابة للغاية. ستساعدك النقاط الثمانية التي تمت مناقشتها هنا في تحديد ما يجب القيام به مع قائمة التنقل الخاصة بك التالية. ترجمة -وبتصرف- للمقال Eight tips to make website navigation menu more usable لصاحبه Darshan
- 1 تعليق
-
- 1
-
- قائمة التنقل
- تصميم المواقع
-
(و 1 أكثر)
موسوم في: