المتغيرات

المتغيّرات هي بنية بيانات تحدّد اسمًا تمثيليًا لقيمة معيّنة. ويمكن أن تحتوي على بيانات من أي نوع.

يُعرف اسم المتغيّر باسم المعرّف. يجب أن يتبع المعرّف الصالح القواعد التالية:

  • يمكن أن تحتوي المعرّفات على أحرف Unicode وعلامات الدولار ($) وعلامات الشرطة السفلية (_) والأرقام (0-9) وحتى بعض أحرف Unicode.
  • لا يمكن أن تحتوي المعرّفات على مسافات بيضاء، لأنّ المُحلِّل يستخدم المسافات البيضاء لفصل عناصر الإدخال. على سبيل المثال، إذا حاولت استدعاء متغيّر my Variable بدلاً من myVariable، سيرصد المُحلِّل معرّفَين، my وVariable، ويُرسِل خطأ في البنية ("رمز غير متوقّع: معرّف").
  • يجب أن تبدأ المعرّفات بحرف أو شرطة سفلية (_) أو علامة دولار أمريكي ($). ولا يمكن أن تبدأ بأرقام، لتجنّب الخلط بين الأرقام والمعرّفات:

    let 1a = true;
    
    > Uncaught SyntaxError: Invalid or unexpected token
    

    إذا كانت JavaScript تسمح بظهور الأرقام في بداية المعرّف، سيؤدي ذلك إلى السماح باستخدام معرّفات تتكوّن من أرقام فقط، ما سيؤدي إلى حدوث تعارضات بين الأرقام المستخدَمة كأرقام والأرقام المستخدَمة كمعرّفات:

    let 10 = 20
    
    10 + 5
    > ?
    
  • لا يمكن استخدام "الكلمات المحجوزة" التي لها معنى نحوي كعناصر تعريف.

  • لا يمكن أن تحتوي المعرّفات على رموز خاصة (! . , / \ + - * =).

ليست القواعد التالية قواعد صارمة لإنشاء المعرّفات، بل هي أفضل الممارسات في المجال التي تسهّل الحفاظ على رمزك. إذا كان مشروعك المحدّد يتضمّن معايير مختلفة، اتّبِع هذه المعايير بدلاً من ذلك لتحقيق الاتساق.

استنادًا إلى المثال الذي وضعته الطرق والسمات المضمّنة في JavaScript، فإنّ طريقة الترميز بأسلوب الجمل (التي تُعرف أيضًا باسم camelCase) هي اصطلاح شائع جدًا لتحديد هوية العناصر التي تتألف من كلمات متعددة. الكتابة بالأحرف الكبيرة بحالة الجمل هي ممارسة كتابة الحرف الأول من كل كلمة بأحرف كبيرة باستثناء الكلمة الأولى لتحسين سهولة القراءة بدون مسافات.

let camelCasedIdentifier = true;

تستخدم بعض المشاريع اصطلاحات تسمية أخرى استنادًا إلى السياق وطبيعة البيانات. على سبيل المثال، عادةً ما يتم كتابة الحرف الأول من الفئة بأحرف كبيرة، لذا غالبًا ما تستخدم أسماء الفئات متعددة الكلمات صيغة camel التي تُعرف عادةً باسم "upper camel case" أو Pascal case.

class MyClass {

}

يجب أن تصف المعرّفات بأسلوب موجز طبيعة البيانات التي تحتوي عليها (على سبيل المثال، currentMonthDays هو اسم أفضل من theNumberOfDaysInTheCurrentMonth) وأن تكون واضحة للقراءة بنظرة سريعة (originalValue أفضل من val). تعمل معرّفات myVariable المستخدَمة في هذه الوحدة في سياق مثالين منفصلين، ولكنّها لن تكون مفيدة جدًا في الرمز البرمجي المخصّص للنشر لأنّها لا تقدّم أي معلومات عن البيانات التي تحتوي عليها.

يجب ألا تكون المعرّفات محدّدة جدًا بشأن البيانات التي تحتوي عليها، لأنّه يمكن أن تتغيّر قيمها استنادًا إلى كيفية تفاعل النصوص البرمجية مع هذه البيانات أو استنادًا إلى قرارات القائمين على صيانة المحتوى في المستقبل. على سبيل المثال، قد يكون من الضروري في وقت لاحق من المشروع تغيير متغيّر تم منحه في الأصل التعرّفmiles إلى قيمة بوحدة الكيلومترات، ما يتطلّب من القائمين بالصيانة تغيير أيّ إشارات إلى ذلك المتغيّر لتجنّب حدوث التباس في المستقبل. لمنع حدوث ذلك، استخدِم distance كمعرّف بدلاً من ذلك.

لا تمنح JavaScript أي امتياز أو معنى خاصَين للمعرّفات التي تبدأ بعلامات الشرطة السفلية (_)، ولكن يتم استخدامها عادةً للإشارة إلى أنّ المتغيّر أو الطريقة أو السمة "خاصة"، ما يعني أنّها مخصّصة فقط للاستخدام ضمن سياق العنصر الذي يحتوي عليها، ويجب عدم الوصول إليها أو تعديلها خارج هذا السياق. هذا اصطلاح تم نقله من لغات برمجة أخرى، ويعود إلى ما قبل إضافة المواقع الخاصة في JavaScript.

تعريف المتغيّر

هناك عدة طرق لإعلام JavaScript بمعرّف، وهي عملية تتمثّل في "تعريف" متغيّر. يتمّ الإعلان عن متغيّر باستخدام الكلمات الرئيسية let أو const أو var.

let myVariable;

استخدِم let أو var لتعريف متغيّر يمكن تغييره في أي وقت. تُعلم هذه الكلمات الرئيسية مفسِّر JavaScript بأنّ سلسلة الأحرف هي معرّف قد يحتوي على قيمة.

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

يتم إعداد متغيّر تمّت تعريفه من خلال منح قيمة للمتغيّر. استخدِم إشارة مساواة واحدة (=) لتعيين قيمة لمتغيّر أو إعادة تعيينها. ويمكنك تنفيذ ذلك كجزء من العبارة نفسها التي تحدّد ذلك:

let myVariable = 5;

myVariable + myVariable
> 10

يمكنك أيضًا تعريف متغيّر باستخدام let (أو var) بدون إعداده على الفور. وفي هذه الحالة، تكون القيمة الأولية للمتغيّر هي undefined إلى أن يحدّد الرمز قيمة له.

let myVariable;

myVariable;
> undefined

myVariable = 5;

myVariable + myVariable
> 10

يختلف المتغيّر الذي يحمل القيمة undefined عن المتغيّر غير المحدّد الذي لم يتمّ الإعلان عن معرّفه بعد. يؤدي الإشارة إلى متغيّر لم تذكره إلى حدوث خطأ.

myVariable
> Uncaught ReferenceError: myVariable is not defined

let myVariable;

myVariable
> undefined

يُطلق على ربط معرّف بقيمة بشكل عام اسم "ربط". تُعرف البنية التي تتبع الكلمات الرئيسية let أو var أو const باسم "قائمة الربط"، وتسمح بإعلانات متعدّدة للمتغيّرات مفصولة بفواصل (تنتهي بفاصلة منقوطة متوقّعة). وهذا يجعل مقتطفات الرموز البرمجية التالية متطابقة من الناحية الوظيفية:

let firstVariable,
     secondVariable,
     thirdVariable;
let firstVariable;
let secondVariable;
let thirdVariable;

لا تستخدِم إعادة تعيين قيمة متغيّر العنصر let (أو var)، لأنّ JavaScript تعرف على وجود المتغيّر:

let myVariable = true;

myVariable
> true

myVariable = false;

myVariable
> false

يمكنك إعادة تعيين قيم جديدة للمتغيّرات استنادًا إلى قيمها الحالية:

let myVariable = 10;

myVariable
> 10

myVariable = myVariable * myVariable;

myVariable
> 100

إذا حاولت إعادة تعريف متغيّر باستخدام let في بيئة الإنتاج، سيظهر لك خطأ في البنية:

let myVariable = true;
let myVariable = false;
> Uncaught SyntaxError: redeclaration of let myVariable

أدوات المطوّرين في المتصفّحات أكثر تساهلاً بشأن إعادة تعريف letclass)، لذا قد لا تظهر لك المشكلة نفسها في وحدة تحكّم المطوّرين.

للحفاظ على توافق المتصفّحات القديمة، يسمح var بإعادة التعريف غير الضرورية بدون خطأ في أي سياق:

var myVariable = true;
var myVariable = false;

myVariable\
> false

const

استخدِم الكلمة الرئيسية const لتعريف ثابت، وهو نوع من المتغيّرات التي يجب إعدادها على الفور، ولا يمكن تغييرها بعد ذلك. تلتزم معرّفات الثوابت بجميع القواعد نفسها التي تلتزم بها المتغيّرات التي تمّت صياغتها باستخدام letvar):

const myConstant = true;

myConstant
> true

لا يمكنك تحديد قيمة ثابتة بدون تحديد قيمة لها على الفور، لأنّه لا يمكن إعادة تحديد قيم الثابتة بعد إنشائها، لذا سيظل أي ثابت غير مُنشئundefined إلى الأبد. إذا حاولت تعريف ثابت بدون إعداده، ستظهر لك رسالة خطأ في البنية:

const myConstant;
Uncaught SyntaxError: missing = in const declaration

يؤدي محاولة تغيير قيمة متغيّر تمّت تعريفه باستخدام const بالطريقة التي قد تؤدي بها إلى تغيير قيمة متغيّر تمّت تعريفه باستخدام let (أو var) إلى خطأ نوع:

const myConstant = true;

myConstant = false;
> Uncaught TypeError: invalid assignment to const 'myConstant'

ومع ذلك، عندما يكون ثابت مرتبطًا بعنصر، يمكن تغيير سمات ذلك العنصر.

const constantObject = { "firstvalue" : true };

constantObject
> Object { firstvalue: true }

constantObject.secondvalue = false;

constantObject
> Object { firstvalue: true, secondvalue: false }

الثابت الذي يحتوي على عنصر هو إشارة إلى قيمة بيانات قابلة للتغيير. على الرغم من أنّه لا يمكن تغيير الثابت نفسه، يمكن تغيير خصائص الكائن المُشار إليه أو إضافتها إليه أو إزالتها:

const constantObject = { "firstvalue" : true };

constantObject = false
> Uncaught TypeError: invalid assignment to const 'constantObject'

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

نطاق المتغيّر

نطاق المتغيّر هو الجزء من النص البرمجي الذي يتوفّر فيه هذا المتغيّر. خارج نطاق المتغيّر، لن يتم تعريفه، ليس كمعرِّف يحتوي على قيمة undefined، ولكن كما لو لم يتمّ الإعلان عنه.

استنادًا إلى الكلمة الرئيسية التي تستخدمها لتعريف متغيّر والسياق الذي تحدّده فيه، يمكنك تحديد نطاق المتغيّرات لمنع العبارات (نطاق الحظر) أو الدوال الفردية (نطاق الدالة) أو تطبيق JavaScript بأكمله (النطاق العام).

نطاق الحظر

أي متغيّر تحدّده باستخدام let أو const يكون نطاقه هو عبارة الرمز البرمجي التي تحتوي عليه، ويعني ذلك أنّه لا يمكن الوصول إلى المتغيّر إلا داخل هذا الرمز البرمجي. تؤدي محاولة الوصول إلى متغيّر على مستوى الكتلة خارج الكتلة التي يحتويها إلى المحاولة نفسها التي تؤدي إلى خطأ محاولة الوصول إلى متغيّر غير موجود:

{
    let scopedVariable = true;
    console.log( scopedVariable );
}
> true

scopedVariable
> ReferenceError: scopedVariable is not defined

في ما يتعلّق بلغة JavaScript، لا يكون المتغيّر على مستوى الكتلة متوفّرًا خارج الكتلة التي يحتوي عليها. على سبيل المثال، يمكنك تحديد قيمة ثابتة داخل كتلة، ثم تحديد قيمة ثابتة أخرى خارج هذه الكتلة تستخدم المعرّف نفسه:

{
  const myConstant = false;
}
const myConstant = true;

scopedConstant;
> true

على الرغم من أنّ المتغيّر المُعلَن عنه لا يمكن أن يمتد إلى الكتلة الرئيسية، يكون متاحًا لجميع الكتل الفرعية:

{
    let scopedVariable = true;
    {
    console.log( scopedVariable );
    }
}
> true

يمكن تغيير قيمة متغيّر تمّت تعريفه من داخل كتلة فرعية:

{
    let scopedVariable = false;
    {
    scopedVariable = true;
    }
    console.log( scopedVariable );
}
> true

يمكن بدء متغيّر جديد باستخدام let أو const داخل ملف برمجي مشتق بدون أخطاء، حتى إذا كان يستخدم المعرّف نفسه كمتغيّر في ملف برمجي رئيسي:

{
    let scopedVariable = false;
    {
    let scopedVariable = true;
    }
    console.log( scopedVariable );
}
> false

نطاق الدالة

تكون النطاقات التي تُعلن فيها المتغيّرات باستخدام var هي النطاقات التي تتضمّن أقرب دالة إليها (أو وحدة الإعداد الثابت داخل فئة).

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

ويظل هذا هو الحال بعد استدعاء دالة. على الرغم من أنّه يتم بدء معالجة المتغيّر أثناء تنفيذ الدالة، لا يزال هذا المتغيّر غير متاح خارج نطاق الدالة:

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

myFunction();
> true

scopedVariable;
> ReferenceError: scopedVariable is not defined

النطاق الشامل

يتوفّر المتغيّر العام في تطبيق JavaScript بأكمله، ويظهر في أيّ وجميع الكتل والدوالّ، وفي أي نص برمجي على الصفحة.

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

أي متغيّر تم الإعلان عنه باستخدام var خارج دالة رئيسية، أو باستخدام let أو const خارج كتلة رئيسية، يكون عامًا:

var functionGlobal = true; // Global
let blockGlobal = true; // Global

{
    console.log( blockGlobal );
    console.log( functionGlobal );
}
> true
> true

(function() {
    console.log( blockGlobal );
    console.log( functionGlobal );
}());
> true
> true

يؤدي منح قيمة لمتغيّر بدون تحديده صراحةً (أي عدم استخدام var أو let أو const لإنشاءه) إلى ترقية المتغيّر إلى النطاق العمومي، حتى عند إعداده داخل دالة أو كتلة. يُطلق أحيانًا على المتغيّر الذي تم إنشاؤه باستخدام هذا النمط اسم "متغيّر عام ضمني".

function myFunction() {
    globalVariable = "global";

    return globalVariable
}

myFunction()\
> "global"

globalVariable\
> "global"

الرفع المتغير

يتم تصعيد المتغيّرات وتعريفات الدوالّ إلى أعلى نطاقها، ما يعني أنّ مفسّر JavaScript يعالج أي متغيّر تمّ تعريفه في أي نقطة في نص برمجي وينقله بفعالية إلى السطر الأول من النطاق المحيط به قبل تنفيذ النص البرمجي. وهذا يعني أنّه يمكن الإشارة إلى متغيّر تمّ تعريفه باستخدام var قبل تعريف المتغيّر بدون مواجهة خطأ:

hoistedVariable
> undefined

var hoistedVariable;

بما أنّه يتم استضافة بيان المتغيّر فقط، وليس عملية الإعداد، لا يتمّ رفع المتغيّرات التي لم يتمّ تحديدها بشكل صريح باستخدام var أو let أو const:

unhoistedVariable;
> Uncaught ReferenceError: unhoistedVariable is not defined

unhoistedVariable = true;

كما ذكرنا سابقًا، يتمّ تخصيص القيمة undefined لمتغيّر تمّت تعريفه ولكنّه لم يتمّ بدء تشغيله. ينطبق هذا السلوك أيضًا على تعريفات المتغيّرات التي تمّ نقلها، ولكن فقط على تلك التي تمّ تعريفها باستخدام var.

hoistedVariable
> undefined

var hoistedVariable = 2 + 2;

hoistedVariable\
> 4

ويعود هذا السلوك غير البديهي إلى حد كبير إلى قرارات التصميم التي تمّ اتّخاذها في أقدم إصدارات JavaScript، ولا يمكن تغييره بدون المخاطرة ب إيقاف المواقع الإلكترونية الحالية.

يعالج let وconst هذا السلوك من خلال عرض خطأ بدلاً من ذلك عند الوصول إلى ملف برمجي متغير قبل إنشائه:

{
    hoistedVariable;

    let hoistedVariable;
}
> Uncaught ReferenceError: can't access lexical declaration 'hoistedVariable' before initialization

يختلف هذا الخطأ عن خطأ "لم يتم تعريف المتغيّر hoistedVariable" الذي قد تتوقّعه عند محاولة الوصول إلى متغيّر لم يتم الإعلان عنه. ولأنّ JavaScript قد رفعت المتغيّر، فهي على دراية بأنّه سيتم إنشاء المتغيّر ضمن النطاق المحدّد. ومع ذلك، بدلاً من إتاحة هذا المتغيّر قبل تعريفه بالقيمة undefined، يُرسِل المُفسِّر خطأ. يُقال إنّ المتغيّرات التي تمّ تعريفها باستخدام let أو const (أو class) تكون متوفّرة في "منطقة زمنية غير صالحة" ("TDZ") من بداية الكتلة التي تحيط بها إلى النقطة في الرمز البرمجي التي تمّ فيها تعريف المتغيّر.

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

التحقّق من فهمك

ما هي أنواع الأحرف التي يمكنك بدء المعرّف بها؟

رسالة
شرطة سفلية
رقم

ما هي الطريقة المفضّلة لتعريف متغيّر يمكن تغيير قيمته في أي وقت؟

let
const
var