هناك طريقة أخرى لإنشاء وظيفة. ونادرا ما يستخدم، ولكن في بعض الأحيان لا يوجد بديل.
بناء الجملة لإنشاء وظيفة:
دع func = وظيفة جديدة ([arg1, arg2, ...argN], functionBody);
يتم إنشاء الدالة باستخدام الوسيطات arg1...argN
functionBody
المحددة.
من الأسهل أن نفهم من خلال النظر إلى مثال. إليك دالة تحتوي على وسيطتين:
Let sum = new Function('a', 'b', 'return a + b'); تنبيه(مجموع(1, 2)); // 3
وهنا توجد دالة بدون وسائط، مع نص الوظيفة فقط:
Let sayHi = new Function('alert("Hello")'); sayHi(); // مرحبًا
والفرق الرئيسي عن الطرق الأخرى التي رأيناها هو أن الوظيفة يتم إنشاؤها حرفيًا من سلسلة، يتم تمريرها في وقت التشغيل.
جميع الإعلانات السابقة تطلبت منا نحن المبرمجين كتابة كود الوظيفة في البرنامج النصي.
لكن new Function
تسمح بتحويل أي سلسلة إلى دالة. على سبيل المثال، يمكننا استلام دالة جديدة من الخادم ثم تنفيذها:
دع str = ... احصل على الكود من الخادم ديناميكيًا ... دع func = وظيفة جديدة (str)؛ وظيفة();
يتم استخدامه في حالات محددة جدًا، مثل عندما نتلقى تعليمات برمجية من خادم، أو لتجميع وظيفة ديناميكيًا من قالب، في تطبيقات الويب المعقدة.
عادةً ما تتذكر الدالة مكان ولادتها في الخاصية الخاصة [[Environment]]
. إنه يشير إلى البيئة المعجمية من المكان الذي تم إنشاؤه فيه (تناولنا ذلك في الفصل النطاق المتغير، الإغلاق).
ولكن عندما يتم إنشاء دالة باستخدام new Function
، يتم تعيين [[Environment]]
الخاصة بها للإشارة ليس إلى البيئة المعجمية الحالية، بل إلى البيئة العالمية.
لذلك، لا تتمتع هذه الوظيفة بإمكانية الوصول إلى المتغيرات الخارجية، بل إلى المتغيرات العامة فقط.
وظيفة getFunc () { دع القيمة = "اختبار"؛ دع func = وظيفة جديدة('تنبيه(قيمة)'); وظيفة العودة؛ } getFunc()(); // خطأ: لم يتم تعريف القيمة
قارنه بالسلوك العادي:
وظيفة getFunc () { دع القيمة = "اختبار"؛ دع func = function() { تنبيه (قيمة)؛ }; وظيفة العودة؛ } getFunc()(); // "اختبار"، من البيئة المعجمية لـ getFunc
تبدو هذه الميزة الخاصة new Function
غريبة، ولكنها تبدو مفيدة جدًا في الممارسة العملية.
تخيل أنه يجب علينا إنشاء دالة من سلسلة. رمز هذه الوظيفة غير معروف في وقت كتابة البرنامج النصي (ولهذا السبب لا نستخدم وظائف عادية)، ولكنه سيكون معروفًا في عملية التنفيذ. قد نتلقاها من الخادم أو من مصدر آخر.
تحتاج وظيفتنا الجديدة إلى التفاعل مع البرنامج النصي الرئيسي.
ماذا لو كان بإمكانه الوصول إلى المتغيرات الخارجية؟
تكمن المشكلة في أنه قبل نشر جافا سكريبت في مرحلة الإنتاج، يتم ضغطها باستخدام أداة تصغير - وهو برنامج خاص يعمل على تقليص التعليمات البرمجية عن طريق إزالة التعليقات والمسافات الإضافية - والأهم من ذلك، إعادة تسمية المتغيرات المحلية إلى متغيرات أقصر.
على سبيل المثال، إذا كانت الدالة تحتوي على let userName
، فإن المصغر يستبدله بـ let a
(أو حرف آخر إذا كان هذا الحرف مشغولًا)، ويقوم بذلك في كل مكان. عادةً ما يكون هذا أمرًا آمنًا، لأن المتغير محلي، ولا يمكن لأي شيء خارج الوظيفة الوصول إليه. وداخل الدالة، يستبدل المصغر كل ذكر لها. المصغرون أذكياء، ويقومون بتحليل بنية التعليمات البرمجية، لذلك لا يكسرون أي شيء. إنهم ليسوا مجرد عملية بحث واستبدال غبية.
لذا، إذا كانت new Function
تتمتع بإمكانية الوصول إلى المتغيرات الخارجية، فلن تتمكن من العثور على userName
المعاد تسميته.
إذا تمكنت new Function
من الوصول إلى المتغيرات الخارجية، فستواجه مشكلات مع المصغرات.
علاوة على ذلك، فإن مثل هذا الكود سيكون سيئًا من الناحية المعمارية وعرضة للأخطاء.
لتمرير شيء ما إلى دالة تم إنشاؤها new Function
، يجب أن نستخدم وسيطاتها.
بناء الجملة:
دع func = وظيفة جديدة ([arg1, arg2, ...argN], functionBody);
لأسباب تاريخية، يمكن أيضًا تقديم الحجج كقائمة مفصولة بفواصل.
هذه التصريحات الثلاثة تعني نفس الشيء:
وظيفة جديدة('a', 'b', 'return a + b'); // بناء الجملة الأساسي وظيفة جديدة('a,b', 'return a + b'); // مفصولة بفواصل وظيفة جديدة('أ، ب'، 'إرجاع أ + ب')؛ // مفصولة بفواصل
الوظائف التي تم إنشاؤها باستخدام new Function
، لها [[Environment]]
تشير إلى البيئة المعجمية العالمية، وليس البيئة الخارجية. وبالتالي، لا يمكنهم استخدام المتغيرات الخارجية. ولكن هذا أمر جيد في الواقع، لأنه يؤمن لنا من الأخطاء. يعد تمرير المعلمات بشكل صريح طريقة أفضل بكثير من الناحية المعمارية ولا يسبب أي مشاكل مع أدوات التصغير.