مقدمة
في العديد من اللغات التقليدية (C/C ++/Java/C#، إلخ) ، توجد وظائف كمواطنين من الدرجة الثانية. يمكنك فقط إعلان وظيفة مع الكلمات الرئيسية للغة ثم تسميتها. إذا كنت بحاجة إلى تمرير الوظيفة كمعلمة إلى وظيفة أخرى ، أو تعيين قيمة لمتغير محلي ، أو كقيمة إرجاع ، فأنت بحاجة إلى تحقيق اختراق من خلال أساليب خاصة مثل مؤشر الوظيفة والوكيل (مندوب).
في عالم JavaScript ، الوظائف هي مواطنين من الدرجة الأولى. ليس لديهم فقط جميع طرق استخدام الوظائف التقليدية (الإعلانات والمكالمات) ، ولكن يمكنهم أيضًا تعيين القيم ، ومرور المعلمات ، والعودة مثل القيم البسيطة. وتسمى هذه الوظائف أيضًا وظائف من الدرجة الأولى. ليس ذلك فحسب ، فإن الوظائف في JavaScript تعمل أيضًا كمشاركات فئة ، كما أنها مثيلات من فئة الوظائف. هذه الهويات المتعددة تجعل وظائف JavaScript مهمة للغاية.
1. مستوى دخول وظيفة JavaScript
مثل اللغات العادية ، تتبع وظائف JavaScript أيضًا مبدأ الإعلان أولاً ثم استخدامها. يمكن أن تحتوي أسماء الوظائف فقط على رسائل أو أرقام أو سطحية أو $ ، ولا يمكن أن تبدأ بالأرقام. هناك طريقتان شائعتان لإعلان وظائف:
نسخة الكود كما يلي:
// إعلان وظيفة myfunc مباشرة
وظيفة myfunc (/ * الوسائط */) {
}
// تعيين وظائف مجهولة للمتغير المحلي myfunc
var myfunc = function (/ * ediuments */) {
}
لاحظ أن هناك اختلافات دقيقة في طريقتي إعلان وظيفتين أعلاه: الطريقة الأولى هي وظيفة تسمية عند إعلانها ، سواء كانت وظيفة معلنة من قبل ، أو بعد المكالمة ، أو حتى الموقع الذي لن يتم تنفيذه (على سبيل المثال ، بعد بيان الإرجاع أو في فرع لن يكون صحيحًا أبدًا) ، فإنه يمكن الوصول إليه في النطاق بأكمله ؛ الطريقة الثانية هي تعيين وظائف مجهولة للمتغيرات. بالمعنى الدقيق للكلمة ، هذا ليس إعلانًا وظيفيًا بل تعبيرًا عن الوظيفة. قبل التخصيص ، لا يمكن الوصول إلى هذه الوظيفة بواسطة أي رمز ، مما يعني أنه يجب إكمال المهمة قبل المكالمة ، وإلا فإن الخطأ سيظهر عند الاتصال: "Typeerror: غير محدد ليست وظيفة". على سبيل المثال:
نسخة الكود كما يلي:
myfunc1 () ؛ // يمكن تسميتها بشكل طبيعي ، لأن MyFUNC1 يستخدم طريقة إعلان مباشر
وظيفة myfunc1 () {
}
myfunc2 () ؛ // error typeerror: غير محدد ليس وظيفة
var myfunc2 = function () {
} ؛
يتم استدعاء طريقة الاتصال الأساسية للوظائف بنفس الطريقة كما في اللغات التقليدية: myfunc (). تدعم وظائف JavaScript أيضًا المكالمات العودية المباشرة أو غير المباشرة. على سبيل المثال ، يمكن تنفيذ وظيفة Fibonacci الكلاسيكية في JavaScript مثل هذا:
نسخة الكود كما يلي:
وظيفة fib (n) {
if (n == 1 || n == 2) {
العودة 1 ؛
} آخر {
إرجاع fib (n - 2) + fib (n - 1) ؛
}
}
يمكن للوظائف في JavaScript التعامل مع معلمات طول المتغير. لديهم جميعًا متغير محلي يسمى الوسائط داخل الوظيفة. إنه كائن يشبه المصفوفة يحتوي على جميع المعلمات التي تم تمريرها عند الاتصال ، ولديها سمة طول لتمثيل عدد المعلمات. على سبيل المثال:
نسخة الكود كما يلي:
اختبار الوظيفة () {
التنبيه (الحجج. الطول) ؛
}
اختبار (1) ؛ // 1
اختبار (1 ، 'a') ؛ // 2
اختبار (صحيح ، [] ، {}) ؛ // 3 استخدم وسيطات لتنفيذ وظائف مشابهة لـ printf في لغة C ، ويمكن أيضًا استخدامها لتنفيذ تعدد الأشكال للطرق.
2. وظائف JavaScript المتقدمة
2.1 وظائف مجهولة ومتداخلة
في JavaScript ، يمكنك إعلان وظيفة بدون اسم ، تسمى وظيفة مجهولة (وظيفة مجهولة). في الوقت نفسه ، يسمح JavaScript أيضًا بإعلان الوظائف داخل الوظائف ، وتسمى الوظائف المتداخلة ، ونطاق الوظائف المتداخلة هو وظيفة الوالدين بأكملها.
في الجزء السابق من إعلان الوظيفة ، رأيت استخدام وظائف مجهولة ووظائف متداخلة. نظرًا لأن الوظائف المجهولة ليس لها اسم ، فإنها لن تقدم متغيرات جديدة لتلويث السياق وسيحضر نطاقات متغيرة جديدة. لذلك ، غالبًا ما تستخدم وظائف مجهولة لمنع التلوث البيئي العالمي.
هناك كائن عالمي خاص في وقت تشغيل JavaScript. هذا الكائن يخزن الوظائف والمتغيرات العالمية. في التطوير الفعلي ، غالبًا ما يتم استخدام العديد من مكتبات الطرف الثالث أو ملفات JS متعددة. إذا قمت بتقديم متغيرات مكررة أو إعلانات الوظيفة في الكائن العالمي ، فسيؤدي ذلك إلى ارتباك في تنفيذ الكود. على سبيل المثال ، يتم تقديم ملفان JS على التوالي ، ويتم تعريف سجل الوظائف الخاص بهما على أنه الاستخدام الداخلي. ستؤدي الوظيفة الثانية المقدمة إلى الكتابة فوق تعريف الأول ولن ترمي أي أخطاء. قد يتسبب استدعاء وظيفة السجل في التنفيذ اللاحق في حدوث خطأ. في هذا الوقت ، يمكن أن يؤدي استخدام دالة مجهولة المصدر لفائف المنطق في JS بأكمله تجنب هذا الخطأ. تم استخدام هذه الطريقة من قبل معظم مكتبات JS مفتوحة المصدر.
نسخة الكود كما يلي:
(Function () {// وظيفة مجهولة
سجل الوظائف (msg) {
console.log (msg) ؛
}
// رموز أخرى
} ()) ؛ // تنفيذ على الفور
الرمز أعلاه هو مثال بسيط. يقتصر نطاق وظيفة السجل على هذه الوظيفة المجهولة. يتم تضمين الوظيفة المجهولة في زوج من الأقواس () في الخارج لتشكيل تعبير وظيفة. قيمة التعبير هي وظيفة ، تليها زوج من الأقواس للإشارة إلى أن الوظيفة يتم تنفيذها على الفور ، بحيث يمكن تنفيذ الكود الأصلي بشكل طبيعي. ومع ذلك ، فإن الوظائف التي تم الإعلان عنها بهذه الطريقة ، فإن المتغيرات التي تم إعلانها من خلال VAR ، وما إلى ذلك الداخلية ولا يمكن الوصول إليها بواسطة أي رمز آخر غير الوظائف المجهولة. إذا كنت بحاجة إلى فضح بعض الوظائف كواجهات ، فهناك عدة طرق:
نسخة الكود كما يلي:
var mylib = (function (Global) {
سجل الوظائف (msg) {
console.log (msg) ؛
}
log1 = سجل ؛ // الطريقة 1: استخدم السلوك الافتراضي للإعلان المتغير دون VAR ليصبح متغيرًا عالميًا في Log1 (غير موصى به)
global.log2 = log ؛ // الطريقة 2: إضافة سمة log2 مباشرة إلى الكائن العالمي وقم بتعيينها إلى وظيفة السجل (موصى بها)
إرجاع {// الطريقة 3: إرجاع سلسلة من كائنات مجموعة وظيفة الواجهة من خلال وظائف مجهولة وتعيينها إلى المتغير العالمي mylib (موصى به)
السجل: سجل
} ؛
} (نافذة)) ؛
2.2 وظيفة عالية الترتيب
إذا تم استخدام وظيفة كمعلمة أو قيمة إرجاع ، فسيتم تسمى وظيفة الترتيب العالي. يمكن استخدام الوظائف في JavaScript كوظائف ذات ترتيب أعلى ، وهي أيضًا ميزة من النوع الأول من الوظائف. دعنا نحلل هذين طريقتي الاستخدام أدناه.
نسخة الكود كما يلي:
وظيفة السلبية (n) {
العودة -n ؛ // خذ القيمة المعاكسة لـ n
}
وظيفة مربع (ن) {
العودة n*n ؛ // مربع N
}
عملية الوظيفة (NUMS ، رد الاتصال) {
var result = [] ؛
لـ (var i = 0 ، length = nums.length ؛ i <length ؛ i ++) {
النتيجة [i] = رد الاتصال (nums [i]) ؛ // تمرير جميع العناصر في Nums Array إلى رد الاتصال للمعالجة ، وحفظ قيمة الإرجاع كنتيجة
}
نتيجة العودة
}
var nums = [-3 ، -2 ، -1 ، 0 ، 1 ، 2 ، 3 ، 4] ؛
var n_neg = العملية (nums ، سلبية) ؛
// n_neg = [3 ، 2 ، 1 ، 0 ، -1 ، -2 ، -3 ، -4] ؛
var n_square = العملية (nums ، square) ؛
// n_square = [9 ، 4 ، 1 ، 0 ، 1 ، 4 ، 9 ، 16] ؛
يعرض الرمز أعلاه مثالًا على تمرير وظيفة كمعلمة في مكالمة عملية أخرى. في تنفيذ وظيفة العملية ، يُعتبر رد الاتصال بمثابة مربع أسود ، مسؤول عن تمرير المعلمة إليها ، ثم الحصول على قيمة الإرجاع. إن التنفيذ المحدد لاستدعاء رد الاتصال غير واضح قبل المكالمة. فقط عند تنفيذ 20 و 22 سطرًا ، يتم تمثيل رد الاتصال بواسطة سلبي أو مربع ، على التوالي ، ويتم أخذ كل عنصر ذات قيمة معاكسة أو قيمة مربعة.
نسخة الكود كما يلي:
مولد الوظيفة () {
var i = 0 ؛
وظيفة الإرجاع () {
إرجاع i ++ ؛
} ؛
}
var gen1 = generator () ؛ // احصل على مولد أرقام طبيعي
var gen2 = generator () ؛ // احصل على مولد رقم طبيعي آخر
var r1 = gen1 () ؛ // r1 = 0
var r2 = gen1 () ؛ // r2 = 1
var r3 = gen2 () ؛ // r3 = 0
var r4 = gen2 () ؛ // r4 = 1
يعرض الرمز أعلاه مثالًا على استخدام وظيفة كقيمة إرجاع. المولد هو وظيفة مولد الأرقام الطبيعية ، وقيمة الإرجاع هي وظيفة المولد الأرقام الطبيعية. في كل مرة يتم استدعاء المولد ، سيتم إرجاع وظيفة مجهولة كنتيجة. هذه الوظيفة المجهولة تُرجع كل رقم طبيعي بدوره عندما يطلق عليه بالفعل. سيزداد المتغير I في المولد بمقدار 1 في كل مرة تسمى هذه الوظيفة المجهولة ، والتي هي في الواقع إغلاق. دعونا نقدم الإغلاق أدناه.
2.3 الإغلاق
الإغلاق ليس مفهومًا جديدًا ، والعديد من اللغات الوظيفية تستخدم الإغلاق. في JavaScript ، عند استخدام المتغيرات في نطاق الوظائف الخارجية في وظيفة مضمنة ، يمكنك استخدام الإغلاق. استخدم قياسًا شائع الاستخدام لشرح العلاقة بين الإغلاق والفئة: الفئة هي بيانات ذات وظائف ، والإغلاق هو وظيفة مع البيانات.
تتميز المتغيرات المستخدمة في الإغلاق بخاصية لا يتم إطلاقها عند إرجاع وظيفة الوالدين ، ولكن تنتهي بنهاية دورة حياة الإغلاق. على سبيل المثال ، كما هو الحال في مثال المولد في القسم السابق ، يستخدم Gen1 و Gen2 متغيرات مستقلة أنا على التوالي (عندما يتم زيادة Gen1 I بمقدار 1 ، لن يتأثر I Gen2 ، والعكس صحيح). طالما أن المتغيرين Gen1 أو Gen2 لا يتم جمعهم بواسطة محرك JavaScript ، فلن يتم إطلاق متغيراتهما الخاصة. في برمجة JavaScript ، يتم استخدام عمليات الإغلاق دون وعي. هذه الميزة من الإغلاق سهلة الاستخدام ، ولكنها تؤدي بسهولة إلى مشاكل تسرب الذاكرة. على سبيل المثال:
نسخة الكود كما يلي:
var elem = document.getElementById ('test') ؛
elem.addeventListener ('click' ، function () {
ALERT ("أنت نقر" + elem.tagname) ؛
}) ؛
الغرض من هذا الرمز هو عرض اسم التسمية الخاص به عند النقر فوق عقدة. يسجل وظيفة مجهولة كدالة معالجة الأحداث النقر لعقدة DOM. تتم الإشارة إلى كائن DOM ELEM في الوظيفة ، والتي تشكل إغلاقًا. سيؤدي ذلك إلى إنشاء مرجع دائري ، أي: dom-> clossary-> dom-> clossary ... لن يتم إصدار كائن DOM قبل إصدار الإغلاق ؛ ويوجد الإغلاق كدالة معالجة الأحداث لكائن DOM ، وبالتالي لن يتم إصدار الإغلاق قبل إصدار كائن DOM. حتى إذا تم حذف كائن DOM في شجرة DOM ، نظرًا لوجود هذا المرجع الدائري ، فلن يتم إصدار كائن DOM ولا الإغلاق. يمكن تجنب تسرب الذاكرة هذا باستخدام الطرق التالية:
نسخة الكود كما يلي:
var elem = document.getElementById ('test') ؛
elem.addeventListener ('click' ، function () {
ALERT ('لقد نقرت' + this.tagname) ؛ // لا مزيد من الإشارة مباشرة إلى متغير ELEM
}) ؛
في الكود أعلاه ، يتم استخدام هذا بدلاً من Elem (يشير هذا المؤشر إلى عنصر DOM نفسه في وظيفة معالجة الأحداث DOM) ، بحيث لم يعد وقت تشغيل JS يعتقد أن الوظيفة تستخدم متغيرات الفئة الأصل ، لذلك لم تعد تشكل إغلاقًا.
ستجلب عمليات الإغلاق أيضًا العديد من مشاكل تسرب الذاكرة المماثلة. يمكنك فقط الانتباه إلى عمليات الإغلاق عند كتابة التعليمات البرمجية ومحاولة تجنب مثل هذه المشكلات.
2.4 مُنشئ فئة
تُستخدم وظائف JavaScript أيضًا كمشاركات فئة ، بحيث يمكنك استخدام الكلمة الرئيسية الجديدة لإنشاء مثيل للفئة طالما كنت تعلن وظيفة.
نسخة الكود كما يلي:
وظيفة الشخص (الاسم) {
this.name = name ؛
this.toString = function () {
إرجاع "مرحبا ،" + this.name + "!" ؛
} ؛
}
var p = شخص جديد ('GhosttheAven') ؛
تنبيه (P) ؛ // مرحبا ، Ghosttheven! في المثال أعلاه ، يتم استخدام وظيفة الشخص كمؤسس فئة. في هذا الوقت ، يشير هذا إلى كائن المثيل الذي تم إنشاؤه حديثًا ، ويمكنه إضافة خصائص وطرق إلى المثيل. للحصول على برمجة JavaScript المفصلة الموجهة للكائنات ، يرجى الرجوع إلى هذه المقالة. ما أريد أن أقوله هنا هو مشكلة قيمة الإرجاع عند استخدام وظائف JavaScript كملكات فئة.
نسخة الكود كما يلي:
وظيفة myClass (الاسم) {
this.name = name ؛
اسم العودة // قيمة إرجاع المُنشئ؟
}
var obj1 = new myClass ('foo') ؛
var obj2 = myClass ('foo') ؛
var obj3 = new myClass ({}) ؛
var obj4 = myClass ({}) ؛
المُنشئ أعلاه مميز تمامًا ، مع بيان العودة ، فما هي الأشياء التي تشير إليها OBJ1 ~ OBJ4؟ النتيجة الفعلية هي:
نسخة الكود كما يلي:
OBJ1 = كائن myClass
OBJ2 = 'foo'
OBJ3 = {}
OBJ4 = {}
تم شرح الأسباب المحددة في هذه المقالة ، ولن أكررها في هذه المقالة. نظرًا لأن المُنشئين الذين لديهم قيم الإرجاع سوف ينتجون نتائج غريبة ، فلا تستدعي عبارات الإرجاع مع قيم الإرجاع في المُنشئ (يمكن إجراء عائد فارغ).
3. JavaScript وظائف وحش مستوى
مرحبًا بكم في منطقة تدريس وظائف المستوى الوحش ، حيث ستحصل على كيفية مواجهة الوحش القديم بهدوء وحرية. . .
3.1 فئة الوظيفة
هناك فئة مدمجة تسمى الوظيفة في وقت تشغيل JavaScript. إن إعلان وظيفة مع الكلمة الرئيسية للوظيفة هو في الواقع اختصار لإنشاء كائنات فئة الوظائف. تحتوي جميع الوظائف على جميع طرق فئة الوظائف ، مثل الاتصال ، تطبيق ، Bind ، وما إلى ذلك ، يمكنك التحقق من هذا العبارة من خلال الكلمة الرئيسية من مثيل.
نظرًا لأن الوظيفة فئة ، فإن منشئها هو وظيفة (إنها في حد ذاتها كائن من فئة الوظائف) ، ويجب إنشاء كائن الوظيفة من خلال الكلمة الرئيسية الجديدة. الوحش الأول هنا ، وهو كيفية إنشاء وظيفة باستخدام فئة الوظائف. بناء جملة الوظيفة كما يلي:
نسخة الكود كما يلي:
وظيفة جديدة ([arg1 [، arg2 [، ... argn]] ،] functionBody)
حيث Arg1 و Arg2 و ... Argn عبارة عن سلسلة ، تمثل اسم المعلمة ، و FunctionBody هي أيضًا سلسلة ، تمثل جسم الوظيفة. اسم المعلمة السابق هو أكثر أو أقل. سيعامل مُنشئ الوظيفة المعلمة الأخيرة كجسم الوظيفة ، والمعلمات السابقة.
نسخة الكود كما يلي:
var func1 = وظيفة جديدة ('name' ، 'return "hello ،" + name + "!" ؛') ؛
FUNC1 ('Ghosttheaven') ؛ // مرحبا ، Ghosttheven!
تقوم الطريقة أعلاه ببناء وظيفة من خلال وظيفة ، وهي بالضبط نفس الوظائف الأخرى المعلنة مع الكلمة الرئيسية للوظيفة.
رؤية هذا ، قد يسأل الكثير من الناس لماذا هناك حاجة إلى مثل هذا الوحش؟ "ما هو موجود معقول" ، فئة الوظائف لها غرضها الفريد. يمكنك استخدامه لإنشاء منطق وظائف مختلف ديناميكيًا ، أو استبدال وظائف وظيفة eval ، والحفاظ على البيئة الحالية من التلوث*.
3.2 وظيفة التحديث الذاتي
في العديد من اللغات ، بمجرد إعلان وظيفة ما ، لا يمكن أن تعلن وظيفة الاسم نفسه مرة أخرى ، وإلا ستحدث أخطاء بناء الجملة. لا يمكن إعلان الوظائف في JavaScript مرارًا وتكرارًا ، ولكن أيضًا تحديث نفسها. الوحش الذي آكله هنا!
نسخة الكود كما يلي:
وظيفة selfupdate () {
window.selfupdate = function () {
تنبيه ("المدى الثاني!") ؛
} ؛
تنبيه ("أول تشغيل!") ؛
}
selfupdate () ؛ // أول تشغيل!
selfupdate () ؛ // المدى الثاني! يمكن استخدام هذه الوظيفة للمنطق الذي يتم تشغيله مرة واحدة فقط ، وبعد التشغيل الأول ، يتم استبداله بقطعة جديدة من المنطق.
ملخص
وظائف JavaScript قوية للغاية. بينما يحل العديد من المشكلات بشكل جميل ، فإنها تجلب أيضًا العديد من المشكلات السلبية. عادة ما تستخدم وظائف مستوى الوحش مع استخدام غير معروف. ما لم يكن ذلك ضروريًا بشكل خاص ، فإنه سيؤدي إلى صعوبات في قراءة الكود ويؤثر على كفاءة تطوير الفريق.
* تم تقديم وضع صارم في ECMAScript الجديد. في الوضع الصارم ، يتم تقييد وظيفة eval إلى حد كبير ويمكن أن تضمن أن البيئة غير ملوثة.
هل تفهم ، إنه عملي للغاية. إذا كان هناك أي أماكن مفقودة ، فالرجاء إعطائي بعض النصائح وتقدم التقدم معًا.