متغيرات جافاسكريبت: دليل المبتدئين الشامل لـ `var` و `const` و `let`
تُعد المتغيرات (Variables) من المفاهيم الأساسية والجوهرية في أي لغة برمجة. في عالم جافاسكريبت، يمكنك تعريف المتغيرات باستخدام ثلاث كلمات مفتاحية رئيسية: var، const، أو let. يهدف هذا المقال إلى أن يكون دليلك الشامل لفهم سبب استخدامنا للمتغيرات، وكيفية توظيفها بفعالية، بالإضافة إلى استكشاف الفروقات الجوهرية بين const، let، و var لتمكينك من كتابة كود أكثر كفاءة وخالٍ من الأخطاء.
لماذا نستخدم المتغيرات في جافاسكريبت؟
في سياق البرمجة، تُعد “البيانات” هي المعلومات التي نستخدمها في برامج الكمبيوتر الخاصة بنا. على سبيل المثال، اسم المستخدم الخاص بك على تويتر هو قطعة من البيانات. يرتكز جزء كبير من البرمجة على معالجة هذه البيانات أو عرضها. ولتحقيق ذلك، يحتاج المبرمجون إلى طريقة لتخزين البيانات وتتبعها بكفاءة. دعنا نوضح هذا بمثال عملي.
لنبدأ بفتح وحدة تحكم جافاسكريبت (JavaScript console) في متصفحك. على متصفح كروم (Chrome)، يمكنك تشغيلها باستخدام الاختصار Ctrl + Shift + J لنظامي التشغيل ويندوز ولينكس، أو Cmd + Option + J لمستخدمي ماك. بمجرد فتح الوحدة، تخيل عمر حيوانك الأليف (أو أي رقم مشابه إذا لم يكن لديك حيوانات أليفة) وأدخله مباشرة في الوحدة:
4
ماذا لو أردنا الإشارة إلى هذا الرقم مرة أخرى؟ سيتعين علينا كتابته مرة ثانية. نحن بحاجة إلى طريقة للإشارة إلى قطعة البيانات هذه حتى نتمكن من إعادة استخدامها في جميع أنحاء برنامجنا دون تكرار. هنا يأتي دور المتغيرات في جافاسكريبت.
مفهوم المتغيرات: تسميات للقيم
يمكن تشبيه المتغيرات بتسميات (Labels) للقيم التي نخزنها. تخيل وعاءً من التوت الأزرق (blueberries) عليه ملصق مكتوب عليه “توت أزرق”. في هذا المثال، المتغير blueberries يشير إلى قيمة، وهي التوت الأزرق نفسه. لنقم بتعريف متغير باسم age (العمر) ونستخدم عامل التعيين (assignment operator)، وهو علامة المساواة (=)، لإسناد القيمة 4 لهذا المتغير. سنستخدم الكلمة المفتاحية var:
var age = 4;
بهذه الطريقة، يمنح المبرمجون اسمًا لقيمة ما بحيث يمكنهم إعادة استخدامها، تحديثها، أو ببساطة تتبعها. يمكن استخدام المتغيرات لتخزين أي نوع من أنواع البيانات في جافاسكريبت. الآن بعد أن قمنا بإسناد هذه القيمة إلى المتغير age، يمكننا الرجوع إلى هذه القيمة لاحقًا. إذا قمت الآن بكتابة المتغير age في وحدتك، ستُعاد لك القيمة 4.
كيفية استخدام الكلمة المفتاحية var في جافاسكريبت
الكلمات المفتاحية في جافاسكريبت هي كلمات محجوزة لها معانٍ خاصة. عند استخدام الكلمة المفتاحية var، فإنك تخبر جافاسكريبت أنك بصدد تعريف متغير جديد. إحدى الخصائص الرئيسية للمتغيرات المُعلنة باستخدام var هي إمكانية إعادة تعيين قيمتها (reassignment). لنوضح ذلك من خلال تعريف متغير جديد باسم name وإسناد القيمة 'Madison' إليه:
var name = 'Madison';
بعد ذلك، سنقوم بإعادة تعيين هذا المتغير ليشير إلى قيمة اسم مختلف، وهو 'Ben':
name = 'Ben';
الآن، إذا قمت بتشغيل console.log(name)، ستحصل على المخرجات Ben. هذا يوضح مرونة var في السماح بتغيير القيمة التي يشير إليها المتغير.
تعريف المتغيرات بدون قيمة ابتدائية
عند استخدام الكلمة المفتاحية var، يمكن أيضًا تعريف المتغيرات بدون قيمة ابتدائية. على سبيل المثال:
var year;
هنا، قمنا بتعريف متغير باسم year، لكنه لا يشير إلى أي قيمة بعد. لاحقًا، إذا أردنا أن يشير إلى قيمة ما، يمكننا استخدام عامل التعيين للقيام بذلك:
year = 2020;
الآن، سيشير المتغير year إلى القيمة 2020.
تطور المتغيرات في جافاسكريبت
عندما تم إنشاء جافاسكريبت لأول مرة، كانت الكلمة المفتاحية var هي الطريقة الوحيدة لتعريف المتغيرات. ولكن مع التحديثات الأخيرة لجافاسكريبت (خاصة في معيار ECMAScript2015 المعروف أيضًا باسم ES6)، تم تقديم الكلمتين المفتاحيتين const و let كطرق بديلة وأكثر تحكمًا لتعريف المتغيرات. لفهم سبب الحاجة إليهما، سنتناول المشكلات التي كانت تواجه الكلمة المفتاحية var، وللقيام بذلك، يجب أن نفهم أولاً مفهوم “النطاق” (Scope).
ما هو النطاق (Scope) في جافاسكريبت؟
يشير “النطاق” إلى الأماكن في الكود الخاص بنا حيث تكون المتغيرات متاحة للاستخدام. عندما يكون المتغير ذو “نطاق عام” (globally scoped)، فهذا يعني أنه متاح في أي جزء من برنامجك. دعنا نلقي نظرة على مثال:
var name = 'Bob';
function printName () {
console.log(name);
}
printName();
في هذا المثال، قمنا بإنشاء واستدعاء دالة printName التي ستطبع قيمة المتغير name، وهي Bob. ستلاحظ ظهور هذه القيمة مطبوعة في وحدتك. نظرًا لأن متغيرنا name تم إنشاؤه خارج الدالة، فهو ذو نطاق عام. هذا يعني أنه متاح في أي مكان في الكود الخاص بك، بما في ذلك داخل أي دالة. لهذا السبب، تتمكن دالتنا printName من الوصول إلى المتغير name.
النطاق الوظيفي (Function Scope)
الآن، دعنا ننشئ متغيرًا ذو “نطاق وظيفي” (function-scoped). هذا يعني أن المتغير لا يمكن الوصول إليه إلا داخل الدالة التي تم إنشاؤه فيها. سيكون المثال التالي مشابهًا جدًا للكود أعلاه، ولكن مع اختلاف في موضع تعريف المتغير:
function printYear () {
var year = 2020;
}
console.log(year);
الآن في وحدتنا، سنحصل على خطأ: year is not defined. هذا لأن المتغير year ذو نطاق وظيفي. أي أنه موجود فقط داخل الدالة التي تم إنشاؤه فيها. لا يمكننا الوصول إليه خارج الدالة، وهو المكان الذي نحاول الوصول إليه فيه عندما نشغل console.log.
تُعد المتغيرات ذات النطاق الوظيفي مفيدة للمبرمجين لأننا غالبًا ما نرغب في إنشاء متغيرات تكون مفيدة أو مطلوبة فقط داخل دالة معينة. كما أن إنشاء متغيرات عامة بشكل مفرط يمكن أن يؤدي إلى أخطاء أو مشكلات غير متوقعة في الكود. الآن بعد أن أصبح لدينا فهم أساسي للنطاق، يمكننا العودة إلى مناقشة المشكلات المتعلقة بالكلمة المفتاحية var.
مشكلات الكلمة المفتاحية var في جافاسكريبت
دعنا نلقي نظرة على مثال آخر يوضح إحدى المشكلات الجوهرية مع var. سنقوم بإنشاء متغير age. بعد ذلك، سنكتب جملة شرطية if تتحقق مما إذا كان للمتغير age قيمة، وإذا كان كذلك، فستقوم بطباعة ضعف هذا العمر في وحدة التحكم. هذا مثال مبسط، لكننا سنتحقق أولاً مما إذا كان للمتغير age قيمة للتأكد من أننا نضيف إلى قيمة صالحة:
var age = 27;
if (age) {
var doubleAge = age + age;
console.log(`Double your current age is ${doubleAge}`);
}
الآن في وحدتك، سترى المخرجات: Double your current age is 54. المشكلة هنا هي أن المتغير doubleAge أصبح الآن متغيرًا عامًا (global variable). إذا قمت بإدخال doubleAge في وحدتك، فسترى أنك تستطيع الوصول إليه:
doubleAge
54
كما ناقشنا سابقًا، المتغيرات التي يتم إنشاؤها باستخدام الكلمة المفتاحية var تكون ذات نطاق وظيفي (function-scoped). المتغيرات ذات النطاق الوظيفي لا توجد إلا داخل الدالة التي تم إنشاؤها فيها. ولكن بما أن المتغير doubleAge ليس داخل دالة، فهذا يعني أنه أصبح ذو نطاق عام. أي أن المتغير doubleAge متاح الآن في أي مكان في الكود الخاص بنا. المشكلة هي أن doubleAge هو مجرد متغير استخدمناه مرة واحدة داخل جملة if، ولا نحتاج بالضرورة أن يكون متاحًا في كل مكان في الكود. لقد “تسرب” (leaked) خارج جملة if التي تم إنشاؤه فيها، على الرغم من أننا لم نكن بحاجة إلى ذلك. هذا السلوك قد يؤدي إلى تضارب في الأسماء أو أخطاء غير مقصودة في التطبيقات الكبيرة.
var age = 27;
if (age) {
// نحن نحتاج المتغير doubleAge فقط في هذا الجزء من الكود بين الأقواس المعقوفة.
var doubleAge = age + age;
console.log(`Double your current age is ${doubleAge}`);
}
doubleAge; // المتغير doubleAge متاح خارج هذه الأقواس المعقوفة، في النطاق العام.
54
سيكون من الرائع لو كان لدينا طريقة لإنشاء متغير موجود فقط داخل جملة if التي تم إنشاؤه فيها، أي داخل الكتلة البرمجية (block of code) الموجودة بين الأقواس المعقوفة {}:
var age = 27;
if (age) {
// نريد أن يكون متغيرنا موجودًا هنا فقط، حيث سنستخدمه
var doubleAge = age + age;
console.log(`Double your current age is ${doubleAge}`);
}
للمساعدة في إصلاح هذه المشكلة، تم تقديم الكلمتين المفتاحيتين const و let في جافاسكريبت.
كيفية استخدام الكلمة المفتاحية const في جافاسكريبت
تعمل const بطريقة مشابهة لـ var، ولكن مع بعض الاختلافات الجوهرية. أولاً، const ذات “نطاق كتلي” (block-scoped)، بينما var ذات نطاق وظيفي. ما هي “الكتلة” (Block)؟ تشير الكتلة إلى أي مساحة بين قوسين معقوفين مفتوحين ومغلقين {}. قد يبدو هذا مربكًا في البداية، لذا دعنا نطبق مثالنا السابق، ولكن هذه المرة باستخدام const بدلاً من var عند تعريف متغير doubleAge:
var age = 27;
if (age) {
const doubleAge = age + age;
console.log(`Double your current age is ${doubleAge}`);
}
الآن، اكتب doubleAge في وحدتك واضغط Enter. يجب أن تحصل على خطأ: doubleAge is not defined. هذا لأن const ذات نطاق كتلي: فهي موجودة فقط في الكتلة التي تم تعريفها فيها. المتغير doubleAge “محاصر” داخل القوسين المعقوفين {} اللذين تم تعريفه فيهما. يمكن للكود الموجود داخل هذه الأقواس الوصول إلى doubleAge، ولكن لا يمكن لأي كود خارجها الوصول إليه. باستخدام const بدلاً من var، تم إصلاح مشكلتنا السابقة. لم يعد متغيرنا doubleAge “يتسرب” إلى النطاق العام دون داعٍ. بدلاً من ذلك، فإنه موجود فقط داخل الكتلة التي تم إنشاؤه فيها.
const والنطاق الوظيفي
كيف تعمل المتغيرات ذات النطاق الكتلي في سياق الدوال؟ لفهم ذلك، دعنا ننشئ ثم نستدعي دالة returnX:
function returnX () {
const x = 1;
return x;
}
returnX();
باستدعاء هذه الدالة returnX، يمكننا أن نرى أن دالتنا تُعيد قيمة x، وهي 1. إذا قمنا بعد ذلك بكتابة x في وحدة التحكم، فسنحصل على خطأ ReferenceError: x is not defined. هذا لأن الدوال تُعتبر أيضًا كتلًا برمجية، وبالتالي فإن const x ستكون موجودة فقط داخل الدالة.
عدم إمكانية إعادة تعريف const
الشيء التالي الذي يجب معرفته عن const هو أنه لا يمكن تعريفها إلا مرة واحدة فقط. اكتب هذا الكود في وحدتك:
const y = 1;
const y = 2;
يجب أن ترى خطأ: Identifier 'y' has already been declared. هذا هو أحد الفروقات بين var و const. بينما ستعطيك const خطأً، لتخبرك أنك قد قمت بالفعل بتعريف هذا المتغير، فإن الكلمة المفتاحية var لن تفعل ذلك:
var x = 1;
var x = 2;
سيشير المتغير x إلى القيمة 2 دون أي خطأ. يمكن أن يتسبب هذا في أخطاء (bugs) لك كمبرمج، حيث ربما لم تكن تقصد إعادة تعيين قيمتك إلى متغير جديد. وبالتالي، فإن استخدام const يمكن أن يساعدك حيث ستتلقى خطأ إذا حاولت عن طريق الخطأ إعادة تعريف أو إعادة تعيين متغير. هذه هي إحدى نقاط قوة الكلمة المفتاحية const التي تم تقديمها كطريقة محدثة وأفضل لإنشاء المتغيرات في جافاسكريبت.
عدم إمكانية إعادة تعيين قيمة const
ومع ذلك، ماذا عن الأوقات التي ترغب فيها بتحديث قيمة المتغير الخاص بك؟ دعنا نلقي نظرة على مثال يوضح سبب حاجتنا إلى ذلك. لنقم بتعريف متغير adult ونضبط قيمته على false. سنقوم أيضًا بإنشاء متغير age ونضبط قيمته على 20:
const adult = false;
const age = 20;
لنفترض أننا نريد التحقق من عمر المستخدم، وتعيين متغير adult إلى true إذا كان العمر أكبر من 18. يمكننا كتابة جملة if للقيام بذلك:
if (age > 18) {
adult = true;
}
ماذا يحدث عندما نشغل هذا الكود؟ هنا سنرى خطأ: Error: Assignment to constant variable. هذا لأنه، وفقًا لقواعد const، لا يمكننا إعادة تعيين قيمة هذا المتغير. أي أن متغيرنا adult يشير بالفعل إلى القيمة false، ولا يمكننا الآن جعله يشير إلى شيء آخر (true). إذا قمنا بطباعة adult مرة أخرى، يمكننا أن نرى أنه ظل كما هو ولا يزال يحمل القيمة false. لا يمكننا إعادة تعيين متغيرنا adult، وتعمل const كما هو متوقع. ومع ذلك، ماذا لو أردنا إعادة تعيين هذا المتغير؟ غالبًا ما يرغب المبرمجون في أن يكونوا قادرين على تحديث قيم متغيراتهم. هنا يأتي دور كلمتنا المفتاحية الثالثة، let.
كيفية استخدام الكلمة المفتاحية let في جافاسكريبت
دعنا أولاً نستعرض أوجه التشابه بين let و const. فـ let، مثل const، ذات نطاق كتلي (block-scoped). إذا استبدلت const بـ let في مثالنا السابق لـ doubleAge، فستعمل بنفس الطريقة، حيث لن تكون doubleAge متاحة خارج كتلة if.
ومع ذلك، تختلف let عن const بطريقة جوهرية: المتغيرات المُعلنة باستخدام الكلمة المفتاحية let يمكن إعادة تعيين قيمتها (reassigned)، بينما المتغيرات التي تم إنشاؤها باستخدام الكلمة المفتاحية const لا يمكن إعادة تعيين قيمتها. لنستعرض مثالًا. باستخدام نفس مثالنا أعلاه، استبدل const بـ let. سنبقي متغيرنا age كـ const بقيمة 20:
let adult = false;
const age = 20;
if (age > 18) {
adult = true;
}
الآن إذا كتبنا adult في وحدة التحكم، فبدلاً من الحصول على خطأ كما فعلنا سابقًا، سنرى المخرجات true. باستخدام الكلمة المفتاحية let، قمنا بتحديث متغيرنا ليشير إلى القيمة true كما أردنا. في بعض الأحيان في البرمجة، سنرغب في تحديث متغيرنا اعتمادًا على بيانات معينة نتلقاها. يمكننا استخدام let للقيام بذلك بفعالية وأمان.
خلاصة شاملة
في الختام، لقد تعلمنا أن المتغيرات تُستخدم لتتبع البيانات وإعادة استخدامها بكفاءة في برامج الكمبيوتر الخاصة بنا. يشير “النطاق” إلى الأماكن في الكود حيث تكون المتغيرات متاحة للاستخدام. يمكن تعريف المتغيرات باستخدام var، const، أو let.
var: ذات نطاق وظيفي (function-scoped)، ويمكن إعادة تعريفها وإعادة تعيين قيمتها.const: ذات نطاق كتلي (block-scoped)، ولا يمكن إعادة تعريفها أو إعادة تعيين قيمتها بعد الإسناد الأولي.let: ذات نطاق كتلي (block-scoped)، ولا يمكن إعادة تعريفها في نفس النطاق، ولكن يمكن إعادة تعيين قيمتها.
قد تبدو الفروقات بين var، const، و let مربكة في البداية. من المفيد قراءة شروحات متعددة حولها، بالإضافة إلى تجربة كتابة الأكواد الخاصة بك بطرق مختلفة لترسيخ فهمك. إن امتلاك أساس قوي لهذه المفاهيم لن يساعدك فقط في بداية مسيرتك المهنية في جافاسكريبت، بل سيبقى ذا قيمة طوال رحلتك في عالم البرمجة.
الخلاصة التقنية
يُظهر التطور من var إلى const و let في جافاسكريبت توجهًا واضحًا نحو كتابة أكواد أكثر أمانًا، قابلية للصيانة، وأقل عرضة للأخطاء. بينما لا تزال var موجودة لأسباب التوافق مع الإصدارات القديمة، يُنصح بشدة باستخدام const و let في المشاريع الحديثة. استخدام const للمتغيرات التي لا تتغير قيمتها يمنع التغييرات غير المقصودة، مما يعزز استقرار الكود. وفي المقابل، توفر let المرونة اللازمة للمتغيرات التي تحتاج إلى تحديث قيمتها، مع الحفاظ على نطاق كتلي يحد من “تسرب” المتغيرات ويقلل من فرص التضارب. إن فهم هذه الفروقات الدقيقة وتطبيقها بشكل صحيح هو حجر الزاوية في كتابة جافاسكريبت حديثة وفعالة.