let مقابل var: حرب عوالم أم تطور JavaScript؟
\n\nهل سبق لك أن كتبت كودًا بلغة JavaScript وأمسكت بنفسك تفكر: «لماذا يتصرف هذا المتغير بهذه الطريقة الغريبة؟» أو الأسوأ من ذلك، واجهت خطأً يختفي عند استبدال var بـ let؟ مرحبًا بك في النادي. سؤال «ما الفرق بين "let" و "var"؟» على Stack Overflow يجمع مئات الآلاف من المشاهدات ليس من قبيل الصدفة - إنه أحد أكثر الفخاخ شيوعًا للمبتدئين وحتى المطورين ذوي الخبرة.
اليوم لن نقوم فقط بسرد الاختلافات. سننظر تحت الغطاء: سنفهم كيف يؤثر let و var على أداء كودك، والتحسين في المحركات الحديثة (V8، SpiderMonkey) ولماذا الاختيار بينهما ليس مجرد مسألة أسلوب، بل كفاءة أيضًا. استعدوا، سيكون الأمر مثيرًا.
باختصار عن الأساسيات: النطاق والرفع (hoisting)
\n\nقبل أن نتحدث عن الأداء، دعنا نتذكر الأساسيات. هناك ثلاثة اختلافات رئيسية بين var و let تغير كل شيء.
1. النطاق (Scope)
\n\n- \n
var- له نطاق دالة. إذا تم تعريف المتغير داخل دالة، فهو مرئي في جميع أنحاء الدالة. إذا كان خارجها، فهو عام. \nlet- له نطاق كتلة. يعيش فقط داخل الأقواس المتعرجة الأقرب{}(حلقة، شرط، كتلة). \n
// مثال مع var\nif (true) {\n var x = 10;\n}\nconsole.log(x); // 10 — المتغير "تسرب" خارج الكتلة\n\n// مثال مع let\nif (true) {\n let y = 20;\n}\nconsole.log(y); // ReferenceError: y is not defined\n\n\n2. الرفع (Hoisting) والمنطقة الميتة المؤقتة (Temporal Dead Zone - TDZ)
\n\nكلا المتغيرين «يُرفعان» (hoisting)، لكنهما يتصرفان بشكل مختلف.
\n\n- \n
var: يتم رفع التصريح إلى أعلى النطاق ويتم تهيئته بقيمةundefined. يمكنك الوصول إلى المتغير قبل تصريحه (لكنك ستحصل علىundefined). \nlet: يتم رفع التصريح، لكنه لا يُهيأ. حتى لحظة التصريح الفعلي، يكون المتغير في «منطقة ميتة مؤقتة» (Temporal Dead Zone). أي محاولة للوصول إليه تسببReferenceError. \n
// var — "undefined" آمن\nconsole.log(a); // undefined\nvar a = 5;\n\n// let — خطأ في TDZ\nconsole.log(b); // ReferenceError: Cannot access 'b' before initialization\nlet b = 10;\n\n\n3. إعادة التصريح
\n\n- \n
var- يمكن إعادة التصريح به أي عدد من المرات في نفس النطاق. لن يحدث خطأ. \nlet- إعادة التصريح في نفس النطاق ستسببSyntaxError. \n
var z = 1;\nvar z = 2; // جيد\n\nlet w = 1;\nlet w = 2; // SyntaxError: Identifier 'w' has already been declared\n\n\nوالآن عن الموضوع الرئيسي: الأداء والتحسين
\n\nيعتقد الكثيرون: «بما أن let هو المعيار الجديد، فهو أبطأ من var؟» أو العكس: «let أفضل، إذن هو أسرع». الحقيقة، كالعادة، في المنتصف وتعتمد على السياق.
كيف تحسن المحركات المتغيرات
\n\nمحركات JavaScript الحديثة (V8 في Chrome/Node.js، SpiderMonkey في Firefox) تترجم الكود إلى كود آلة باستخدام مترجمات JIT. تستخدم هذه المحركات استدلالات للتحسين.
\n\n- \n
var: بسبب نطاق الدالة وإمكانية إعادة التعريف العشوائي، يصعب على المحرك وضع افتراضات حول نوع المتغير. غالبًا ما ينشئ كائنات «ثقيلة»VariableObjectتخزن المتغيرات في الذاكرة الديناميكية. \nlet: نطاق الكتلة ومنع إعادة التصريح يعطيان المحرك أفضلية في التحسين. يمكن للمحرك تخصيص مساحة ثابتة للمتغير، مما يقلل الحمل على جمع القمامة (garbage collection) ويسرع الوصول. \n