يعد النطاق أحد أهم المفاهيم في JavaScript إذا كنت تريد تعلم JavaScript جيدًا، فأنت بحاجة إلى فهم كيفية عمل نطاق JavaScript وسلاسل النطاق. توفر مقالة اليوم مقدمة مختصرة عن نطاق JavaScript وسلسلة النطاق، على أمل مساعدة الجميع على تعلم JavaScript بشكل أفضل.
نطاق جافا سكريبت
أي لغة برمجة لديها مفهوم النطاق. ببساطة، النطاق هو النطاق الذي يمكن الوصول إليه من المتغيرات والوظائف، أي أن النطاق يتحكم في رؤية ودورة حياة المتغيرات والوظائف. في JavaScript، هناك نوعان من النطاق المتغير: النطاق العام والنطاق المحلي.
1. النطاق العالمي
الكائنات التي يمكن الوصول إليها في أي مكان في الكود لها نطاق عالمي، بشكل عام، المواقف التالية لها نطاق عالمي:
(1) الوظيفة الخارجية والمتغيرات المحددة خارج الوظيفة الخارجية لها نطاق عالمي، على سبيل المثال:
انسخ رمز الكود كما يلي:
varauthorName="Mountainside Stream";
وظيفة دو شيء () {
varblogName = "";
دالة داخلية (){
تنبيه (اسم المدونة)؛
}
insideSay();
}
تنبيه (authorName)؛ // جدول الجبل
تنبيه (اسم المدونة)؛ // خطأ في البرنامج النصي
افعل شيئًا () ؛//
InnerSay () // خطأ في البرنامج النصي
(2) يتم تلقائيًا الإعلان عن أن جميع المتغيرات التي لم يتم تعريفها وتعيينها بشكل مباشر لها نطاق عالمي، على سبيل المثال:
انسخ رمز الكود كما يلي:
وظيفة دو شيء () {
varauthorName="Mountainside Stream";
اسم المدونة = "";
تنبيه (اسم المؤلف)؛
}
تنبيه (اسم المدونة)؛//
تنبيه (اسم المؤلف)؛ // خطأ في البرنامج النصي
المتغير blogName له نطاق عالمي، ولكن لا يمكن الوصول إلى AuthorName خارج الوظيفة.
(3) جميع خصائص كائنات النافذة لها نطاق عالمي
بشكل عام، الخصائص المضمنة لكائن النافذة لها نطاق عالمي، مثل window.name، window.location، window.top، وما إلى ذلك.
2. النطاق المحلي
على عكس النطاق العام، لا يمكن الوصول إلى النطاق المحلي بشكل عام إلا من خلال جزء ثابت من التعليمات البرمجية، والأكثر شيوعًا داخل الوظيفة، لذلك في بعض الأماكن سترى أيضًا أشخاصًا يشيرون إلى هذا النطاق باعتباره نطاق وظيفة، مثل التعليمة البرمجية التالية كلاهما blogName والوظيفة الداخلية لهما نطاق محلي فقط.
انسخ رمز الكود كما يلي:
وظيفة دو شيء () {
varblogName = "";
دالة داخلية (){
تنبيه (اسم المدونة)؛
}
insideSay();
}
تنبيه (اسم المدونة)؛ // خطأ في البرنامج النصي
InnerSay();// خطأ في البرنامج النصي
سلسلة النطاق
في JavaScript، تعد الوظائف أيضًا كائنات. في الواقع، كل شيء في JavaScript هو كائن. تمتلك الكائنات الوظيفية، مثل الكائنات الأخرى، خصائص يمكن الوصول إليها من خلال التعليمات البرمجية ومجموعة من الخصائص الداخلية التي لا يمكن الوصول إليها إلا من خلال محرك JavaScript. إحدى الخصائص الداخلية هي [[النطاق]]، التي تم تعريفها بواسطة الإصدار الثالث القياسي من ECMA-262. تحتوي هذه الخاصية الداخلية على مجموعة الكائنات الموجودة في النطاق الذي تم إنشاء الوظيفة فيه. وتسمى هذه المجموعة بسلسلة نطاق الوظيفة ، والذي يحدد البيانات التي يمكن الوصول إليها بواسطة الوظيفة.
عند إنشاء دالة، يتم ملء سلسلة النطاق الخاصة بها بكائنات بيانات يمكن الوصول إليها من النطاق الذي تم إنشاء الوظيفة فيه. على سبيل المثال، حدد الوظيفة التالية:
انسخ رمز الكود كما يلي:
وظيفة إضافة (رقم 1، رقم 2) {
varsum=num1+num2;
العائد؛
}
عند إنشاء الدالة add، سيتم ملء سلسلة النطاق الخاصة بها بكائن عام، والذي يحتوي على جميع المتغيرات العامة، كما هو موضح في الشكل التالي (ملاحظة: الصورة توضح جزءًا فقط من جميع المتغيرات):
سيتم استخدام نطاق إضافة الوظيفة أثناء التنفيذ. على سبيل المثال، قم بتنفيذ التعليمة البرمجية التالية:
انسخ رمز الكود كما يلي:
فار الإجمالي = إضافة(5,10);
عند تنفيذ هذه الوظيفة، يتم إنشاء كائن داخلي يسمى "سياق التنفيذ". يحدد سياق وقت التشغيل البيئة التي يتم فيها تنفيذ الوظيفة. يحتوي كل سياق وقت تشغيل على سلسلة نطاق خاصة به لتحليل المعرف. عند إنشاء سياق وقت تشغيل، تتم تهيئة سلسلة النطاق الخاصة به إلى الكائن الموجود في [[النطاق]] للوظيفة قيد التشغيل حاليًا.
يتم نسخ القيم إلى سلسلة نطاق سياق وقت التشغيل بالترتيب الذي تظهر به في الوظيفة. يشكلون معًا كائنًا جديدًا يسمى "كائن التنشيط". يحتوي هذا الكائن على جميع المتغيرات المحلية والمعلمات المسماة ومجموعات المعلمات وهذه الوظيفة. ثم سيتم دفع هذا الكائن إلى الواجهة الأمامية لسلسلة النطاق يتم تدمير الكائن النشط أيضًا. تظهر سلسلة النطاق الجديدة أدناه:
أثناء تنفيذ الوظيفة، إذا لم تتم مواجهة متغير، فسوف يمر بعملية تحليل المعرف لتحديد مكان الحصول على البيانات وتخزينها. تبدأ هذه العملية من رأس سلسلة النطاق، أي من الكائن النشط، وتبحث عن معرف بنفس الاسم، إذا تم العثور عليه، استخدم المتغير المقابل لهذا المعرف ابحث عن الكائن التالي في سلسلة النطاق. إذا لم يتم العثور على أي كائنات بعد البحث، فسيتم اعتبار المعرف غير محدد. أثناء تنفيذ الوظيفة، يخضع كل معرف لعملية البحث هذه.
تسلسل النطاق وتحسين التعليمات البرمجية
يمكن أن نرى من هيكل سلسلة النطاق أنه كلما كان المعرف موجودًا بشكل أعمق في سلسلة النطاق لسياق وقت التشغيل، كلما كانت سرعة القراءة والكتابة أبطأ. كما هو موضح في الشكل أعلاه، نظرًا لأن المتغيرات العامة موجودة دائمًا في نهاية سلسلة نطاق سياق وقت التشغيل، فإن العثور على المتغيرات العامة هو الأبطأ أثناء تحليل المعرف. لذلك، عند كتابة التعليمات البرمجية، يجب عليك استخدام المتغيرات العامة بأقل قدر ممكن واستخدام المتغيرات المحلية قدر الإمكان. القاعدة الأساسية الجيدة هي: إذا تمت الإشارة إلى كائن عبر النطاق أكثر من مرة، فقم بتخزينه في متغير محلي قبل استخدامه. على سبيل المثال الكود التالي:
انسخ رمز الكود كما يلي:
دالة تغيير اللون () {
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red";
};
}
تشير هذه الدالة إلى مستند المتغير العام مرتين للعثور على المتغير، يجب عليك اجتياز سلسلة النطاق بأكملها حتى يتم العثور عليه أخيرًا في الكائن العام. يمكن إعادة كتابة هذا الكود على النحو التالي:
انسخ رمز الكود كما يلي:
دالة تغيير اللون (){
vardoc=document;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}
هذا الكود بسيط نسبيًا ولن يظهر تحسنًا كبيرًا في الأداء بعد إعادة كتابته، ومع ذلك، إذا كان هناك عدد كبير من المتغيرات العامة في البرنامج التي يتم الوصول إليها بشكل متكرر، فسيتم تحسين أداء الكود المعاد كتابته بشكل ملحوظ.
تغيير سلسلة النطاق
يكون سياق وقت التشغيل المقابل فريدًا في كل مرة يتم فيها تنفيذ وظيفة، لذا فإن استدعاء نفس الوظيفة عدة مرات سيؤدي إلى إنشاء سياقات وقت تشغيل متعددة. عندما تكتمل الوظيفة، سيتم تدمير سياق التنفيذ. يرتبط كل سياق وقت تشغيل بسلسلة نطاق. في ظل الظروف العادية، أثناء تنفيذ سياق وقت التشغيل، لن تتأثر سلسلة نطاقها إلا بعبارة with وcatch.
عبارة with هي طريقة مختصرة لاستخدام الكائنات لتجنب كتابة التعليمات البرمجية المتكررة. على سبيل المثال:
انسخ رمز الكود كما يلي:
وظيفةinitUI () {
مع (وثيقة){
فاربد = الجسم،
الروابط=getElementsByTagName("a"),
ط = 0،
len=links. length;
بينما (أنا <لين) {
تحديث(روابط[i++]);
}
getElementById("btnInit").onclick=function(){
doSomething();
};
}
}
يتم استخدام بيان العرض هنا لتجنب كتابة المستند عدة مرات، وهو ما يبدو أكثر كفاءة، ولكنه يسبب في الواقع مشاكل في الأداء.
عندما يصل الكود إلى العبارة with، يتم تغيير سلسلة النطاق الخاصة بسياق وقت التشغيل مؤقتًا. يتم إنشاء كائن جديد قابل للتغيير يحتوي على كافة خصائص الكائن المحدد بواسطة المعلمة. سيتم دفع هذا الكائن إلى رأس سلسلة النطاق، مما يعني أن جميع المتغيرات المحلية للوظيفة موجودة الآن في كائن سلسلة النطاق الثاني، وبالتالي فإن الوصول إليها أكثر تكلفة. كما هو موضح أدناه:
لذلك، يجب عليك تجنب استخدام العبارة with في برنامجك. في هذا المثال، يمكن أن يؤدي تخزين المستند في متغير محلي إلى تحسين الأداء.
الشيء الآخر الذي يغير سلسلة النطاق هو بيان الالتقاط في بيان محاولة الالتقاط. عند حدوث خطأ في كتلة التعليمات البرمجية للتجربة، تنتقل عملية التنفيذ إلى عبارة الالتقاط، ثم يتم دفع كائن الاستثناء إلى كائن قابل للتغيير ووضعه على رأس النطاق. داخل كتلة الالتقاط، سيتم وضع كافة المتغيرات المحلية للوظيفة في كائن سلسلة النطاق الثاني. رمز العينة:
انسخ رمز الكود كما يلي:
يحاول{
doSomething();
}قبض(على سبيل المثال){
تنبيه (ex.message)؛ // تتغير سلسلة النطاق هنا
}
يرجى ملاحظة أنه بمجرد تنفيذ بيان الالتقاط، ستعود سلسلة النطاق إلى حالتها السابقة. تعتبر عبارة Try-catch مفيدة جدًا في تصحيح أخطاء التعليمات البرمجية ومعالجة الاستثناءات، لذا لا يوصى بتجنبها تمامًا. يمكنك تقليل تأثير أداء عبارات الالتقاط عن طريق تحسين التعليمات البرمجية الخاصة بك. النمط الجيد هو تفويض معالجة الأخطاء إلى وظيفة، على سبيل المثال:
انسخ رمز الكود كما يلي:
يحاول{
doSomething();
}قبض(على سبيل المثال){
HandleError(ex);//تفويض إلى طريقة المعالج
}
في التعليمة البرمجية المُحسّنة، يكون أسلوب HandleError هو التعليمة البرمجية الوحيدة التي يتم تنفيذها في عبارة الالتقاط. تستقبل هذه الوظيفة كائن استثناء كمعلمة، بحيث يمكنك التعامل مع الأخطاء بشكل أكثر مرونة واتساقًا. نظرًا لأنه يتم تنفيذ عبارة واحدة فقط وعدم الوصول إلى أي متغيرات محلية، فإن التغييرات المؤقتة في سلسلة النطاق لن تؤثر على أداء التعليمات البرمجية.