ما هذا؟
في JavaScript ، كل وظيفة ، عند استدعاء ، تنشئ سياق تنفيذ جديد. نظرًا لأن المتغيرات والوظائف المحددة في الوظائف هي المتغيرات الوحيدة التي يتم الوصول إليها داخليًا ، وليس خارجيًا ، عند استدعاء وظيفة ، يوفر السياق الذي توفره الوظيفة طريقة بسيطة للغاية لإنشاء متغيرات خاصة.
وظيفة makecounter () {var i = 0 ؛ Return Function () {console.log (++ i) ؛ } ؛ } // تذكر: `counter` و` counter2 `لديهم متغيراتهما الخاصة` i`var counter = makecounter () ؛ counter () ؛ // 1counter () ؛ // 2var counter2 = makecounter () ؛ counter2 () ؛ // 1counter2 () ؛ // 2i ؛في كثير من الحالات ، قد لا تحتاج إلى وظيفة مثل Makehwhanting لإرجاع قيم متعددة المتراكمة ، ويمكنك تسميتها مرة واحدة فقط للحصول على قيمة واحدة ، وفي بعض الحالات الأخرى ، لا تحتاج حتى إلى معرفة قيمة الإرجاع بشكل صريح.
جوهرها
الآن ، بغض النظر عن تحديد دالة مثل FOUT FOO () {} أو var foo = function () {} ، عند الاتصال ، تحتاج إلى إضافة زوج من الأقواس بعد ذلك ، مثل foo ().
// يمكن استدعاء الوظيفة المحددة أدناه عن طريق إضافة زوج من الأقواس بعد اسم الوظيفة ، مثل `foo ()` ، // لأن foo مجرد متغير مرجع var foo = function () {/ * code */`code` code */`//`//`//`//`code` « قوسين بعد ذلك؟ function () { / * code * /} () ؛ // syntaxerror: رمز غير متوقع (كما ترون ، يتم اكتشاف خطأ هنا. عندما تظهر الأقواس خلف وظيفة من أجل استدعاء وظيفة ، سواء واجهت مثل هذه الكلمة الرئيسية للدالة في البيئة العالمية أو البيئة المحلية ، بشكل افتراضي ، فإنها ستتعامل معها كعلامة دالة بدلاً من تعبير الوظيفة. إذا لم تخبر الأقواس بشكل صريح بأنها تعبير ، فسيتعامل معها كدالة بدون اسم ورمي خطأ ، لأن إعلان الوظيفة يتطلب اسمًا.
السؤال 1: هل يمكنني التفكير في سؤال هنا؟ هل يمكننا أيضًا استدعاء الدالة var foo = function () {console.log (1)} () مثل هذا مباشرة ، والإجابة على ما يرام.
السؤال 2: وبالمثل ، يمكننا أيضًا التفكير في سؤال. إذا كان إعلان الوظيفة مثل هذا يسمى مباشرة مع الأقواس بعد ذلك ، ماذا يحدث؟ يرجى الاطلاع على الإجابة أدناه.
وظيفة ، أقواس ، خطأ
ومن المثير للاهتمام ، إذا حددت اسمًا لوظيفة ووضع زوجًا من الأقواس خلفها ، فسيتم طرح نفس الخطأ ، ولكن هذه المرة هو لسبب آخر. عندما يتم وضع الأقواس بعد تعبير وظيفة ، فإنه يشير إلى أن هذه وظيفة تسمى ، ويتم وضع الأقواس بعد إعلان ، فهذا يعني أنها منفصلة تمامًا عن إعلان الوظيفة السابق. في هذا الوقت ، فإن الأقواس هي مجرد تمثيل بسيط للأقواس (الأقواس المستخدمة للتحكم في أولوية التشغيل).
// ومع ذلك ، فإن إعلان الوظيفة غير صالح بشكل نحلي ، ولا يزال إعلانًا ، والأقواس التالية غير صالحة ، لأن الأقواس تحتاج إلى تضمين وظيفة التعبير foo () {/* code*/} () */} (1) // يعادل ما يلي ، يتبع إعلان الوظيفة تعبيرًا ليس له علاقة على الإطلاق: Function foo () {/ *code */} (1) ؛تنفيذ تعبيرات الوظيفة على الفور (iife)
لحسن الحظ ، من السهل إصلاح أخطاء القواعد. الطريقة الأكثر شعبية والأكثر قبولًا هي لف إعلانات الوظيفة بين قوسين لإخبار المحلل بالتعبير عن تعبير الوظيفة ، لأنه في جافا سكريبت ، لا يمكن أن تحتوي الأقواس على إعلانات. لهذا السبب ، عندما تواجه الأقواس الكلمة الرئيسية للوظيفة من أجل لف الوظيفة ، فإنها تعرف تحليلها كتعبير وظيفة بدلاً من إعلان الوظيفة. انتبه لفهم أن الأقواس هنا تختلف عن الأقواس أعلاه عندما تواجه وظائف ، وهذا هو ،
عندما تظهر الأقواس في نهاية دالة مجهولة ويريد استدعاء وظيفة ، فإنه سيؤدي إلى التعامل مع الوظيفة كعلامة دالة.
عند لف وظيفة بين قوسين ، ستحلّل الوظيفة كتعبير افتراضيًا ، بدلاً من إعلان الوظيفة.
// يمكن استخدام كلا النموذجين لاستدعاء تعبير الوظيفة على الفور ، باستخدام تنفيذ الوظيفة لإنشاء متغيرات خاصة (function () {/ * code */} ()) ؛ يجب على المشغلين إزالة الغموض // بين تعبيرات الوظائف وإعلانات الوظائف ، يمكن حذفهم // عندما يتوقع المحلل بالفعل تعبيرًا (ولكن يرجى الاطلاع على // "ملاحظة مهمة" أدناه) .var i = function () {return 10 ؛} () ممكن ، يمكنك تخزين البايتات عن طريق أخذ مشغل أحادي أمام وظيفتك! function () {/ * code */} () ؛ ~ function () {/ * code */} () ؛ - function () {/ * code */} () ؛+function () {/ * code */} () ؛ http://twitter.com/kuvos/status/18209252090847232new function () {/ * code */} وظيفة جديدة () {/ * code */} ()ملاحظات مهمة على الأقواس
في بعض الحالات ، ليس من الضروري أن يحيط بالتعبير عن الوظيفة عندما تكون أقواس غامضة إضافية حول تعبير الوظيفة (لأن الأقواس يتم التعبير عنها بالفعل كتعبير) ، لكن هذا لا يزال فكرة جيدة عند استخدام الأقواس لاستدعاء تعبير الوظيفة.
تشير هذه الأقواس إلى أن تعبير الوظيفة سيتم استدعاؤه على الفور وأن المتغير سيخزن نتيجة الوظيفة ، وليس الوظيفة نفسها. عندما يكون هذا تعبيرًا طويلًا للغاية ، فإن هذا يوفر الوقت من الأشخاص الذين يقرؤون الكود الخاص بك دون الحاجة إلى التمرير إلى أسفل الصفحة لمعرفة ما إذا كانت الوظيفة قد تم استدعاؤها.
كقاعدة عامة ، عندما تكتب رمزًا واضحًا وواضحًا ، من الضروري منع JavaScript من رمي الأخطاء ، ومن الضروري أيضًا منع المطورين الآخرين من إلقاء أخطاء إليك Wtferror!
احفظ حالة الإغلاق
تمامًا مثلما يتم استدعاء وظيفة باسمها ، يتم تمرير المعلمات ، وعندما يتم استدعاء تعبير الوظيفة على الفور ، يتم تمرير المعلمات. يمكن استخدام تعبير الوظيفة على الفور لقفل القيم وحفظ الحالة بشكل فعال في هذا الوقت ، لأن أي وظيفة محددة في دالة يمكن أن تستخدم المعلمات والمتغيرات التي يتم تمريرها بواسطة الوظيفة الخارجية (تسمى هذه العلاقة الإغلاق).
// قد لا تعمل كما تعتقد ، لأن قيمة "أنا مقفل أبدًا. // على العكس من ذلك ، عند النقر فوق كل رابط (تم تنفيذ الحلقة بشكل جيد) ، فإن العدد الإجمالي لجميع العناصر سوف يظهر ، // لأن هذه هي القيمة الحقيقية لـ "أنا في هذا الوقت. var elems = document.getElementsByTagName ('a') ؛ for (var i = 0 ؛ i <elems.length ؛ i ++) {elems [i] `أنا قفل في` lockedInindex`. // عندما يتم تنفيذ الحلقة ، على الرغم من أن القيمة الرقمية للقيمة "I I" هي مجموع جميع العناصر ، في كل مرة يطلق عليها تعبير الوظيفة ، فإن قيمة "LockedInIndex` في Iife هي القيمة التي تم تمريرها إليها بواسطة" أنا ، لذلك عندما يتم النقر على الرابط ، يتم ظهور القيمة الصحيحة. var elems = document.getElementsByTagName ('a') ؛ for (var i = 0 ؛ i <elems.length ؛ i ++) {(function (lockedInIndex) {elems [i] .addeventListener ('click' ، function (e) { }) (i) ؛} // يمكنك أيضًا استخدام iife مثل ما يلي ، فقط باستخدام الأقواس لتضمين وظيفة معالجة النقر ، وليس بما في ذلك `addeventListener`. // بغض النظر عن الطريقة ، يمكن قفل كلا الأمثلة مع iife ، لكنني وجدت أن المثال السابق أكثر قابلية للقراءة var elems = document.getElementSbyTagName ('a') ؛ لـ (var i = 0 ؛ i <elems.length ؛ i ++) {elems [i] ALERT ("أنا رابط #' + lockedInindex) ؛ }تذكر أنه في هذين المثالين الأخيرين ، يمكن أن يصل LockedInIndex إلى I دون أي مشاكل ، ولكن باستخدام معرف مختلف المسماة كمعلمة للوظيفة يمكن أن يجعل المفهوم أسهل في التفسير.
واحدة من أهم مزايا تنفيذ وظيفة على الفور هي أنه حتى لو لم يتم تسميتها أو مجهول ، يمكن استدعاء تعبير الوظيفة على الفور دون استخدام معرف ، ويمكن استخدام إغلاق دون تلوث المتغير الحالي.
ما هي المشكلة في وظيفة مجهولة المصدر ذاتية ("وظيفة مجهولة المصدر ذاتية")؟
لقد رأيت أنه تم ذكره عدة مرات ، لكن لا يزال يتم شرحه بشكل واضح ، أقترح تغيير المصطلح إلى "تعبير الوظيفة الملقح على الفور" ، أو ، إذا كنت تحب الاختصار.
ما هو التعبير الوظيفي الملقح على الفور؟ يجعل تعبير وظيفة تسمى على الفور. إنه مثل تعبير وظيفة يقودك إلى الاتصال.
أعتقد أن أعضاء مجتمع JavaScript يجب أن يكونوا قادرين على قبول المصطلحات ، والتعبير عن الوظيفة الملقح على الفور و IAFE في مقالاتهم أو عباراتهم ، لأنني أشعر أنه من الأسهل فهم المفهوم ، ومصطلح "وظيفة مجهولة المصدر ذاتيًا" غير دقيق في الحقيقة بما فيه الكفاية.
// فيما يلي وظيفة تنفيذ ذاتية ، والتي تدعو بشكل متكرر وظيفتها الخاصة foo () {foo () ؛} ؛ // هذه وظيفة مجهولة المصدر ذاتيا. نظرًا لأنه لا يحتوي على معرف ، يجب أن يستخدم خاصية `encuments.callee` أن نسميها نفسها var foo = function () {enduments.callee () ؛} ؛ وظيفة مجهولة مثل هذا ، حتى لو لم تكن تنفيذ الذات لأنها لا تسمي نفسها. ثم ، كان يسمى فقط على الفور. (function () {/*code*/} ()) ؛ // إضافة معرف إلى تعبير دالة (أي ، فإن إنشاء وظيفة تسمية) سيكون مفيدًا لتصحيح الأخطاء. بمجرد تسمية ، لن تكون الوظيفة مجهولة. (وظيفة foo () {/ * code */} ()) ؛ // يمكن أيضًا تنفيذ IIFEs بمفردها ، على الرغم من أنه ربما لا يكون النمط الأكثر فائدة (function () {enduments.callee () ؛} ()) (وظيفة foo () {foo () ؛} ()) رائع ، huh؟ (وظيفة foo () {foo () ؛} ()) ؛نأمل أن يمنحك المثال أعلاه فهمًا أوضح لمصطلح التنفيذ الذاتي الذي يعد مضللاً إلى حد ما لأنه لا ينفذ وظيفته الخاصة ، على الرغم من أن الوظيفة قد تم تنفيذها. وبالمثل ، ليست هناك حاجة للإشارة إلى وظائف مجهولة ، لأن تعبير الوظيفة الذي تم استدعاؤه على الفور يمكن أن يكون إما وظيفة محددة أو دالة مجهولة المصدر.
الأخير: وضع الوحدة النمطية
عندما أسمي تعبير وظيفة ، إذا أذكّر نفسي بنمط الوحدة النمطية مرة واحدة على الأقل ، فمن المرجح أن أتجاهله. إذا لم يكن لديك نمط الوحدة النمطية في JavaScript ، فهذا يشبه إلى حد كبير مثالي أدناه ، ولكن قيمة الإرجاع تستخدم كائن بدلاً من الوظيفة.
var counter = (function () {var i = 0 ؛ return {get: function () {return i ؛} ، set: function (val) {i = val ؛} ، styp: function () {return ++ i ؛}}} ()) ؛ counter.get () ؛ // 0 counter.set (3) ؛ counter.increment () ؛ // 4 counter.increment () ؛ // 5 conuter.i ؛ // undefined (`i ليس خاصية للكائن الذي تم إرجاعه)طريقة وضع الوحدة النمطية ليست فقط قوية جدًا ولكنها بسيطة أيضًا. مع القليل جدًا من التعليمات البرمجية ، يمكنك الاستفادة الفعالة من التسمية المتعلقة بالطرق والسمات. في كائن ، يقلل تنظيم جميع رموز الوحدة النمطية من تلوث المتغيرات العالمية ويخلق استخدام المتغيرات.