في كثير من الأحيان نحتاج إلى تنفيذ إجراء مماثل في العديد من أماكن البرنامج النصي.
على سبيل المثال، نحتاج إلى إظهار رسالة جميلة المظهر عندما يقوم الزائر بتسجيل الدخول أو الخروج وربما في مكان آخر.
الوظائف هي "اللبنات الأساسية" الرئيسية للبرنامج. أنها تسمح باستدعاء الرمز عدة مرات دون تكرار.
لقد رأينا بالفعل أمثلة على الوظائف المضمنة، مثل alert(message)
و prompt(message, default)
و confirm(question)
. ولكن يمكننا إنشاء وظائف خاصة بنا أيضًا.
لإنشاء دالة يمكننا استخدام إعلان دالة .
يبدو مثل هذا:
وظيفة showMessage () { تنبيه("مرحبا بالجميع!"); }
تبدأ الكلمة الأساسية function
أولاً، ثم تنتقل إلى اسم الوظيفة ، ثم قائمة المعلمات بين القوسين (مفصولة بفواصل، فارغة في المثال أعلاه، وسنرى أمثلة لاحقًا) وأخيرًا رمز الوظيفة، المسمى أيضًا "الجسم الوظيفي" بين الأقواس المتعرجة.
اسم الوظيفة (المعلمة 1، المعلمة 2، ... المعلمة N) { // جسم }
يمكن استدعاء وظيفتنا الجديدة باسمها: showMessage()
.
على سبيل المثال:
وظيفة showMessage () { تنبيه("مرحبا بالجميع!"); } showMessage(); showMessage();
يقوم استدعاء showMessage()
بتنفيذ كود الوظيفة. هنا سنرى الرسالة مرتين.
يوضح هذا المثال بوضوح أحد الأغراض الرئيسية للوظائف: تجنب تكرار التعليمات البرمجية.
إذا احتجنا في أي وقت إلى تغيير الرسالة أو طريقة عرضها، فيكفي تعديل الكود في مكان واحد: الوظيفة التي تخرجها.
المتغير المُعلن داخل الدالة يكون مرئيًا فقط داخل تلك الدالة.
على سبيل المثال:
وظيفة showMessage () { Let message = "مرحبًا، أنا جافا سكريبت!"; // المتغير المحلي تنبيه(رسالة); } showMessage(); // مرحبًا، أنا جافا سكريبت! تنبيه(رسالة); // <-- خطأ! المتغير محلي للدالة
يمكن للدالة الوصول إلى متغير خارجي أيضًا، على سبيل المثال:
دع اسم المستخدم = 'جون'؛ وظيفة showMessage () { Let message = 'Hello,' + userName; تنبيه (رسالة)؛ } showMessage(); // مرحبا جون
تتمتع الوظيفة بحق الوصول الكامل إلى المتغير الخارجي. ويمكن تعديله كذلك.
على سبيل المثال:
دع اسم المستخدم = 'جون'؛ وظيفة showMessage () { اسم المستخدم = "بوب"; // (1) تم تغيير المتغير الخارجي Let message = 'Hello,' + userName; تنبيه (رسالة)؛ } تنبيه (اسم المستخدم) ؛ // جون قبل استدعاء الوظيفة showMessage(); تنبيه (اسم المستخدم) ؛ // بوب، تم تعديل القيمة بواسطة الوظيفة
يتم استخدام المتغير الخارجي فقط في حالة عدم وجود متغير محلي.
إذا تم الإعلان عن متغير يحمل نفس الاسم داخل الدالة، فإنه يظلل المتغير الخارجي. على سبيل المثال، في الكود الموجود أدناه، تستخدم الوظيفة userName
المحلي . يتم تجاهل الخارجي:
دع اسم المستخدم = 'جون'؛ وظيفة showMessage () { دع اسم المستخدم = "بوب"؛ // أعلن عن متغير محلي Let message = 'Hello,' + userName; // بوب تنبيه (رسالة)؛ } // ستقوم الوظيفة بإنشاء واستخدام اسم المستخدم الخاص بها showMessage(); تنبيه (اسم المستخدم) ؛ // جون، دون تغيير، لم تتمكن الوظيفة من الوصول إلى المتغير الخارجي
المتغيرات العالمية
تسمى المتغيرات المعلنة خارج أي وظيفة، مثل userName
الخارجي في الكود أعلاه، بـ global .
تكون المتغيرات العامة مرئية من أي دالة (ما لم يتم تظليلها بواسطة السكان المحليين).
إنها ممارسة جيدة لتقليل استخدام المتغيرات العالمية. يحتوي الكود الحديث على عدد قليل من الكرات العالمية أو لا يحتوي على أي منها. معظم المتغيرات تكمن في وظائفهم. ومع ذلك، في بعض الأحيان، قد تكون مفيدة لتخزين البيانات على مستوى المشروع.
يمكننا تمرير بيانات عشوائية إلى الوظائف باستخدام المعلمات.
في المثال أدناه، تحتوي الدالة على معلمتين: from
و text
.
وظيفة showMessage(from, text) { // المعلمات: from, text تنبيه (من + ':' + نص)؛ } showMessage('آن', 'مرحبا!'); // آن: مرحبًا! (*) showMessage('آن', "ما الأمر؟"); // آن: ما الأمر؟ (**)
عندما يتم استدعاء الدالة في السطرين (*)
و (**)
، يتم نسخ القيم المعطاة إلى المتغيرات المحلية from
و text
. ثم تستخدمها الوظيفة.
إليك مثال آخر: لدينا متغير from
ونمرره إلى الدالة. يرجى ملاحظة: تتغير الدالة from
، لكن التغيير لا يظهر في الخارج، لأن الدالة تحصل دائمًا على نسخة من القيمة:
وظيفة showMessage(من، نص) { من = '*' + من + '*'؛ // اجعل "من" تبدو أجمل تنبيه (من + ':' + نص)؛ } دع من = "آن"؛ showMessage(from, "Hello"); // *آن*: مرحبًا // قيمة "من" هي نفسها، وقد قامت الدالة بتعديل نسخة محلية تنبيه (من)؛ // آن
عندما يتم تمرير قيمة كمعلمة دالة، فإنها تسمى أيضًا وسيطة .
وبعبارة أخرى، لوضع هذه المصطلحات في نصابها الصحيح:
المعلمة هي المتغير المدرج داخل الأقواس في إعلان الوظيفة (وهو مصطلح زمني للإعلان).
الوسيطة هي القيمة التي يتم تمريرها إلى الدالة عند استدعائها (وهو مصطلح وقت الاتصال).
نعلن عن الدوال مع إدراج معلماتها، ثم نطلق عليها اسم وسيطات التمرير.
في المثال أعلاه، يمكن للمرء أن يقول: "تم الإعلان عن الدالة showMessage
بمعلمتين، ثم تم استدعاؤها باستخدام وسيطتين: from
و "Hello"
".
إذا تم استدعاء دالة، ولكن لم يتم توفير وسيطة، تصبح القيمة المقابلة undefined
.
على سبيل المثال، يمكن استدعاء الدالة showMessage(from, text)
المذكورة أعلاه باستخدام وسيطة واحدة:
showMessage("آن");
هذا ليس خطأ. مثل هذه المكالمة ستؤدي إلى إخراج "*Ann*: undefined"
. وبما أن قيمة text
لم يتم تمريرها، فإنها تصبح undefined
.
يمكننا تحديد ما يسمى بالقيمة "الافتراضية" (لاستخدامها في حالة حذفها) لمعلمة في تعريف الدالة، باستخدام =
:
وظيفة showMessage(from, text = "لم يتم تقديم نص") { تنبيه(من + ":" + نص ); } showMessage("آن"); // آن: لم يتم تقديم نص
الآن إذا لم يتم تمرير معلمة text
، فستحصل على القيمة "no text given"
.
تنتقل القيمة الافتراضية أيضًا في حالة وجود المعلمة، ولكنها تساوي بشكل صارم undefined
، مثل هذا:
showMessage("آن", غير محدد); // آن: لم يتم تقديم نص
هنا "no text given"
عبارة عن سلسلة، ولكن يمكن أن يكون تعبيرًا أكثر تعقيدًا، والذي يتم تقييمه وتعيينه فقط إذا كانت المعلمة مفقودة. إذن، هذا ممكن أيضًا:
دالة showMessage(from, text = AnotherFunction()) { // لا يتم تنفيذ وظيفة أخرى إلا في حالة عدم تقديم نص // تصبح نتيجته قيمة النص }
تقييم المعلمات الافتراضية
في JavaScript، يتم تقييم المعلمة الافتراضية في كل مرة يتم فيها استدعاء الوظيفة بدون المعلمة المعنية.
في المثال أعلاه، لا يتم استدعاء anotherFunction()
على الإطلاق، إذا تم توفير معلمة text
.
ومن ناحية أخرى، يتم استدعاؤه بشكل مستقل في كل مرة يكون فيها text
مفقودًا.
المعلمات الافتراضية في كود JavaScript القديم
منذ عدة سنوات، لم تكن JavaScript تدعم بناء جملة المعلمات الافتراضية. لذلك استخدم الناس طرقًا أخرى لتحديدها.
في الوقت الحاضر، يمكننا أن نصادفهم في النصوص القديمة.
على سبيل المثال، التحقق الصريح لـ undefined
:
وظيفة showMessage(من، نص) { إذا (النص === غير محدد) { text = 'لم يتم تقديم نص'; } تنبيه(من + ":" + نص ); }
…أو باستخدام ||
المشغل:
وظيفة showMessage(من، نص) { // إذا كانت قيمة النص خاطئة، فقم بتعيين القيمة الافتراضية // هذا يفترض أن النص == "" هو نفس عدم وجود نص على الإطلاق نص = نص || "لم يتم تقديم نص"؛ ... }
في بعض الأحيان يكون من المنطقي تعيين قيم افتراضية للمعلمات في مرحلة لاحقة بعد إعلان الوظيفة.
يمكننا التحقق مما إذا كان قد تم تمرير المعلمة أثناء تنفيذ الوظيفة، من خلال مقارنتها مع undefined
:
وظيفة showMessage (نص) { // ... إذا (نص === غير محدد) {// إذا كانت المعلمة مفقودة النص = "رسالة فارغة"؛ } تنبيه(نص); } showMessage(); // رسالة فارغة
…أو يمكننا استخدام ||
المشغل:
وظيفة showMessage (نص) { // إذا كان النص غير محدد أو غير صحيح، فاضبطه على "فارغ" نص = نص || 'فارغ'؛ ... }
تدعم محركات JavaScript الحديثة عامل الدمج الفارغ ??
، فمن الأفضل أن تعتبر معظم القيم الزائفة، مثل 0
، "طبيعية":
دالة showCount(count) { // إذا كان العدد غير محدد أو فارغًا، قم بإظهار "غير معروف" تنبيه(العد؟؟ "غير معروف"); } showCount(0); // 0 showCount(null); // مجهول showCount(); // مجهول
يمكن للوظيفة إرجاع قيمة مرة أخرى إلى رمز الاستدعاء كنتيجة.
أبسط مثال هو دالة تجمع قيمتين:
مجموع الدالة (أ، ب) { العودة أ + ب؛ } دع النتيجة = مجموع (1، 2)؛ تنبيه (النتيجة)؛ // 3
يمكن أن يكون return
التوجيه في أي مكان من الوظيفة. عندما يصل التنفيذ إليها، تتوقف الوظيفة، ويتم إرجاع القيمة إلى رمز الاستدعاء (المخصص result
أعلاه).
قد يكون هناك العديد من تكرارات return
في دالة واحدة. على سبيل المثال:
وظيفة التحقق من العمر (العمر) { إذا (العمر >= 18) { عودة صحيحة؛ } آخر { إرجاع تأكيد("هل لديك إذن من والديك؟"); } } Let age = موجه('كم عمرك؟', 18); إذا (فحص العمر (العمر)) { تنبيه ("تم منح الوصول")؛ } آخر { تنبيه ("تم رفض الوصول")؛ }
من الممكن استخدام return
بدون قيمة. يؤدي ذلك إلى خروج الوظيفة على الفور.
على سبيل المثال:
وظيفة عرض الفيلم (العمر) { إذا (! checkAge(age) ) { يعود؛ } تنبيه("يظهر لك الفيلم"); // (*) // ... }
في الكود أعلاه، إذا قام checkAge(age)
بإرجاع false
، فلن يقوم showMovie
بالمتابعة إلى alert
.
دالة ذات return
فارغ أو بدونه ترجع undefined
إذا لم تُرجع الدالة قيمة، فسيكون الأمر كما لو أنها تُرجع undefined
:
الدالة doNothing() { /* فارغة */ } تنبيه (doNothing () === غير محدد)؛ // حقيقي
return
الفارغ هو أيضًا نفس return undefined
:
وظيفة لا تفعل شيئا () { يعود؛ } تنبيه (doNothing () === غير محدد)؛ // حقيقي
لا تقم مطلقًا بإضافة سطر جديد بين return
والقيمة
بالنسبة للتعبير الطويل في return
، قد يكون من المغري وضعه في سطر منفصل، مثل هذا:
يعود (بعض + طويل + تعبير + أو + مهما كان * f(a) + f(b))
هذا لا يعمل، لأن JavaScript تفترض وجود فاصلة منقوطة بعد return
. هذا سوف يعمل بنفس الطريقة:
يعود؛ (بعض + طويل + تعبير + أو + مهما كان * f(a) + f(b))
لذلك، يصبح فعليا عائدا فارغا.
إذا أردنا أن يلتف التعبير الذي تم إرجاعه عبر أسطر متعددة، فيجب أن نبدأه من نفس سطر return
. أو على الأقل ضع الأقواس الافتتاحية هناك كما يلي:
يعود ( بعض + طويل + التعبير + أو + مهما كان * و (أ) + و (ب) )
وسوف تعمل تماما كما نتوقع.
الوظائف هي الإجراءات. لذلك عادة ما يكون اسمهم فعلًا. يجب أن تكون مختصرة ودقيقة قدر الإمكان وأن تصف وظيفة الوظيفة، حتى يتمكن أي شخص يقرأ الكود من الحصول على إشارة إلى وظيفة الوظيفة.
من الممارسات الشائعة أن تبدأ وظيفة ببادئة لفظية تصف الإجراء بشكل غامض. يجب أن يكون هناك اتفاق داخل الفريق على معنى البادئات.
على سبيل المثال، عادةً ما تعرض الوظائف التي تبدأ بـ "show"
شيئًا ما.
الدالة تبدأ بـ...
"get…"
- إرجاع قيمة،
"calc…"
- حساب شيء ما،
"create…"
- إنشاء شيء ما،
"check…"
- التحقق من شيء ما وإرجاع قيمة منطقية، وما إلى ذلك.
أمثلة على هذه الأسماء:
showMessage(..) // يظهر رسالة getAge(..) // يُرجع العمر (يحصل عليه بطريقة ما) calcSum(..) // يحسب المجموع ويعيد النتيجة createForm(..) // يُنشئ نموذجًا (ويُرجعه عادةً) checkPermission(..) // التحقق من الإذن، وإرجاع صحيح/خطأ
مع وجود البادئات في مكانها، تعطي نظرة سريعة على اسم الوظيفة فهمًا لنوع العمل الذي تقوم به ونوع القيمة التي تُرجعها.
وظيفة واحدة - عمل واحد
يجب أن تقوم الوظيفة بالضبط بما يقترحه اسمها، لا أكثر.
عادةً ما يستحق الإجراءان المستقلان وظيفتين، حتى لو تم استدعاؤهما معًا (في هذه الحالة يمكننا إنشاء دالة ثالثة تستدعي هاتين الوظيفتين).
بعض الأمثلة على كسر هذه القاعدة:
getAge
- سيكون سيئًا إذا أظهر alert
بالعمر (يجب أن يحصل فقط).
createForm
- سيكون سيئًا إذا قام بتعديل المستند وإضافة نموذج إليه (يجب فقط إنشائه وإعادته).
checkPermission
- سيكون سيئًا إذا عرض رسالة access granted/denied
(يجب إجراء الفحص فقط وإرجاع النتيجة).
تفترض هذه الأمثلة المعاني الشائعة للبادئات. لك أنت وفريقك الحرية في الاتفاق على معاني أخرى، لكنها عادةً لا تختلف كثيرًا. على أي حال، يجب أن يكون لديك فهم جيد لما تعنيه البادئة، وما يمكن أن تفعله الوظيفة البادئة وما لا يمكنها فعله. يجب أن تمتثل جميع الوظائف ذات البادئة نفسها للقواعد. ويجب على الفريق مشاركة المعرفة.
أسماء الوظائف فائقة القصر
الوظائف التي يتم استخدامها في كثير من الأحيان لها أسماء قصيرة جدًا في بعض الأحيان.
على سبيل المثال، يحدد إطار عمل jQuery دالة بـ $
. تحتوي مكتبة Lodash على وظيفتها الأساسية المسماة _
.
هذه استثناءات. بشكل عام، يجب أن تكون أسماء الوظائف موجزة ووصفية.
يجب أن تكون الوظائف قصيرة وتفعل شيئًا واحدًا بالضبط. إذا كان هذا الشيء كبيرًا، فربما يكون من المفيد تقسيم الوظيفة إلى بضع وظائف أصغر. في بعض الأحيان، قد لا يكون اتباع هذه القاعدة بهذه السهولة، لكنه بالتأكيد أمر جيد.
الوظيفة المنفصلة ليست فقط أسهل في الاختبار والتصحيح - بل إن وجودها في حد ذاته يعد تعليقًا رائعًا!
على سبيل المثال، قارن بين الدالتين showPrimes(n)
أدناه. كل واحد يخرج أعدادًا أولية تصل إلى n
.
يستخدم المتغير الأول تسمية:
وظيفة showPrimes (ن) { nextPrime: for (let i = 2; i < n; i++) { لـ (دع j = 2; j < i; j++) { إذا (i % j == 0) تابع nextPrime؛ } تنبيه (ط)؛ // رئيس الوزراء } }
يستخدم المتغير الثاني وظيفة إضافية isPrime(n)
لاختبار البدائية:
وظيفة showPrimes (ن) { لـ (دع i = 2; i < n; i++) { إذا استمر (!isPrime(i)) ؛ تنبيه (ط)؛ // رئيس الوزراء } } الدالة هي برايم (ن) { لـ (دع i = 2; i < n; i++) { إذا (n %i == 0) يُرجع خطأ؛ } عودة صحيحة؛ }
البديل الثاني أسهل للفهم، أليس كذلك؟ بدلاً من جزء التعليمات البرمجية نرى اسم الإجراء ( isPrime
). يشير الأشخاص أحيانًا إلى هذه التعليمات البرمجية على أنها وصف ذاتي .
لذلك، يمكن إنشاء الوظائف حتى لو لم نكن ننوي إعادة استخدامها. يقومون ببناء الكود وجعله قابلاً للقراءة.
يبدو إعلان الوظيفة كما يلي:
اسم الوظيفة (المعلمات، محددة، بواسطة، فاصلة) { /* شفرة */ }
يتم نسخ القيم التي تم تمريرها إلى دالة كمعلمات إلى متغيراتها المحلية.
يمكن للوظيفة الوصول إلى المتغيرات الخارجية. لكنها تعمل فقط من الداخل إلى الخارج. لا يرى الكود الموجود خارج الوظيفة متغيراته المحلية.
يمكن للدالة إرجاع قيمة. وإذا لم يحدث ذلك، فإن نتيجته undefined
.
لجعل التعليمات البرمجية واضحة وسهلة الفهم، يوصى باستخدام المتغيرات والمعلمات المحلية بشكل أساسي في الوظيفة، وليس المتغيرات الخارجية.
من الأسهل دائمًا فهم الدالة التي تحصل على المعلمات، وتعمل معها وترجع نتيجة، مقارنة بالدالة التي لا تحصل على أي معلمات، ولكنها تعدل المتغيرات الخارجية كأثر جانبي.
تسمية الوظيفة:
يجب أن يصف الاسم بوضوح ما تفعله الوظيفة. عندما نرى استدعاء دالة في الكود، فإن الاسم الجيد يمنحنا على الفور فهمًا لما يفعله وما يُرجعه.
الوظيفة هي إجراء، لذا فإن أسماء الوظائف عادة ما تكون لفظية.
توجد العديد من بادئات الوظائف المعروفة مثل create…
و show…
get…
و check…
وما إلى ذلك. استخدمها للتلميح إلى ما تفعله الوظيفة.
الوظائف هي اللبنات الأساسية للنصوص البرمجية. لقد قمنا الآن بتغطية الأساسيات، حتى نتمكن بالفعل من البدء في إنشائها واستخدامها. ولكن هذه مجرد بداية الطريق. سنعود إليها عدة مرات، ونتعمق أكثر في ميزاتها المتقدمة.
الأهمية: 4
ترجع الدالة التالية true
إذا كان age
المعلمة أكبر من 18
.
وإلا فإنه يطلب التأكيد ويعيد النتيجة:
وظيفة التحقق من العمر (العمر) { إذا (العمر > 18) { عودة صحيحة؛ } آخر { // ... إرجاع تأكيد("هل سمح لك والديك؟"); } }
هل ستعمل الوظيفة بشكل مختلف إذا تمت إزالة else
؟
وظيفة التحقق من العمر (العمر) { إذا (العمر> 18) { عودة صحيحة؛ } // ... إرجاع تأكيد("هل سمح لك والديك؟"); }
هل هناك أي اختلاف في سلوك هذين المتغيرين؟
لا فرق!
في كلتا الحالتين، يُنفَّذ return confirm('Did parents allow you?')
تمامًا عندما يكون شرط if
خاطئًا.
الأهمية: 4
ترجع الدالة التالية true
إذا كان age
المعلمة أكبر من 18
.
وإلا فإنه يطلب التأكيد ويعيد النتيجة.
وظيفة التحقق من العمر (العمر) { إذا (العمر > 18) { عودة صحيحة؛ } آخر { إرجاع تأكيد("هل سمح لك والديك؟"); } }
أعد كتابته للقيام بنفس الشيء، لكن بدون if
، في سطر واحد.
قم بعمل نوعين مختلفين من checkAge
:
باستخدام عامل علامة الاستفهام ?
باستخدام أو ||
استخدام عامل تشغيل علامة الاستفهام '?'
:
وظيفة التحقق من العمر (العمر) { العودة (العمر> 18) ؟ صحيح : تأكيد ("هل سمح لك الوالدان؟")؛ }
باستخدام أو ||
(الصيغة الأقصر):
وظيفة التحقق من العمر (العمر) { العودة (العمر > 18) || تأكيد("هل سمح لك والديك؟"); }
لاحظ أن الأقواس حول age > 18
غير مطلوبة هنا. أنها موجودة لتحسين القراءة.
الأهمية: 1
اكتب دالة min(a,b)
تُرجع أقل رقمين a
و b
.
على سبيل المثال:
دقيقة (2، 5) == 2 دقيقة (3، -1) == -1 دقيقة (1، 1) == 1
الحل باستخدام if
:
وظيفة دقيقة (أ، ب) { إذا (أ < ب) { العودة أ؛ } آخر { العودة ب؛ } }
الحل مع عامل تشغيل علامة الاستفهام '?'
:
وظيفة دقيقة (أ، ب) { إرجاع أ < ب ؟ أ : ب؛ }
PS في حالة المساواة a == b
لا يهم ما يعود.
الأهمية: 4
اكتب دالة pow(x,n)
تُرجع x
في القوة n
. أو بمعنى آخر، ضرب x
في نفسه n
مرات وإرجاع النتيجة.
الأسرى (3، 2) = 3 * 3 = 9 الأسرى (3، 3) = 3 * 3 * 3 = 27 الأسرى(1, 100) = 1 * 1 * ...* 1 = 1
أنشئ صفحة ويب تطالبك بـ x
و n
، ثم تعرض نتيجة pow(x,n)
.
قم بتشغيل العرض التوضيحي
ملاحظة: في هذه المهمة، يجب أن تدعم الدالة القيم الطبيعية فقط لـ n
: الأعداد الصحيحة بدءًا من 1
.
وظيفة الأسرى (س، ن) { دع النتيجة = س؛ لـ (دع i = 1; i < n; i++) { النتيجة *= س؛ } نتيجة الإرجاع؛ } دع x = موجه("x؟", ''); Let n = موجه("n؟", ''); إذا (ن < 1) { تنبيه ("الطاقة ${n} غير مدعومة، استخدم عددًا صحيحًا موجبًا") ؛ } آخر { تنبيه( الأسرى(x, n)); }