احذر من lodash.memoize
تبدو وظيفة memoize في Lodash وكأنها تحسين مجاني للأداء. تقوم بتغليف دالة وتحصل على ذاكرة تخزين مؤقت (cache).
لكن السلوك الافتراضي يحتوي على فخ؛ فهي تبني مفتاح التخزين المؤقت من الوسيط الأول فقط، وتتجاهل أي وسائط أخرى.
إذا كنت تستخدم وسيطاً واحداً، فأنت في أمان. أما إذا كنت تستخدم وسيطين أو أكثر، فستتسبب في حدوث أخطاء (bugs).
انظر إلى هذا المثال:
const add = memoize((a, b) => a + b);
add(1, 2); // تُرجع 3. تقوم بتخزين النتيجة تحت المفتاح 1.
add(1, 9); // تُرجع 3. هذا خطأ، كان يجب أن تكون 10.
ترى Lodash الرقم 1 مرة أخرى، فتجده في ذاكرة التخزين المؤقت، ثم تُرجع النتيجة القديمة. إنها لا تنظر أبداً إلى الرقم 9.
تنسيق العملات هو فخ شائع.
const formatPrice = memoize((amount, currency) =>
new Intl.NumberFormat('en', { style: 'currency', currency }).format(amount)
);
formatPrice(100, 'USD'); // تُرجع "$100.00". المفتاح هو 100.
formatPrice(100, 'EUR'); // تُرجع "$100.00". هذا خطأ.
تتجاهل الاستدعاء الثاني 'EUR'. فهي ترى الرقم 100 في ذاكرة التخزين المؤقت وتُرجع الدولارات بدلاً من اليورو. لا يوجد خطأ، ولا يوجد تحذير؛ أنت فقط تعرض مبالغ مالية خاطئة لمستخدميك.
يجب عليك تقديم وسيط ثانٍ لتحديد مفتاح التخزين المؤقت. يجب أن يغطي هذا المفتاح كل المدخلات.
const formatPrice = memoize(
(amount, currency) =>
new Intl.NumberFormat('en', { style: 'currency', currency }).format(amount),
(amount, currency) => `${amount}|${currency}`
);
formatPrice(100, 'USD'); // "$100.00"
formatPrice(100, 'EUR'); // "€100.00"
يجب أن يشمل المفتاح كل ما يؤدي إلى تغيير المخرجات.
خاطرة أخيرة: الـ memoization تضيف مخاطرة. استخدمها فقط عندما تكون الدالة مكلفة (expensive) وتعمل بشكل متكرر بنفس المدخلات. بالنسبة للدوال البسيطة، قم باستدعائها فحسب؛ فخطر حدوث خطأ غالباً ما يكون أكبر من السرعة التي ستكتسبها.
النقاط الرئيسية:
- المفاتيح الافتراضية تستخدم الوسيط الأول فقط.
- استخدم resolver لتضمين جميع الوسائط في المفتاح.
- المفتاح الجيد يعكس كل ما يغير المخرجات.
- لا تستخدم memoize للدوال البسيطة (cheap functions).
المصدر: https://dev.to/figsify/beware-of-lodashmemoize-it-only-remembers-the-first-argument-4cjl