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

ما المقصود بالمصفوفات الكثيفة في JavaScript؟
المصفوفة الكثيفة هي الشكل الأكثر شيوعًا للمصفوفات، وهي التي يتعامل معها معظم المطورين يوميًا. في هذا النوع تكون العناصر مرتبة بشكل متسلسل، وتبدأ الفهارس من 0 دون وجود فجوات بين العناصر.
عند استخدام هذا النوع، فإن الخاصية length تعبّر بدقة عن عدد العناصر الموجودة فعليًا داخل المصفوفة.
let array = [1, 2, 3, 4, 5]
array.length // Returns 5
في المثال السابق، عدد العناصر الحقيقي يساوي 5، كما أن قيمة array.length تساوي أيضًا 5، لذلك لا توجد أي مفاجآت في السلوك.
ما هي المصفوفات المتناثرة؟
المصفوفة المتناثرة هي مصفوفة لا تكون عناصرها متجاورة بالكامل، وقد تحتوي على فجوات أو فراغات منطقية بين الفهارس. بمعنى آخر، ليست كل قيمة بين أول فهرس وآخر فهرس موجودة فعليًا.
يمكن وصف هذا النوع بأنه مصفوفة تحتوي على holes، أي أماكن غير مملوءة بعناصر حقيقية.
let array = [];
array[100] = "Holes now exist";
array.length // 101, but only 1 element
في هذا المثال، أُضيف عنصر واحد فقط عند الفهرس 100، لكن قيمة length أصبحت 101. السبب هو أن JavaScript تحسب الطول اعتمادًا على أكبر فهرس مستخدم زائد 1، وليس بناءً على عدد العناصر الفعلية فقط.
لماذا لا تعبّر الخاصية length دائمًا عن عدد العناصر؟
في المصفوفات الكثيفة، تكون الخاصية length مطابقة تقريبًا لعدد العناصر. أما في المصفوفات المتناثرة، فقد تكون قيمة length أكبر بكثير من عدد القيم الموجودة فعلًا، لأن الفجوات تُحتسب ضمن المجال العام للفهارس رغم أنها لا تمثل عناصر حقيقية.
لماذا يمكن أن تكون مصفوفات JavaScript متناثرة؟
لفهم السبب، من المهم معرفة أن المصفوفات في JavaScript هي في الأساس كائنات من نوع Object تعمل بطريقة خاصة. الفهارس العددية فيها تُستخدم كمفاتيح، بينما القيم المخزنة تكون هي عناصر المصفوفة.

let array = [];
array[20] = {};
array[100] = {};
array[19] = {};
alert(array.length); // Logs 101
هنا، آخر فهرس مستخدم هو 100، لذلك تصبح قيمة length مساوية لـ 101. لا يهم إن كان عدد العناصر الفعلية 3 فقط؛ فالمهم هو أكبر فهرس تم الوصول إليه.
بمعنى أدق، الخاصية length في المصفوفة لا تبحث عن عدد القيم المخزنة، بل تعيد أعلى فهرس موجود + 1.

كيف تحصل على مصفوفة متناثرة؟
هناك عدة طرق شائعة قد تؤدي إلى إنشاء مصفوفة متناثرة، أحيانًا بشكل مقصود وأحيانًا عن طريق الخطأ.
1) استخدام المُنشئ Array
عند إنشاء مصفوفة باستخدام new Array(10)، فإنك لا تنشئ عشرة عناصر فعلية، بل تنشئ مصفوفة بطول 10 تحتوي على فجوات.
let array = new Array(10); // array initialized with no elements
array.length // 10

2) الإسناد إلى فهرس بعيد
إذا وضعت قيمة داخل فهرس كبير مباشرة، فستنشأ فجوات بين بداية المصفوفة وذلك الفهرس.
array[1000] = 0; // Assignment adds one element
array.length // But .length returns 1001

3) استخدام العامل delete
عند حذف عنصر من المصفوفة باستخدام delete، لا يتم تقليص الطول ولا إعادة ترتيب الفهارس، بل يتكون فراغ في موضع العنصر المحذوف.
let array = [1, 2, 3, 4, 5]
delete array[0]
array.length // .length returns 5

هذه نقطة مهمة جدًا: العامل delete يحذف القيمة، لكنه لا يعيد بناء المصفوفة كمصفوفة كثيفة.
4) تهيئة المصفوفة مع فجوات منذ البداية
يمكن أيضًا إنشاء فجوات مباشرة أثناء كتابة المصفوفة، سواء عن قصد أو نتيجة خطأ بسيط مثل فاصلة زائدة.
[,,,,] // You have created an array with nothing but holes
[1, 2, 3, 4, , 5] // A hole exists between 4 and 5

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

أما بعض إصدارات Firefox فقد تعرض هذه الفجوات بطريقة مختلفة بصريًا.

لهذا السبب، لا ينبغي الاعتماد على شكل العرض البصري وحده عند التشخيص. الأفضل دائمًا فحص length، والتحقق من وجود القيم فعليًا عند كل فهرس.
متى تسبب المصفوفات المتناثرة أخطاء فعلية؟
المشكلة تظهر غالبًا عند تنفيذ منطق يتوقع أن كل فهرس بين 0 وarray.length - 1 يحتوي على قيمة صالحة. في المصفوفات المتناثرة، هذا الافتراض غير صحيح.
لذلك قد يؤدي المرور التقليدي على المصفوفة إلى نتائج غير متوقعة، مثل:
- محاولة قراءة خصائص من قيمة غير موجودة.
- تشغيل دوال على عناصر فارغة منطقيًا.
- الحصول على مخرجات مضللة أثناء التصحيح.
- اختلاف السلوك بين بيئات التشغيل والمتصفحات.
طريقة عملية للتعامل مع الفجوات بأمان
إذا كنت تمر على المصفوفة بحلقة مثل for، فمن المفيد التحقق أولًا من وجود قيمة قبل استخدامها.
let array = [,,,,]
for (let i = 0; i < array.length; i++) {
if (array[i]) {
console.log(array[i])
}
}
الفكرة هنا أن الفجوات لا تحتوي على قيم قابلة للاستخدام، ولذلك يمكن تجاهلها قبل تنفيذ المنطق البرمجي عليها.
لكن يجدر الانتباه إلى أن التحقق باستخدام if (array[i]) قد يستبعد أيضًا القيم الصحيحة لكنها falsy مثل:
0""falsenull
لذلك في الحالات العملية يكون من الأفضل أحيانًا استخدام فحص أدق، مثل التحقق مما إذا كان الفهرس موجودًا داخل المصفوفة.
for (let i = 0; i < array.length; i++) {
if (i in array) {
console.log(array[i])
}
}
هذا الأسلوب أكثر دقة، لأنه يميّز بين الفجوة الحقيقية وبين قيمة موجودة لكنها تُعد falsy.
أفضل الممارسات عند التعامل مع المصفوفات في JavaScript
- تجنب استخدام
deleteمع المصفوفات إذا كان هدفك إزالة عنصر وإعادة ترتيب الفهارس، واستخدم بدلًا منه أساليب مناسبة مثلsplice(). - لا تعتمد على
lengthوحدها لمعرفة عدد العناصر الفعلية في المصفوفات التي قد تحتوي على فجوات. - تحقق من الفهرس باستخدام
inأو أسلوب مناسب قبل تنفيذ عمليات حساسة على العنصر. - كن حذرًا عند استخدام
new Array(n)إذا كنت تتوقع قيمًا فعلية جاهزة للاستخدام. - راجع عرض المصفوفة في أدوات المطور، لكن لا تجعل التمثيل البصري هو المصدر الوحيد للحكم على محتواها.
مقارنة سريعة بين المصفوفات الكثيفة والمتناثرة
| الجانب | المصفوفة الكثيفة | المصفوفة المتناثرة |
|---|---|---|
| ترتيب الفهارس | متسلسل دون فجوات | قد يحتوي على فجوات |
دقة length |
تعكس عدد العناصر غالبًا | تعكس أعلى فهرس + 1 |
| سهولة التنبؤ بالسلوك | أعلى | أقل |
| الظهور في أدوات المطور | واضح ومباشر | يختلف حسب المتصفح |
| مسبباتها الشائعة | إضافة عناصر بشكل طبيعي | delete، فهارس بعيدة، new Array()، فواصل زائدة |
الخلاصة التقنية
الفرق بين المصفوفات الكثيفة والمصفوفات المتناثرة في JavaScript ليس مجرد تفصيل نظري، بل هو نقطة عملية تؤثر مباشرة في سلوك التطبيق، خاصة أثناء التكرار والتحقق من القيم. أهم ما يجب تذكره هو أن length لا تعني دائمًا عدد العناصر الحقيقية، بل قد تعكس فقط أكبر فهرس مستخدم. من الناحية التقنية، يُفضَّل الحفاظ على المصفوفات في صورة كثيفة كلما أمكن، لأن ذلك يجعل الكود أوضح وأسهل في الصيانة وأقل عرضة للأخطاء الخفية.