كلمة "حفرة" تعني "فخ". نظرًا لطبيعة "اللغة الضعيفة" لـ JavaScript ، فهي فضفاضة ومرنة للغاية أثناء الاستخدام ، ولكن من السهل للغاية "ضرب". غالبًا ما تكون هذه الحفر مخفية ، لذلك يجب أن تبقي عينيك مفتوحة لتسهيل الإبحار على طريق التعلم وتطبيق JS.
1. المتغيرات العالمية
JavaScript يدير النطاقات من خلال الوظائف. المتغيرات المعلنة داخل وظيفة ما هي فقط داخل هذه الوظيفة وليست متوفرة خارج الوظيفة. من ناحية أخرى ، يتم الإعلان عن المتغيرات العالمية خارج أي وظيفة أو تُستخدم ببساطة وببساطة دون إعلان.
"استخدمه ببساطة دون إعلان" يعني أن المتغيرات يتم الإعلان عنها دون استخدام الكلمة الرئيسية VAR. لقد أوضحنا بالفعل أن طريقة تجنب توليد المتغيرات العالمية ضمنيًا هي إعلان المتغيرات مع الكلمة الرئيسية VAR قدر الإمكان.
ولكن هل تعتقد أنه من المقبول استخدام VAR؟ دعونا نلقي نظرة على هذه الحفرة:
نسخة الكود كما يلي:
وظيفة foo () {
var a = b = 0 ؛
// جسم...
}
ربما ما تتوقعه هو متغيران محليان ، لكن B هو متغير عالمي حقيقي. لماذا؟ لأن عملية المهمة من اليمين إلى اليسار ، لذلك هذا يعادل:
نسخة الكود كما يلي:
وظيفة foo () {
var a = (b = 0) ؛
// جسم...
}
لذلك B هو متغير عالمي.
املأ الحفرة: إعلانات متغيرة ، من الأفضل أن تأتي واحداً تلو الآخر ، لا تفعل الجملة ~ _ ~ ؛
2. إعلان متغير
دعونا نلقي نظرة على الحفرة أولاً:
نسخة الكود كما يلي:
myName = "Global" ؛
وظيفة foo () {
تنبيه (myName) ؛
var myName = "Local" ؛
تنبيه (myName) ؛
}
foo () ؛
للوهلة الأولى ، نتوقع أن تتوقع أن تكون نتائج التنبيهات "عالمية" و "محلية" ، على التوالي ، لكن النتائج الحقيقية "غير محددة" و "محلية". لماذا؟ نظرًا لأن المتغيرات يتم الإعلان عنها في نفس النطاق (نفس الوظيفة) ، ويتم تحليلها أولاً من أعلى النطاق.
لذلك قد يبدو سلوك تنفيذ مقتطف الرمز أعلاه هكذا:
نسخة الكود كما يلي:
وظيفة foo () {
var myname ؛
تنبيه (myName) ؛ // "غير محدد"
myName = "Local" ؛
تنبيه (myName) ؛ // "محلي"
}
استخدم حفرة أخرى لاختبار ما إذا كنت تفهم حقًا ما قبل التزوير:
نسخة الكود كما يلي:
if (! ("A" في النافذة)) {
var a = 1 ؛
}
تنبيه (أ) ؛
يتم تقديم إعلان المتغير إلى أعلى الكود ولم يتم تعيينه بعد. بعد ذلك ، أدخل البيان if ، وحكم على أن "A" في النافذة قد تم إعلانها (تم إعلان A كمتغير عالمي) ، وبالتالي فإن نتيجة حساب بيان الحكم خاطئة ، وستقفز البيان ، وبالتالي فإن قيمة A غير محددة.
نسخة الكود كما يلي:
var a ؛ // "غير محدد"
console.log ("A" في النافذة) ؛ // حقيقي
if (! ("A" في النافذة)) {
var a = 1 ؛ // لم ينفذ
}
تنبيه (أ) ؛ // "غير محدد"
املأ الحفرة: من الأفضل وضع الإعلان المتغير يدويًا في الجزء العلوي من النطاق. بالنسبة للمتغيرات التي لا يمكن تعيينها في الوقت الحالي ، يمكنك استخدام طريقة الإعلان أولاً ثم تعيين القيم.
3. إعلان الوظيفة
يتم أيضًا تقديم إعلانات الوظيفة إلى أعلى النطاق ، ويتم تحليلها وتقييمها قبل تحليل أي تعبيرات وبيانات وتقييمها.
نسخة الكود كما يلي:
تنبيه (typeof foo) ؛ // "وظيفة"
وظيفة foo () {
// جسم...
}
يمكنك مقارنتها:
نسخة الكود كما يلي:
تنبيه (typeof foo) ؛ // "غير محدد"
var foo = function () {
// جسم...
} ؛
إذا فهمت هذا المبدأ ، فهل ستظل ستقع في المزالق التالية؟
نسخة الكود كما يلي:
اختبار الوظيفة () {
تنبيه ("1") ؛
}
امتحان()؛
اختبار الوظيفة () {
تنبيه ("2") ؛
}
امتحان()؛
عند تشغيل مقتطف الرمز أعلاه ، فإن النوافذ المنبثقة التي تراها هي "2" ، لماذا لا "1" و "2" على التوالي؟ الأمر بسيط للغاية ، يتم تحليل إعلان الاختبار قبل الاختبار (). نظرًا لأن الأخير يتجاوز الأول ، فإن نتيجة كلا الإعدام هي "2".
املأ الحفرة: في معظم الحالات ، أستخدم تعبيرات الوظائف بدلاً من إعلانات الوظيفة ، خاصة في بعض كتل البيانات.
4. تعبيرات الوظيفة
دعونا نلقي نظرة على تعبير الوظيفة المسمى أولاً. بالطبع ، يجب أن يكون له اسم ، على سبيل المثال:
انسخ الرمز كما يلي: var bar = function foo () {
// جسم...
} ؛
تجدر الإشارة إلى أن اسم الوظيفة مرئي فقط لوظائفه في الداخل. مثل المزالق التالية:
نسخة الكود كما يلي:
var bar = function foo () {
foo () ؛ // تشغيل بشكل طبيعي
} ؛
foo () ؛ // خطأ: مرجعية
املأ الحفرة: حاول استخدام تعبيرات الوظائف المسماة بأقل قدر ممكن (باستثناء بعض أغراض التكرار والتصحيح) ، وعدم استخدام أسماء الوظائف خارجيًا.
5. وظيفة التنفيذ الذاتي
بالنسبة إلى تعبيرات الوظائف ، يمكنك التنفيذ عن طريق إضافة () بعد ذلك ، ويمكن تمرير المعلمات بين قوسين ، في حين أن إعلان الوظيفة لا يمكن. حفرة:
نسخة الكود كما يلي:
// (1) هذا مجرد مشغل تجميع ، وليس مكالمة وظيفة!
// لذلك لم يتم تنفيذ الوظيفة هنا ، فهي لا تزال بيانًا
وظيفة foo (x) {
تنبيه (x) ؛
} (1) ؛
يتم تنفيذ مقتطفات الكود التالية بشكل منفصل ، وتظهر النافذة المنبثقة "1". لأنه قبل (1) ، فهي تعبيرات وظيفية ، وبالتالي فإن () هنا ليس مشغل تجميع ، ولكن المشغل ، يشير إلى تنفيذ المكالمة.
انسخ الرمز كما يلي: // التعبير الوظيفي المجهول القياسي
var bar = function foo (x) {
تنبيه (x) ؛
} (1) ؛
// السابق () يحول إعلان الوظيفة إلى تعبير
(وظيفة foo (x) {
تنبيه (x) ؛
}) (1) ؛
// الكامل () تعبير
(وظيفة foo (x) {
تنبيه (x) ؛
} (1)) ؛
// تعبير جديد
وظيفة جديدة foo (x) {
تنبيه (x) ؛
} (1) ؛
// && ، || ،! ، +، -، ~ إلخ
// لذلك بمجرد أن يعلم المحللون أن أحدهم هو تعبير ، فإن الآخرين سوف يتخلفون عن التعبيرات.
True && وظيفة foo (x) {
تنبيه (x) ؛
} (1) ؛
املأ الحفرة: مفتاح هذه الحفرة هو معرفة جوهر جميع أنواع تعبيرات الوظائف.
6. الإغلاق في الحلقة
ما يلي يوضح مأزق شائع:
نسخة الكود كما يلي:
<! doctype html>
<html lang = "en">
<head>
<meta charset = "utf-8">
<title> وثيقة </title>
</head>
<body>
<h3> عند النقر فوق الروابط أدناه ، أظهر رقم تسلسله </h3>
<ul>
<li> <a href = " #"> link #0 </a> </li>
<li> <a href = " #"> الرابط #1 </a> </li>
<li> <a href = " #"> الرابط #2 </a> </li>
<li> <a href = " #"> الرابط #3 </a> </li>
<li> <a href = " #"> الرابط #4 </a> </li>
</ul>
</body>
</html>
نسخة الكود كما يلي:
var links = document.getElementSbyTagName ("ul") [0] .getElementSbyTagName ("a") ؛
لـ (var i = 0 ، l = links.length ؛ i <l ؛ i ++) {
الروابط [i] .onclick = function (e) {
E.PreventDefault () ؛
ALERT ("أنت تنقر على الرابط #" + i) ؛
}
}
نتوقع أنه عند النقر على الرابط I-Th ، نحصل على قيمة الفهرس I في هذا التسلسل. ومع ذلك ، بغض النظر عن الرابط الذي يتم النقر عليه ، نحصل على النتيجة النهائية لـ I بعد الحلقة: "5".
اشرح السبب: عندما يتم استدعاء التنبيه ، يحافظ تعبير الوظيفة المجهول في الحلقة على إشارة إلى المتغير الخارجي I (الإغلاق). في هذا الوقت ، انتهت الحلقة وتم تعديل قيمة I إلى "5".
املأ الحفرة: من أجل الحصول على النتيجة المرجوة ، تحتاج إلى إنشاء نسخة من المتغير I في كل حلقة. ما يلي يوضح النهج الصحيح:
نسخة الكود كما يلي:
<head>
<meta charset = "utf-8">
<title> وثيقة </title>
</head>
<body>
<h3> عند النقر فوق الروابط أدناه ، أظهر رقم تسلسله </h3>
<ul>
<li> <a href = " #"> link #0 </a> </li>
<li> <a href = " #"> الرابط #1 </a> </li>
<li> <a href = " #"> الرابط #2 </a> </li>
<li> <a href = " #"> الرابط #3 </a> </li>
<li> <a href = " #"> الرابط #4 </a> </li>
</ul>
</body>
</html>
نسخة الكود كما يلي:
var links = document.getElementSbyTagName ("ul") [0] .getElementSbyTagName ("a") ؛
لـ (var i = 0 ، l = links.length ؛ i <l ؛ i ++) {
الروابط [i] .onclick = (function (index) {
وظيفة الإرجاع (هـ) {
E.PreventDefault () ؛
التنبيه ("أنت تنقر على الرابط #" + الفهرس) ؛
}
})(أنا)؛
}
كما ترون ، فإن شكل (function () {...}) () هو التنفيذ الذاتي للوظيفة المذكورة أعلاه. لقد تم تمريرها إلى الفهرس كمعلمة. عندما ينفذ التنبيه مرة أخرى ، يكون لديه إشارة إلى الفهرس. في هذا الوقت ، لن يتم تغيير هذه القيمة بواسطة الحلقة. بالطبع ، بعد فهم المبدأ ، يمكنك أيضًا الكتابة مثل هذا:
نسخة الكود كما يلي:
لـ (var i = 0 ، l = links.length ؛ i <l ؛ i ++) {
(وظيفة (فهرس) {
الروابط [i] .onclick = function (e) {
E.PreventDefault () ؛
التنبيه ("أنت تنقر على الرابط #" + الفهرس) ؛
}
})(أنا)؛
}
إنه يعمل أيضًا.