العربية

مقارنة أداء تفصيلية بين حلقات for و forEach و map في جافاسكريبت، مع أمثلة عملية وأفضل حالات الاستخدام للمطورين.

مقارنة الأداء: حلقة For مقابل forEach مقابل Map في جافاسكريبت

تقدم جافاسكريبت عدة طرق للتكرار عبر المصفوفات، لكل منها بناءها النحوي الخاص، ووظائفها، والأهم من ذلك، خصائص أدائها. إن فهم الاختلافات بين حلقات for، وforEach، وmap أمر بالغ الأهمية لكتابة كود جافاسكريبت فعال ومُحسَّن، خاصة عند التعامل مع مجموعات بيانات كبيرة أو تطبيقات تتطلب أداءً عاليًا. يقدم هذا المقال مقارنة أداء شاملة، ويستكشف الفروق الدقيقة لكل طريقة، ويقدم إرشادات حول متى يجب استخدام كل منها.

مقدمة: التكرار في جافاسكريبت

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

يمكن أن يؤثر اختيار طريقة التكرار الصحيحة بشكل كبير على أداء الكود الخاص بك. دعنا نتعمق في كل طريقة ونحلل خصائص أدائها.

حلقة for: النهج التقليدي

حلقة for هي البنية التكرارية الأساسية والأكثر فهمًا في جافاسكريبت والعديد من لغات البرمجة الأخرى. إنها توفر تحكمًا صريحًا في عملية التكرار.

البنية والاستخدام

بنية حلقة for واضحة ومباشرة:


for (let i = 0; i < array.length; i++) {
  // الكود الذي سيتم تنفيذه لكل عنصر
  console.log(array[i]);
}

فيما يلي تفصيل للمكونات:

خصائص الأداء

تعتبر حلقة for بشكل عام أسرع طريقة تكرار في جافاسكريبت. إنها توفر أقل حمل إضافي (overhead) لأنها تتعامل مباشرة مع العداد وتصل إلى عناصر المصفوفة باستخدام فهرسها.

المزايا الرئيسية:

مثال: معالجة الطلبات من جميع أنحاء العالم

تخيل أنك تعالج قائمة من الطلبات من بلدان مختلفة. قد تحتاج إلى التعامل مع الطلبات من بلدان معينة بشكل مختلف لأغراض ضريبية.


const orders = [
  { id: 1, country: 'USA', amount: 100 },
  { id: 2, country: 'Canada', amount: 50 },
  { id: 3, country: 'UK', amount: 75 },
  { id: 4, country: 'Germany', amount: 120 },
  { id: 5, country: 'USA', amount: 80 }
];

function processOrders(orders) {
  for (let i = 0; i < orders.length; i++) {
    const order = orders[i];
    if (order.country === 'USA') {
      console.log(`Processing USA order ${order.id} with amount ${order.amount}`);
      // تطبيق منطق الضرائب الخاص بالولايات المتحدة
    } else {
      console.log(`Processing order ${order.id} with amount ${order.amount}`);
    }
  }
}

processOrders(orders);

forEach: نهج وظيفي للتكرار

forEach هي دالة من الرتبة العليا متوفرة في المصفوفات توفر طريقة أكثر إيجازًا ووظيفية للتكرار. تنفذ دالة معينة مرة واحدة لكل عنصر في المصفوفة.

البنية والاستخدام

بنية forEach هي كما يلي:


array.forEach(function(element, index, array) {
  // الكود الذي سيتم تنفيذه لكل عنصر
  console.log(element, index, array);
});

تتلقى دالة رد الاتصال (callback function) ثلاث وسائط:

خصائص الأداء

forEach أبطأ بشكل عام من حلقة for. هذا لأن forEach تتضمن الحمل الإضافي لاستدعاء دالة لكل عنصر، مما يضيف إلى وقت التنفيذ. ومع ذلك، قد يكون الفرق ضئيلاً بالنسبة للمصفوفات الأصغر.

المزايا الرئيسية:

العيوب الرئيسية:

مثال: تنسيق التواريخ من مناطق مختلفة

تخيل أن لديك مصفوفة من التواريخ بتنسيق قياسي وتحتاج إلى تنسيقها وفقًا للتفضيلات الإقليمية المختلفة.


const dates = [
  '2024-01-15',
  '2023-12-24',
  '2024-02-01'
];

function formatDate(dateString, locale) {
  const date = new Date(dateString);
  return date.toLocaleDateString(locale);
}

function formatDates(dates, locale) {
  dates.forEach(dateString => {
    const formattedDate = formatDate(dateString, locale);
    console.log(`Formatted date (${locale}): ${formattedDate}`);
  });
}

formatDates(dates, 'en-US'); // تنسيق أمريكي
formatDates(dates, 'en-GB'); // تنسيق بريطاني
formatDates(dates, 'de-DE'); // تنسيق ألماني

map: تحويل المصفوفات

map هي دالة أخرى من الرتبة العليا مصممة لتحويل المصفوفات. إنها تنشئ مصفوفة جديدة عن طريق تطبيق دالة معينة على كل عنصر من عناصر المصفوفة الأصلية.

البنية والاستخدام

بنية map تشبه forEach:


const newArray = array.map(function(element, index, array) {
  // كود لتحويل كل عنصر
  return transformedElement;
});

تتلقى دالة رد الاتصال أيضًا نفس الوسائط الثلاث مثل forEach (element، index، و array)، ولكن يجب أن تُرجع قيمة، والتي ستكون العنصر المقابل في المصفوفة الجديدة.

خصائص الأداء

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

المزايا الرئيسية:

العيوب الرئيسية:

مثال: تحويل العملات من دول مختلفة إلى الدولار الأمريكي

افترض أن لديك مصفوفة من المعاملات بعملات مختلفة وتحتاج إلى تحويلها جميعًا إلى الدولار الأمريكي لأغراض إعداد التقارير.


const transactions = [
  { id: 1, currency: 'EUR', amount: 100 },
  { id: 2, currency: 'GBP', amount: 50 },
  { id: 3, currency: 'JPY', amount: 7500 },
  { id: 4, currency: 'CAD', amount: 120 }
];

const exchangeRates = {
  'EUR': 1.10, // مثال على سعر الصرف
  'GBP': 1.25,
  'JPY': 0.007,
  'CAD': 0.75
};

function convertToUSD(transaction) {
  const rate = exchangeRates[transaction.currency];
  if (rate) {
    return transaction.amount * rate;
  } else {
    return null; // للإشارة إلى فشل التحويل
  }
}

const usdAmounts = transactions.map(transaction => convertToUSD(transaction));

console.log(usdAmounts);

قياس الأداء (Benchmarking)

لمقارنة أداء هذه الطرق بشكل موضوعي، يمكننا استخدام أدوات قياس الأداء مثل console.time() و console.timeEnd() في جافاسكريبت أو مكتبات قياس الأداء المخصصة. إليك مثال أساسي:


const arraySize = 100000;
const largeArray = Array.from({ length: arraySize }, (_, i) => i + 1);

// حلقة For
console.time('For loop');
for (let i = 0; i < largeArray.length; i++) {
  // افعل شيئًا ما
  largeArray[i] * 2;
}
console.timeEnd('For loop');

// forEach
console.time('forEach');
largeArray.forEach(element => {
  // افعل شيئًا ما
  element * 2;
});
console.timeEnd('forEach');

// Map
console.time('Map');
largeArray.map(element => {
  // افعل شيئًا ما
  return element * 2;
});
console.timeEnd('Map');

النتائج المتوقعة:

في معظم الحالات، ستلاحظ ترتيب الأداء التالي (من الأسرع إلى الأبطأ):

  1. حلقة for
  2. forEach
  3. map

اعتبارات هامة:

أفضل الممارسات وحالات الاستخدام

يعتمد اختيار طريقة التكرار الصحيحة على المتطلبات المحددة لمهمتك. فيما يلي ملخص لأفضل الممارسات:

سيناريوهات وأمثلة من العالم الحقيقي

فيما يلي بعض السيناريوهات من العالم الحقيقي حيث قد تكون كل طريقة تكرار هي الخيار الأنسب:

ما وراء الأساسيات: طرق تكرار أخرى

بينما يركز هذا المقال على حلقات for، وforEach، وmap، تقدم جافاسكريبت طرق تكرار أخرى يمكن أن تكون مفيدة في مواقف محددة:

الخاتمة

يعد فهم خصائص الأداء وحالات استخدام طرق التكرار المختلفة في جافاسكريبت أمرًا ضروريًا لكتابة كود فعال ومُحسَّن. بينما توفر حلقات for بشكل عام أفضل أداء، فإن forEach و map توفران بدائل أكثر إيجازًا ووظيفية مناسبة للعديد من السيناريوهات. من خلال النظر بعناية في المتطلبات المحددة لمهمتك، يمكنك اختيار طريقة التكرار الأنسب وتحسين كود جافاسكريبت الخاص بك من أجل الأداء والقراءة.

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