في JavaScript، الوظيفة ليست "بنية لغة سحرية"، ولكنها نوع خاص من القيمة.
بناء الجملة الذي استخدمناه من قبل يسمى إعلان الوظيفة :
الدالة sayHi() { تنبيه("مرحبا"); }
هناك صيغة أخرى لإنشاء دالة تسمى "تعبير الوظيفة" .
يسمح لنا بإنشاء وظيفة جديدة في منتصف أي تعبير.
على سبيل المثال:
دعونا نقول مرحبا = وظيفة () { تنبيه("مرحبا"); };
هنا يمكننا أن نرى المتغير sayHi
يحصل على قيمة، الوظيفة الجديدة، تم إنشاؤها كـ function() { alert("Hello"); }
.
نظرًا لأن إنشاء الوظيفة يحدث في سياق تعبير المهمة (على الجانب الأيمن من =
)، فهذا هو تعبير دالة .
يرجى ملاحظة أنه لا يوجد اسم بعد الكلمة الأساسية function
. يُسمح بحذف اسم للتعبيرات الوظيفية.
هنا نقوم بتعيينها على الفور للمتغير، وبالتالي فإن معنى نماذج التعليمات البرمجية هذه هو نفسه: "إنشاء دالة ووضعها في المتغير sayHi
".
في المواقف الأكثر تقدمًا، والتي سنواجهها لاحقًا، قد يتم إنشاء وظيفة واستدعائها على الفور أو جدولتها لتنفيذ لاحق، ولا يتم تخزينها في أي مكان، وبالتالي تظل مجهولة المصدر.
دعونا نكرر: بغض النظر عن كيفية إنشاء الوظيفة، فإن الوظيفة هي قيمة. يقوم كلا المثالين أعلاه بتخزين دالة في المتغير sayHi
.
يمكننا حتى طباعة هذه القيمة باستخدام alert
:
الدالة sayHi() { تنبيه("مرحبا"); } تنبيه (قل مرحبًا) ؛ // يعرض رمز الوظيفة
يرجى ملاحظة أن السطر الأخير لا يقوم بتشغيل الوظيفة، لأنه لا توجد أقواس بعد sayHi
. هناك لغات برمجة حيث يؤدي أي ذكر لاسم الوظيفة إلى تنفيذها، لكن JavaScript ليس كذلك.
في JavaScript، الدالة هي قيمة، لذا يمكننا التعامل معها كقيمة. يُظهر الكود أعلاه تمثيل السلسلة الخاص به، وهو الكود المصدري.
بالتأكيد، الدالة هي قيمة خاصة، بمعنى أننا يمكن أن نسميها مثل sayHi()
.
لكنها لا تزال قيمة. لذا يمكننا التعامل معها كما هو الحال مع أنواع أخرى من القيم.
يمكننا نسخ دالة إلى متغير آخر:
دالة sayHi() { // (1) إنشاء تنبيه("مرحبا"); } Let func = sayHi; // (2) نسخة وظيفة(); // مرحبا // (3) قم بتشغيل النسخة (تعمل)! sayHi(); // مرحبًا // لا يزال هذا يعمل أيضًا (لماذا لا يعمل)
إليك ما يحدث أعلاه بالتفصيل:
يقوم إعلان الدالة (1)
بإنشاء الدالة ووضعها في المتغير المسمى sayHi
.
السطر (2)
ينسخه في المتغير func
. يرجى الملاحظة مرة أخرى: لا توجد أقواس بعد sayHi
. إذا كان الأمر كذلك، فستكتب func = sayHi()
نتيجة استدعاء sayHi()
في func
، وليس الدالة sayHi
نفسها.
الآن يمكن استدعاء الوظيفة باسم sayHi()
و func()
.
كان بإمكاننا أيضًا استخدام تعبير دالة للإعلان عن sayHi
في السطر الأول:
دع sayHi = function() { // (1) إنشاء تنبيه("مرحبا"); }; Let func = sayHi; // ...
كل شيء سيعمل بنفس الطريقة.
لماذا توجد فاصلة منقوطة في النهاية؟
قد تتساءل، لماذا تحتوي التعبيرات الوظيفية على فاصلة منقوطة ;
في النهاية، لكن إعلانات الوظائف لا:
الدالة sayHi() { // ... } دعونا نقول مرحبا = وظيفة () { // ... };
الجواب بسيط: يتم هنا إنشاء تعبير دالة function(…) {…}
داخل عبارة المهمة: let sayHi = …;
. الفاصلة المنقوطة ;
يوصى به في نهاية العبارة، فهو ليس جزءًا من بناء جملة الوظيفة.
ستكون الفاصلة المنقوطة موجودة لمهمة أبسط، مثل let sayHi = 5;
، وهو موجود أيضًا لتعيين وظيفة.
دعونا نلقي نظرة على المزيد من الأمثلة على تمرير الدوال كقيم واستخدام تعبيرات الدالة.
سنكتب دالة ask(question, yes, no)
بثلاثة معلمات:
question
نص السؤال
yes
الوظيفة التي سيتم تشغيلها إذا كانت الإجابة "نعم"
no
الوظيفة التي سيتم تشغيلها إذا كانت الإجابة "لا"
يجب أن تطرح الدالة question
، واستنادًا إلى إجابة المستخدم، اتصل بـ yes()
أو no()
:
وظيفة اسأل (سؤال، نعم، لا) { إذا (تأكيد (سؤال)) نعم () وإلا لا()؛ } عرض الدالةOk() { تنبيه("لقد وافقت."); } عرض الدالة إلغاء () { تنبيه("لقد ألغيت التنفيذ."); } // الاستخدام: يتم تمرير الوظائف showOk وshowCancel كوسائط للطرح Ask("هل توافق؟", showOk, showCancel);
في الممارسة العملية، هذه الوظائف مفيدة للغاية. يتمثل الاختلاف الرئيسي بين ask
الواقعي والمثال أعلاه في أن وظائف الحياة الواقعية تستخدم طرقًا أكثر تعقيدًا للتفاعل مع المستخدم بدلاً من confirm
البسيط. في المتصفح، عادةً ما ترسم مثل هذه الوظائف نافذة أسئلة جميلة المظهر. لكن هذه قصة أخرى.
تُسمى الوسيطات showOk
و showCancel
الخاصة ask
بوظائف رد الاتصال أو مجرد عمليات رد اتصال .
الفكرة هي أننا نمرر دالة ونتوقع أن يتم "استدعائها" لاحقًا إذا لزم الأمر. في حالتنا، يصبح showOk
رد الاتصال للإجابة "نعم"، ويصبح showCancel
رد الاتصال للإجابة "لا".
يمكننا استخدام التعبيرات الوظيفية لكتابة دالة مكافئة وأقصر:
وظيفة اسأل (سؤال، نعم، لا) { إذا (تأكيد (سؤال)) نعم () وإلا لا()؛ } بسأل( "هل توافق؟"، وظيفة () { تنبيه ("لقد وافقت.")؛ }, function() { تنبيه ("لقد ألغيت التنفيذ."); } );
هنا، يتم الإعلان عن الوظائف مباشرة داخل استدعاء ask(...)
. ليس لديهم اسم، ولذلك يطلق عليهم اسم مجهول . لا يمكن الوصول إلى مثل هذه الوظائف خارج نطاق ask
(لأنها غير مخصصة للمتغيرات)، ولكن هذا ما نريده هنا.
تظهر هذه التعليمات البرمجية في نصوصنا بشكل طبيعي جدًا، وهي تتوافق مع روح JavaScript.
الدالة هي قيمة تمثل "الإجراء"
تمثل القيم العادية مثل السلاسل أو الأرقام البيانات .
يمكن إدراك الوظيفة على أنها إجراء .
يمكننا تمريرها بين المتغيرات وتشغيلها عندما نريد.
دعونا نقوم بصياغة الاختلافات الرئيسية بين إعلانات الوظائف والتعبيرات.
أولاً، النحو: كيفية التفريق بينهما في الكود.
إعلان الوظيفة: دالة، تم الإعلان عنها كبيان منفصل، في تدفق التعليمات البرمجية الرئيسي:
// إعلان الوظيفة مجموع الدالة (أ، ب) { العودة أ + ب؛ }
تعبير الوظيفة: دالة يتم إنشاؤها داخل تعبير أو داخل بناء جملة آخر. هنا يتم إنشاء الدالة على الجانب الأيمن من "تعبير المهمة" =
:
// التعبير الوظيفي دع المبلغ = الوظيفة (أ، ب) { العودة أ + ب؛ };
الفرق الأكثر دقة هو عندما يتم إنشاء دالة بواسطة محرك JavaScript.
يتم إنشاء التعبير الوظيفي عندما يصل التنفيذ إليه ويكون قابلاً للاستخدام فقط من تلك اللحظة.
بمجرد أن يمر تدفق التنفيذ إلى الجانب الأيمن من المهمة let sum = function…
- ها نحن ذا، يتم إنشاء الوظيفة ويمكن استخدامها (تعيينها أو استدعائها وما إلى ذلك) من الآن فصاعدًا.
إعلانات الوظائف مختلفة.
يمكن استدعاء إعلان الوظيفة قبل تحديده.
على سبيل المثال، يكون إعلان الوظيفة العام مرئيًا في البرنامج النصي بأكمله، بغض النظر عن مكان وجوده.
هذا بسبب الخوارزميات الداخلية. عندما تستعد JavaScript لتشغيل البرنامج النصي، فإنها تبحث أولاً عن إعلانات الوظائف العامة فيه وتقوم بإنشاء الوظائف. يمكننا أن نفكر في الأمر على أنه "مرحلة التهيئة".
وبعد معالجة كافة إعلانات الوظائف، يتم تنفيذ التعليمات البرمجية. لذلك لديه حق الوصول إلى هذه الوظائف.
على سبيل المثال، يعمل هذا:
sayHi("جون"); // مرحبا جون وظيفة قل مرحبا (الاسم) { تنبيه (`مرحبا، ${name}`)؛ }
يتم إنشاء إعلان الدالة sayHi
عندما تستعد JavaScript لبدء البرنامج النصي ويكون مرئيًا في كل مكان فيه.
…إذا كان تعبير دالة، فلن يعمل:
sayHi("جون"); // خطأ! Let sayHi = function(name) { // (*) لم يعد هناك سحر بعد الآن تنبيه (`مرحبا، ${name}`)؛ };
يتم إنشاء التعبيرات الوظيفية عندما يصل التنفيذ إليها. سيحدث ذلك فقط في السطر (*)
. فات الأوان.
ميزة خاصة أخرى لإعلانات الوظائف هي نطاق الكتلة الخاص بها.
في الوضع الصارم، عندما يكون إعلان الوظيفة ضمن كتلة تعليمات برمجية، فإنه يكون مرئيًا في كل مكان داخل تلك الكتلة. ولكن ليس خارجها.
على سبيل المثال، لنتخيل أننا بحاجة إلى إعلان دالة welcome()
اعتمادًا على متغير age
الذي نحصل عليه أثناء وقت التشغيل. ثم نخطط لاستخدامه في وقت لاحق.
إذا استخدمنا إعلان الوظيفة، فلن يعمل على النحو المنشود:
Let age = موجه ("ما هو عمرك؟"، 18)؛ // أعلن عن وظيفة بشكل مشروط إذا (العمر <18) { وظيفة الترحيب () { تنبيه("مرحبا!"); } } آخر { وظيفة الترحيب () { تنبيه ("تحياتي!")؛ } } // ...استخدمه لاحقًا مرحباً()؛ // خطأ: لم يتم تعريف الترحيب
وذلك لأن إعلان الوظيفة يكون مرئيًا فقط داخل كتلة التعليمات البرمجية التي يوجد بها.
وهنا مثال آخر:
دع السن = 16؛ // خذ 16 كمثال إذا (العمر <18) { مرحباً()؛ // (يعمل) // | وظيفة الترحيب () { // | تنبيه("مرحبا!"); // | إعلان الوظيفة متاح } // | في كل مكان في الكتلة حيث تم الإعلان عنه // | مرحباً()؛ // / (يعمل) } آخر { وظيفة الترحيب () { تنبيه ("تحياتي!")؛ } } // نحن هنا خارج الأقواس المتعرجة، // لذلك لا يمكننا رؤية إعلانات الوظائف الموجودة داخلها. مرحباً()؛ // خطأ: لم يتم تعريف الترحيب
ما الذي يمكننا فعله لجعل welcome
مرئيًا خارج if
؟
سيكون الأسلوب الصحيح هو استخدام تعبير دالة وتعيين welcome
للمتغير الذي تم الإعلان عنه خارج if
ولديه الرؤية المناسبة.
يعمل هذا الرمز على النحو المنشود:
Let age = موجه ("ما هو عمرك؟"، 18)؛ دعونا نرحب؛ إذا (العمر <18) { ترحيب = وظيفة () { تنبيه("مرحبا!"); }; } آخر { ترحيب = وظيفة () { تنبيه ("تحياتي!")؛ }; } مرحباً()؛ // حسنا الآن
أو يمكننا تبسيط الأمر بشكل أكبر باستخدام عامل علامة الاستفهام ?
:
Let age = موجه ("ما هو عمرك؟"، 18)؛ دع الترحيب = (العمر < 18) ؟ وظيفة () { تنبيه ("مرحبا!")؛ } : وظيفة () { تنبيه ("تحياتي!")؛ }; مرحباً()؛ // حسنا الآن
متى تختار إعلان الوظيفة مقابل التعبير الوظيفي؟
كقاعدة عامة، عندما نحتاج إلى الإعلان عن دالة، فإن أول شيء يجب مراعاته هو بناء جملة إعلان الدالة. إنه يمنح المزيد من الحرية في كيفية تنظيم التعليمات البرمجية الخاصة بنا، لأنه يمكننا استدعاء مثل هذه الوظائف قبل الإعلان عنها.
وهذا أيضًا أفضل لسهولة القراءة، حيث أنه من الأسهل البحث عن function f(…) {…}
في الكود بدلاً من let f = function(…) {…};
. تعد إعلانات الوظائف أكثر "لفتًا للنظر".
…ولكن إذا كان إعلان الوظيفة لا يناسبنا لسبب ما، أو كنا بحاجة إلى إعلان شرطي (لقد رأينا للتو مثالاً)، فيجب استخدام التعبير الوظيفي.
الوظائف هي القيم. يمكن تعيينها أو نسخها أو الإعلان عنها في أي مكان من الكود.
إذا تم الإعلان عن الوظيفة كبيان منفصل في تدفق التعليمات البرمجية الرئيسي، فإن ذلك يسمى "إعلان الوظيفة".
إذا تم إنشاء الدالة كجزء من تعبير، فإنها تسمى "تعبير الدالة".
تتم معالجة إعلانات الوظائف قبل تنفيذ كتلة التعليمات البرمجية. أنها مرئية في كل مكان في الكتلة.
يتم إنشاء التعبيرات الوظيفية عندما يصل إليها تدفق التنفيذ.
في معظم الحالات عندما نحتاج إلى إعلان دالة، يُفضل إعلان الدالة، لأنه يكون مرئيًا قبل الإعلان نفسه. وهذا يمنحنا المزيد من المرونة في تنظيم التعليمات البرمجية، وعادةً ما يكون أكثر قابلية للقراءة.
لذلك يجب علينا استخدام تعبير الدالة فقط عندما لا يكون إعلان الدالة مناسبًا للمهمة. وقد رأينا بضعة أمثلة على ذلك في هذا الفصل، وسنرى المزيد في المستقبل.