فار ، السماح أو الثبات؟ قضايا النطاق المتغير و ES6

كانت النطاقات في JavaScript دائمًا موضوعًا صعبًا ، خاصة عند مقارنته باللغات المنظمة بشكل أكثر صرامة مثل C و Java. لسنوات عديدة ، لم يتم مناقشة النطاق في JS على نطاق واسع بشكل خاص ، لأن اللغة ببساطة لم يكن لديها أي وسيلة من شأنها أن تؤثر بشكل كبير على الوضع الحالي. ولكن في ECMAScript 6 ، هناك بعض الميزات الجديدة التي تسمح للمطورين بالتحكم بشكل أفضل في نطاق المتغيرات. هذه الميزات مدعومة بشكل جيد الآن من قبل المتصفحات ، وهي متاحة تمامًا لمعظم المطورين. ومع ذلك ، فإن الكلمات الرئيسية الجديدة للإعلان عن المتغيرات ، مع الأخذ في الاعتبار حقيقة أن الكلمة الرئيسية var القديمة لم تختف ، لا تعني فقط فرصًا جديدة ، ولكن أيضًا ظهور أسئلة جديدة. متى تستخدم الكلمات الأساسية const and const ؟ كيف يتصرفون؟ في أي حالات لا تزال الكلمة الرئيسية var ذات صلة؟ تهدف المادة ، التي ننشر ترجمتها اليوم ، إلى استكشاف مشكلة نطاق المتغيرات في جافا سكريبت.



نطاقات متغيرة: نظرة عامة


نطاق المتغير هو مفهوم مهم في البرمجة ، ومع ذلك ، يمكن أن يخلط بين بعض المطورين ، وخاصة المبتدئين. نطاق المتغير هو ذلك الجزء من البرنامج الذي يمكن الوصول إلى هذا المتغير فيه.

نلقي نظرة على المثال التالي:

 var myVar = 1; function setMyVar() { myVar = 2; } setMyVar(); console.log(myVar); 

ماذا console.log طريقة console.log ؟ الإجابة على هذا السؤال لن تفاجئ أي شخص: سينتج 2 . myVar الإعلان عن المتغير myVar خارج دالة ، والتي تخبرنا أنه تم الإعلان عنه في النطاق العالمي. لذلك ، سيتمكن أي وظيفة معلن عنها في نفس النطاق من الوصول إلى myVar . في الواقع ، عندما يتعلق الأمر بالكود الذي يتم تنفيذه في المستعرض ، فحتى الوظائف المعلنة في الملفات الأخرى المتصلة بالصفحة ستتمكن من الوصول إلى هذا المتغير.

ألق نظرة الآن على الكود التالي:

 function setMyVar() { var myVar = 2; } setMyVar(); console.log(myVar); 

ظاهريا ، تغييراته ، بالمقارنة مع المثال السابق ، غير ذات أهمية. وبالتحديد ، قمنا فقط بوضع تعريف المتغير داخل الوظيفة. ماذا سيكون إخراج console.log الآن؟ في الواقع ، لا شيء ، حيث لم يتم الإعلان عن هذا المتغير وعندما تحاول الوصول إليه ، سيتم عرض رسالة حول خطأ ReferenceError غير معالج. حدث ذلك لأنه تم التصريح عن المتغير داخل الدالة باستخدام الكلمة var . ونتيجة لذلك ، يقتصر نطاق هذا المتغير على النطاق الداخلي للدالة. يمكن الوصول إليها في جسم هذه الوظيفة ، يمكن أن تعمل الوظائف المضمنة في هذه الوظيفة معها ، ولكن لا يمكن الوصول إليها من الخارج. إذا كنا بحاجة إلى العديد من الوظائف في نفس المستوى لاستخدام متغير معين ، فنحن بحاجة إلى التصريح عن هذا المتغير في نفس المكان حيث يتم الإعلان عن هذه الوظائف ، أي مستوى أعلى من نطاقها الداخلي.

في ما يلي ملاحظة مثيرة للاهتمام: لا ينطبق رمز معظم مواقع الويب وتطبيقات الويب على عمل أي مبرمج واحد. تنتج معظم مشروعات البرامج عن تطوير الفريق ، بالإضافة إلى أنها تستخدم مكتبات وأطر عمل تابعة لجهات خارجية. حتى لو شارك مبرمج واحد فقط في تطوير موقع على شبكة الإنترنت ، فإنه عادة ما يستخدم موارد خارجية. ولهذا السبب ، لا يُنصح عادةً بالإعلان عن المتغيرات في النطاق العالمي ، حيث لا يمكنك معرفة المتغيرات التي سيتم الإعلان عنها مسبقًا بواسطة المطورين الآخرين الذين سيتم استخدام رمزهم في المشروع. للتغلب على هذه المشكلة ، يمكنك استخدام بعض الحيل ، على وجه الخصوص ، نمط " الوحدة النمطية " و IIFE عند تطبيق النهج الموجه للكائنات لتطوير JavaScript ، على الرغم من أن تغليف البيانات والوظائف في الكائنات العادية يمكن أن يحقق نفس التأثير. بشكل عام ، يمكن ملاحظة أن المتغيرات التي يتجاوز نطاقها ما تحتاج إليه عادة ما تكون مشكلة يجب القيام بشيء حيالها.

مشكلة مختلفة في الكلمات الرئيسية


لذا ، اكتشفنا مفهوم "النطاق". الآن دعنا ننتقل إلى أشياء أكثر تعقيدًا. ألق نظرة على الكود التالي:

 function varTest() { for (var i = 0; i < 3; i++) {   console.log(i); } console.log(i); } varTest(); 

ما الذي سيصل إلى وحدة التحكم بعد تنفيذها؟ من الواضح أن قيم العداد المتزايد i : 0 و 1 و 2 سيتم عرضها داخل الحلقة. بعد انتهاء الحلقة ، يستمر البرنامج في العمل. نحاول الآن الوصول إلى متغير العداد نفسه الذي تم الإعلان عنه في الحلقة for ، خارج هذه الحلقة. ماذا سيأتي من هذا؟

بعد استدعاء i خارج الحلقة ، يدخل 3 إلى وحدة التحكم ، حيث تعمل الكلمة الأساسية var على مستوى الوظيفة. إذا قمت بتعريف متغير باستخدام var ، فيمكنك الوصول إليه في دالة حتى بعد الخروج من البناء حيث تم الإعلان عنه.

يمكن أن يتحول هذا إلى مشكلة عندما تصبح الوظائف أكثر تعقيدًا. خذ بعين الاعتبار المثال التالي:

 function doSomething() { var myVar = 1; if (true) {   var myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

ما الذي سيصل إلى وحدة التحكم الآن؟ 2 و 2 . نعلن عن متغير ، ونهيئه بالرقم 1 ، ثم نحاول إعادة تعريف نفس المتغير داخل if . نظرًا لوجود هذين التعريفين في نفس النطاق ، لا يمكننا الإعلان عن متغير جديد يحمل نفس الاسم ، على الرغم من أننا نرغب بوضوح في القيام بذلك. ونتيجة لذلك ، يتم استبدال المتغير الأول داخل if .

هذا هو بالضبط أكبر خلل في الكلمة الرئيسية var . نطاق المتغيرات المعلنة باستخدامه كبير جدًا. يمكن أن يؤدي هذا إلى استبدال البيانات عن غير قصد وأخطاء أخرى. غالبًا ما تؤدي مناطق الرؤية الكبيرة إلى برامج غير دقيقة. بشكل عام ، يجب أن يكون للمتغير نطاق محدود حسب احتياجاته ، ولكن لا يتجاوزها. سيكون من الجيد أن تكون قادرًا على تعريف المتغيرات التي لا يكون نطاقها كبيرًا مثلما هو الحال عند استخدام var ، مما يجعل من الممكن ، إذا لزم الأمر ، استخدام بنيات برامج أكثر استقرارًا وأفضل مقاومة للأخطاء. في الواقع ، يمنحنا ECMAScript 6 مثل هذه الفرص.

طرق جديدة لتعريف المتغيرات


يمنحنا معيار ECMAScript 6 (مجموعة جديدة من ميزات JavaScript ، والمعروفة أيضًا باسم ES6 و ES2015) طريقتين جديدتين للإعلان عن المتغيرات التي تختلف في النطاق ، مقارنةً بالمتغير ، ولديها بعض الميزات الإضافية. هذه هي الكلمات الرئيسية const and const . كلاهما يعطينا ما يسمى بنطاق الكتلة. هذا يعني أن نطاق استخدامها يمكن أن يقتصر على كتلة من التعليمات البرمجية ، مثل حلقة for أو عبارة if . وهذا يمنح المطور المزيد من المرونة في اختيار نطاق المتغيرات. تأمل الكلمات الرئيسية الجديدة.

▍ استخدام الكلمة الرئيسية let


الكلمة الرئيسية let مشابهة جدًا لـ var ، والفرق الرئيسي هو النطاق المحدود للمتغيرات المعلنة معها. نعيد كتابة أحد الأمثلة أعلاه ، مع استبدال var بـ let :

 function doSomething() { let myVar = 1; if (true) {   let myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

في هذه الحالة ، سيصل الرقمان 2 و 1 إلى وحدة التحكم. يحدث هذا لأن if تعين نطاقًا جديدًا للمتغير المُعلن عنه باستخدام الكلمة الرئيسية let . وهذا يؤدي إلى حقيقة أن المتغير المعلن الثاني هو كيان مستقل تمامًا ، ولا يرتبط بالمتغير الأول. يمكنك العمل معهم بشكل مستقل عن بعضهم البعض. ومع ذلك ، هذا لا يعني أن الكتل المتداخلة من التعليمات البرمجية ، مثل if بنا ، مقطوعة تمامًا عن المتغيرات المعلنة باستخدام الكلمة الأساسية let في النطاق الذي توجد فيه بنفسها. ألق نظرة على الكود التالي:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar); } } doSomething(); 

في هذا المثال ، ستحصل وحدة التحكم على الرقم 1 . الكود الموجود في if لديه حق الوصول إلى المتغير الذي أنشأناه خارجه. لذلك ، يعرض قيمته في وحدة التحكم. وماذا يحدث إذا حاولت خلط النطاق؟ على سبيل المثال ، قم بما يلي:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar);   let myVar = 2;   console.log(myVar); } } doSomething(); 

قد يبدو أن المكالمة الأولى من وحدة myVar 1 ، ولكن في الواقع ، عند محاولة تنفيذ هذا الرمز ، سيظهر خطأ ReferenceError يخبرنا أن متغير myVar لهذا النطاق غير معرف أو لم تتم تهيئته (يختلف نص هذا الخطأ باختلاف المتصفحات). في JavaScript ، يوجد شيء مثل رفع المتغيرات إلى أعلى نطاقها. بمعنى ، إذا تم الإعلان عن متغير في نطاق معين ، فإن JavaScript تحتفظ بمكان لها حتى قبل أن يقوم الأمر بالإعلان عن تنفيذه. كيف يحدث هذا بالضبط يختلف عند استخدام var let .

خذ بعين الاعتبار المثال التالي:

 console.log(varTest); var varTest = 1; console.log(letTest); let letTest = 2; 

في كلتا الحالتين ، نحاول استخدام المتغير قبل التصريح عنه. لكن أوامر إخراج وحدة التحكم تتصرف بشكل مختلف. الأول ، باستخدام متغير سيتم الإعلان عنه لاحقًا باستخدام الكلمة الأساسية var ، سيتم إخراج undefined - أي ما سيتم كتابته إلى هذا المتغير. الأمر الثاني ، الذي يحاول الوصول إلى المتغير ، والذي سيتم الإعلان عنه لاحقًا باستخدام الكلمة الأساسية let ، سوف يرمي ReferenceError ويخبرنا أننا نحاول استخدام المتغير قبل الإعلان عنه أو تهيئته. ما الأمر؟

لكن الحقيقة هي أنه قبل تنفيذ الكود ، تنظر الآليات المسؤولة عن تنفيذه في هذا الكود ، وتكتشف ما إذا كان سيتم الإعلان عن أي متغيرات فيه ، وإذا كان الأمر كذلك ، قم برفعها مع حجز مساحة لها. في هذه الحالة ، تتم تهيئة المتغيرات المعلنة باستخدام الكلمة الأساسية var إلى undefined في نطاقها ، حتى إذا تم الوصول إليها قبل الإعلان عنها. المشكلة الرئيسية هنا هي أن القيمة undefined في متغير لا تشير دائمًا إلى أنهم يحاولون استخدام المتغير قبل إعلانه. نلقي نظرة على المثال التالي:

 var var1; console.log(var1); console.log(var2); var var2 = 1; 

في هذه الحالة ، على الرغم من أنه تم الإعلان عن var1 و var2 مختلف ، فإن كلتا المكالمتين إلى وحدة console.log . النقطة هنا هي أنه في المتغيرات المعلنة بـ var ، ولكن لم تتم تهيئتها ، undefined كتابة القيمة undefined تلقائيًا. في الوقت نفسه ، كما قلنا بالفعل ، تحتوي المتغيرات المعلنة باستخدام var ، والتي يتم الوصول إليها قبل الإعلان عنها ، أيضًا على undefined . ونتيجة لذلك ، إذا حدث خطأ ما في مثل هذا الرمز ، فلن يكون من الممكن فهم مصدر الخطأ بالضبط - باستخدام متغير غير مهيأ أو استخدام متغير قبل إعلانه.

مكان المتغيرات المعلنة بالكلمة الرئيسية let محجوزة في كتلتها ، ولكن قبل الإعلان عنها ، تقع في المنطقة الميتة المؤقتة (TDZ ، Temporal Dead Zone). هذا يؤدي إلى حقيقة أنه لا يمكن استخدامها قبل الإعلان عنها ، وتؤدي محاولة الوصول إلى هذا المتغير إلى حدوث خطأ. ومع ذلك ، فإن النظام يعرف بالضبط سبب المشكلة ويبلغ عنها. هذا واضح في هذا المثال:

 let var1; console.log(var1); console.log(var2); let var2 = 1; 

هنا ، سيتم إخراج المكالمة الأولى إلى console.log بشكل undefined ، والثانية ستلقي خطأ ReferenceError ، تخبرنا أن المتغير لم يتم الإعلان عنه أو تهيئته بعد.

نتيجة لذلك ، إذا كان استخدام var يبدو undefined ، فنحن لا نعرف سبب هذا السلوك للبرنامج. يمكن تعريف المتغير أو عدم تهيئته ، أو قد لا يتم الإعلان عنه بعد في هذا النطاق ، ولكن سيتم الإعلان عنه في الرمز الموجود أسفل الأمر للوصول إليه. باستخدام الكلمة let ، يمكننا فهم ما يحدث بالضبط ، وهو أكثر فائدة في تصحيح الأخطاء.

of استخدام الكلمة الأساسية const


تشبه الكلمة الرئيسية const إلى حد كبير ، ولكن لديهم فرق واحد مهم. يتم استخدام هذه الكلمة الرئيسية للإعلان عن الثوابت. لا يمكن تغيير قيم الثوابت بعد التهيئة. وتجدر الإشارة إلى أن هذا ينطبق فقط على قيم الأنواع البدائية أو الماء أو الأوتار أو الأعداد. إذا كان الثابت شيئًا أكثر تعقيدًا ، على سبيل المثال ، كائن أو صفيف ، يمكن تعديل البنية الداخلية لمثل هذا الكيان ، لا يمكنك فقط استبداله بآخر. ألق نظرة على الكود التالي:

 let mutableVar = 1; const immutableVar = 2; mutableVar = 3; immutableVar = 4; 

سيتم تنفيذ هذا الرمز حتى السطر الأخير. ستؤدي محاولة تعيين قيمة جديدة لثابت إلى حدوث خطأ TypeError . هذه هي الطريقة التي تتصرف بها الثوابت ، ولكن ، كما ذكرنا سابقًا ، يمكن تغيير الكائنات التي تهيئها الثوابت ، ويمكن أن تخضع لطفرات ، مما قد يؤدي إلى مفاجآت .

ربما تتساءل ، كمطور جافا سكريبت ، عن أهمية حصانة المتغيرات. الثوابت هي ظاهرة جديدة في JavaScript ، في حين أنها جزء أساسي من لغات مثل C أو Java. لماذا هذا المفهوم شائع جدا؟ الحقيقة هي أن استخدام الثوابت يجعلنا نفكر في كيفية عمل الكود الخاص بنا. في بعض الحالات ، يمكن أن يؤدي تغيير قيمة المتغير إلى تعطيل الرمز ، على سبيل المثال ، إذا كان الرقم Pi مكتوبًا فيه ويتم الوصول إليه باستمرار ، أو إذا كان المتغير يحتوي على رابط لعنصر HTML تحتاج إلى العمل معه باستمرار. لنفترض ، هنا ثابت يتم فيه كتابة ارتباط لزر معين:

 const myButton = document.querySelector('#my-button'); 

إذا كان الرمز يعتمد على الرابط إلى عنصر HTML ، فإننا نحتاج إلى التأكد من عدم ثبات هذا الرابط. نتيجة لذلك ، يمكننا القول أن الكلمة الأساسية لـ const لا تذهب فقط على طول مسار التحسينات في مجال الرؤية ، ولكن أيضًا على طول مسار تحديد إمكانية تعديل قيم الثوابت المعلنة باستخدام هذه الكلمة الرئيسية. تذكر كيف قلنا أن المتغير يجب أن يكون له النطاق الذي يحتاجه بالضبط. يمكن الاستمرار في هذه الفكرة من خلال تقديم توصية ، والتي بموجبها يجب أن يكون للمتغير القدرة على التغيير فقط ، وهو أمر ضروري للعمل معه بشكل صحيح ، وليس أكثر. إليك مادة جيدة حول موضوع الحصانة ، والتي يمكن استخلاص استنتاج مهم منها ، والتي بموجبها يجعلنا استخدام المتغيرات غير المنقولة يفكرون بعناية أكثر في الكود الخاص بنا ، مما يؤدي إلى تحسين نقاء الكود وتقليل عدد المفاجآت غير السارة الناشئة عن تشغيله.

عندما بدأت لأول مرة في استخدام الكلمات الأساسية let and const ، استخدمت بشكل أساسي let ، ولجأت إلى const فقط عند كتابة قيمة جديدة لمتغير تم الإعلان عنه بـ let يمكن أن يضر بالبرنامج. ولكن ، لمعرفة المزيد عن البرمجة ، غيرت رأيي في هذا الأمر. الآن const الرئيسية هي const ، let لي باستخدامها فقط عندما تحتاج إلى إعادة كتابة قيمة المتغير. هذا يجعلني أفكر فيما إذا كان من الضروري حقًا تغيير قيمة متغير معين. في معظم الحالات ، هذا ليس ضروريًا.

هل نحن بحاجة إلى الكلمة var؟


تساهم الكلمات الأساسية const and const في نهج برمجة أكثر مسؤولية. هل هناك حالات لا تزال هناك حاجة إلى الكلمة الرئيسية var ؟ نعم هناك. هناك العديد من المواقف التي لا تزال فيها هذه الكلمة الرئيسية مفيدة لنا. فكر مليًا في ما سنتحدث عنه قبل تغيير var let أو const .

▍ مستوى دعم الكلمة الأساسية المختلفة عن طريق المتصفحات


المتغيرات المعلنة مع الكلمة var بميزة واحدة مهمة للغاية وهي عدم وجود شيء. وبالتحديد ، نتحدث عن حقيقة أن جميع المتصفحات تدعم هذه الكلمة الرئيسية. على الرغم من أن دعم المتصفحات let and const جيد جدًا ، إلا أن هناك خطرًا من أن ينتهي برنامجك في متصفح لا يدعمهم. لفهم عواقب مثل هذا الحادث ، تحتاج إلى النظر في كيفية تعامل المتصفحات مع شفرة جافا سكريبت غير المدعومة ، على سبيل المثال ، كيف تتفاعل مع رمز CSS الذي لا يفهمونه.

إذا كان المتصفح لا يدعم بعض ميزات CSS ، فإن هذا يؤدي في الأساس إلى تشويه ما سيتم عرضه على الشاشة. لن يبدو أي موقع في مستعرض لا يدعم أي من الأنماط المستخدمة من قبل الموقع كما هو متوقع ، ولكن من المحتمل جدًا استخدامه. إذا كنت تستخدم ، على سبيل المثال ، let ، والمتصفح لا يدعم هذه الكلمة الرئيسية ، فلن يعمل كود JS الخاص بك ببساطة هناك. لن يكون - هذا كل شيء. بالنظر إلى أن JavaScript هو أحد المكونات الهامة للويب الحديث ، فقد يصبح هذا مشكلة خطيرة إذا كنت بحاجة إلى برامجك للعمل في متصفحات قديمة.

عندما يتحدث الأشخاص عن دعم المتصفح للمواقع ، يسألون عادةً عن المتصفح الذي يعمل فيه الموقع على النحو الأمثل. إذا كنا نتحدث عن موقع تعتمد وظيفته على استخدام خاصية let و const ، فسيتعين طرح سؤال مماثل بشكل مختلف: "في أي متصفحات لن يعمل موقعنا؟". وهذا أخطر بكثير من الحديث عن استخدام display: flex أم لا. بالنسبة لمعظم مواقع الويب ، لن يكون عدد المستخدمين الذين لديهم متصفحات قديمة كبيرًا بما يكفي للقلق. ومع ذلك ، إذا كنا نتحدث عن شيء مثل متجر على الإنترنت ، أو مواقع يشترى أصحابها إعلانات ، فقد يكون هذا اعتبارًا مهمًا للغاية. قبل استخدام الفرص الجديدة في مثل هذه المشاريع ، قم بتقييم مستوى المخاطر.

إذا كنت بحاجة إلى دعم المتصفحات القديمة حقًا ، ولكنك تريد استخدام let و const وميزات ES6 الجديدة الأخرى ، فإن أحد حلول هذه المشكلة هو استخدام ناقل JavaScript مثل Babel . يوفر المترجمون ترجمة رمز جديد إلى ما سيفهمه المتصفحات القديمة. باستخدام Babel ، يمكنك كتابة تعليمات برمجية حديثة تستخدم أحدث ميزات اللغة ، ثم تحويلها إلى تعليمات برمجية يمكن للمتصفحات القديمة تشغيلها.

, ? , . , , , , . , . , , . ES6-, Babel, Babel , , . , , . . ? - IE8 ? , , , , , .

▍ var


, var , . . :

 var myVar = 1; function myFunction() { var myVar = 2; //  ,     myVar    ! } 

, myVar , , . , . , , , , . , . var .

 var myVar = 1; function myFunction() { var myVar = 2; console.log(myVar); // 2 console.log(window.myVar); // 1 } 

var , window . let const . , JS- , (, , ) , .

, . , . , , , :

 let myGlobalVars = {}; let myVar = 1; myGlobalVars.myVar = myVar; function myFunction() { let myVar = 2; console.log(myVar); // 2 console.log(myGlobalVars.myVar); // 1 } 

, , , , . , , var , , , , , .

الملخص


, ? ? :

  • IE10 - ? — var .
  • JavaScript, , , var , const . - (, , ) — let .

let const , ECMAScript 6, ( ) - -. , , , . , - , «» «» , , let const , .

! , const var , let , , , ?

Source: https://habr.com/ru/post/ar420359/


All Articles