لماذا يجب عليك إعادة استخدام المنسقات بدلاً من إنشاء منسقات جديدة؟
إنشاء منسقات Intl مكلف، لكن إعادة استخدام نفس نسخة المنسق يحسن الأداء
مقدمة
عندما تقوم بإنشاء منسق Intl في جافا سكريبت، يقوم المتصفح بتنفيذ عمليات مكلفة لإعداد نسخة المنسق. يقوم بتحليل الخيارات الخاصة بك، وتحميل بيانات اللغة المحلية من القرص، وبناء هياكل بيانات داخلية للتنسيق. إذا قمت بإنشاء منسق جديد في كل مرة تحتاج فيها إلى تنسيق قيمة، فإنك تكرر هذا العمل المكلف دون داعٍ.
إعادة استخدام نسخ المنسق يلغي هذا العمل المتكرر. تقوم بإنشاء المنسق مرة واحدة وتستخدمه عدة مرات. هذا النمط مهم بشكل خاص في الحلقات، والدوال التي يتم استدعاؤها بشكل متكرر، والشفرة عالية الأداء حيث تقوم بتنسيق العديد من القيم.
يمكن أن يكون الفرق في الأداء بين إنشاء منسقات جديدة وإعادة استخدام المنسقات الموجودة كبيرًا. في السيناريوهات النموذجية، يمكن أن تقلل إعادة استخدام المنسقات من وقت التنسيق من مئات الميلي ثانية إلى بضع ميلي ثانية فقط.
لماذا يعتبر إنشاء المنسقات مكلفًا
يتضمن إنشاء منسق Intl العديد من العمليات المكلفة التي تحدث داخل المتصفح.
أولاً، يقوم المتصفح بتحليل والتحقق من صحة الخيارات التي تقدمها. يتحقق من أن معرفات اللغة المحلية صالحة، وأن الخيارات الرقمية ضمن النطاق، وأن الخيارات غير المتوافقة لا يتم دمجها. يتطلب هذا التحقق عمليات تحليل النصوص والبحث.
ثانيًا، يقوم المتصفح بإجراء تفاوض اللغة المحلية. يأخذ اللغة المحلية المطلوبة ويجد أفضل تطابق متاح من اللغات المحلية التي يدعمها المتصفح. يتضمن ذلك مقارنة معرفات اللغة المحلية وتطبيق قواعد الرجوع.
ثالثًا، يقوم المتصفح بتحميل بيانات خاصة باللغة المحلية. تحتاج منسقات التاريخ إلى أسماء الأشهر، وأسماء الأيام، وأنماط التنسيق للغة المحلية. تحتاج منسقات الأرقام إلى قواعد التجميع، وفواصل عشرية، وأحرف الأرقام. تأتي هذه البيانات من قاعدة بيانات اللغة المحلية الداخلية للمتصفح ويجب تحميلها في الذاكرة.
رابعًا، يقوم المتصفح ببناء هياكل بيانات داخلية للتنسيق. يقوم بتجميع أنماط التنسيق في تمثيلات فعالة وإعداد آلات الحالة لمعالجة القيم. تستمر هذه الهياكل طوال عمر المنسق.
كل هذا العمل يحدث في كل مرة تقوم فيها بإنشاء منسق. إذا قمت بإنشاء منسق، واستخدامه مرة واحدة، ثم التخلص منه، فإنك تهدر كل عمل الإعداد هذا.
الفرق في الأداء
يصبح تأثير الأداء لإعادة إنشاء المنسقات ملحوظًا عندما تقوم بتنسيق العديد من القيم.
فكر في الكود الذي يقوم بتنسيق قائمة من التواريخ دون إعادة استخدام المنسق.
const dates = [
new Date('2024-01-15'),
new Date('2024-02-20'),
new Date('2024-03-10')
];
// ينشئ منسقًا جديدًا لكل تاريخ
dates.forEach(date => {
const formatted = date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
console.log(formatted);
});
تقوم طريقة toLocaleDateString() بإنشاء نسخة جديدة من DateTimeFormat داخليًا لكل تاريخ تقوم بتنسيقه. بالنسبة لثلاثة تواريخ، هذا ينشئ ثلاثة منسقات. بالنسبة لألف تاريخ، فإنه ينشئ ألف منسق.
قارن هذا بالكود الذي ينشئ منسقًا واحدًا ويعيد استخدامه.
const dates = [
new Date('2024-01-15'),
new Date('2024-02-20'),
new Date('2024-03-10')
];
// إنشاء المنسق مرة واحدة
const formatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
// إعادة استخدام المنسق لكل تاريخ
dates.forEach(date => {
const formatted = formatter.format(date);
console.log(formatted);
});
هذا الكود ينشئ منسقًا واحدًا ويستخدمه ثلاث مرات. بالنسبة لألف تاريخ، فإنه لا يزال ينشئ منسقًا واحدًا ويستخدمه ألف مرة.
يزداد الفرق الزمني بين هذه الأساليب مع زيادة عدد القيم التي تقوم بتنسيقها. يمكن أن يستغرق تنسيق ألف تاريخ عن طريق إنشاء ألف منسق وقتًا أطول بأكثر من 50 مرة من تنسيقها باستخدام منسق واحد معاد استخدامه.
إعادة استخدام المنسقات على نطاق الوحدة
أبسط طريقة لإعادة استخدام المنسق هي إنشاؤه مرة واحدة على نطاق الوحدة واستخدامه في جميع أنحاء الوحدة الخاصة بك.
// إنشاء منسق على نطاق الوحدة
const dateFormatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
function formatDate(date) {
return dateFormatter.format(date);
}
function formatDates(dates) {
return dates.map(date => dateFormatter.format(date));
}
// جميع الدوال تشارك نفس نسخة المنسق
console.log(formatDate(new Date()));
console.log(formatDates([new Date(), new Date()]));
يعمل هذا النمط بشكل جيد عندما تقوم بتنسيق القيم بنفس الطريقة في جميع أنحاء الكود الخاص بك. يعيش المنسق طوال فترة حياة تطبيقك، ويمكن لكل دالة تحتاج إليه استخدام نفس النسخة.
يعمل نفس النمط لمنسقات الأرقام ومنسقات القوائم وجميع منسقات Intl الأخرى.
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
const listFormatter = new Intl.ListFormat('en-US', {
style: 'long',
type: 'conjunction'
});
function formatPrice(amount) {
return numberFormatter.format(amount);
}
function formatNames(names) {
return listFormatter.format(names);
}
إعادة استخدام المنسقات في الدوال
عندما تحتاج إلى خيارات تنسيق مختلفة في أجزاء مختلفة من الكود الخاص بك، يمكنك إنشاء منسقات داخل الدوال والاعتماد على الإغلاقات (closures) للحفاظ عليها.
function createDateFormatter() {
const formatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
return function formatDate(date) {
return formatter.format(date);
};
}
const formatDate = createDateFormatter();
// يتم إنشاء المنسق مرة واحدة عند استدعاء createDateFormatter
// كل استدعاء لـ formatDate يعيد استخدام نفس المنسق
console.log(formatDate(new Date('2024-01-15')));
console.log(formatDate(new Date('2024-02-20')));
console.log(formatDate(new Date('2024-03-10')));
هذا النمط مفيد عندما تريد إنشاء منسق مُعد يتم إعادة استخدامه ولكن لا تريد كشف المنسق نفسه.
متى تكون إعادة استخدام المنسق أكثر أهمية
توفر إعادة استخدام المنسقات أكبر فائدة في سيناريوهات محددة.
السيناريو الأول هو الحلقات. إذا قمت بتنسيق القيم داخل حلقة، فإن إنشاء منسق جديد في كل تكرار يضاعف التكلفة بعدد التكرارات.
// غير فعال: ينشئ N من المنسقات
for (let i = 0; i < 1000; i++) {
const formatted = new Intl.NumberFormat('en-US').format(i);
processValue(formatted);
}
// فعال: ينشئ منسق واحد
const formatter = new Intl.NumberFormat('en-US');
for (let i = 0; i < 1000; i++) {
const formatted = formatter.format(i);
processValue(formatted);
}
السيناريو الثاني هو الدوال التي يتم استدعاؤها بشكل متكرر. إذا كانت الدالة تقوم بتنسيق القيم ويتم استدعاؤها عدة مرات، فإن إعادة استخدام المنسق يتجنب إعادة إنشاء المنسق في كل استدعاء.
// غير فعال: ينشئ منسق في كل استدعاء
function formatCurrency(amount) {
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
return formatter.format(amount);
}
// فعال: ينشئ منسق مرة واحدة
const currencyFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
function formatCurrency(amount) {
return currencyFormatter.format(amount);
}
السيناريو الثالث هو معالجة مجموعات البيانات الكبيرة. عندما تقوم بتنسيق مئات أو آلاف القيم، تصبح تكلفة إعداد إنشاء المنسقات جزءًا كبيرًا من الوقت الإجمالي.
// غير فعال لمجموعات البيانات الكبيرة
function processRecords(records) {
return records.map(record => ({
date: new Intl.DateTimeFormat('en-US').format(record.date),
amount: new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(record.amount)
}));
}
// فعال لمجموعات البيانات الكبيرة
const dateFormatter = new Intl.DateTimeFormat('en-US');
const amountFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
function processRecords(records) {
return records.map(record => ({
date: dateFormatter.format(record.date),
amount: amountFormatter.format(record.amount)
}));
}
في هذه السيناريوهات، تقلل إعادة استخدام المنسقات من الوقت المستغرق في عمليات التنسيق وتحسن استجابة التطبيق.
تخزين المنسقات مؤقتًا مع خيارات مختلفة
عندما تحتاج إلى استخدام منسقات مع العديد من مجموعات الخيارات المختلفة، يمكنك تخزين المنسقات مؤقتًا بناءً على تكوينها.
const formatterCache = new Map();
function getNumberFormatter(locale, options) {
// Create a cache key from locale and options
const key = JSON.stringify({ locale, options });
// Return cached formatter if it exists
if (formatterCache.has(key)) {
return formatterCache.get(key);
}
// Create new formatter and cache it
const formatter = new Intl.NumberFormat(locale, options);
formatterCache.set(key, formatter);
return formatter;
}
// First call creates and caches formatter
const formatter1 = getNumberFormatter('en-US', { style: 'currency', currency: 'USD' });
console.log(formatter1.format(42.50));
// Second call reuses cached formatter
const formatter2 = getNumberFormatter('en-US', { style: 'currency', currency: 'USD' });
console.log(formatter2.format(99.99));
// Different options create and cache a new formatter
const formatter3 = getNumberFormatter('en-US', { style: 'percent' });
console.log(formatter3.format(0.42));
يتيح لك هذا النمط الحصول على فوائد إعادة استخدام المنسق حتى عندما تحتاج إلى تكوينات تنسيق مختلفة في أجزاء مختلفة من التعليمات البرمجية الخاصة بك.
تحسينات المتصفح الحديث
لقد قامت محركات جافا سكريبت الحديثة بتحسين إنشاء منسقات Intl لتقليل تكلفة الأداء. أصبح إنشاء المنسقات أسرع اليوم مما كان عليه في المتصفحات القديمة.
ومع ذلك، لا تزال إعادة استخدام المنسقات من أفضل الممارسات. حتى مع التحسينات، لا يزال إنشاء منسق أكثر تكلفة من استدعاء طريقة format() على منسق موجود. الفرق في التكلفة أصغر مما كان عليه من قبل، لكنه لا يزال موجودًا.
في التعليمات البرمجية عالية الأداء، والتعليمات البرمجية التي تعمل في حلقات، والتعليمات البرمجية التي تعالج مجموعات بيانات كبيرة، تستمر إعادة استخدام المنسق في توفير فوائد قابلة للقياس. إن تحسين إنشاء المنسق لا يلغي قيمة إعادة استخدام المنسقات.
النقاط الرئيسية
إنشاء منسقات Intl مكلف لأن المتصفح يجب أن يقوم بتحليل الخيارات، وإجراء تفاوض اللغة، وتحميل بيانات اللغة، وبناء هياكل البيانات الداخلية. يحدث هذا العمل في كل مرة تقوم فيها بإنشاء منسق.
إعادة استخدام نسخ المنسق يتجنب تكرار هذا العمل. تقوم بإنشاء المنسق مرة واحدة وتستدعي طريقة format() الخاصة به عدة مرات. هذا يقلل من الوقت المستغرق في عمليات التنسيق.
إعادة استخدام المنسق مهمة بشكل خاص في الحلقات، والدوال التي يتم استدعاؤها بشكل متكرر، والشيفرة التي تعالج مجموعات بيانات كبيرة. في هذه السيناريوهات، يمكن أن تصبح تكلفة إنشاء المنسقات جزءًا كبيرًا من إجمالي وقت التنفيذ.
أبسط نمط لإعادة الاستخدام هو إنشاء المنسقات على مستوى الوحدة. بالنسبة للسيناريوهات الأكثر تعقيدًا، يمكنك استخدام الإغلاقات أو التخزين المؤقت بناءً على خيارات التكوين.