Kodokon kodokon.com

الذاكرة والأداء: GC، WeakMap/WeakRef، debounce/throttle

افهم كيف يعمل جامع القمامة، استخدم المراجع الضعيفة لتجنب التسريبات، وخفف من العمل المكلف باستخدام debounce وthrottle.

11 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

يعتمد جامع القمامة في V8 على إمكانية الوصول (reachability): يصبح الكائن قابلا للجمع حالما لا تربطه أي سلسلة مراجع بالجذور (المكدس، النطاقات النشطة، المتغيرات العامة). جامع القمامة جيلي: تعيش الكائنات الحديثة في *الحضانة*، تجمع كثيرا وبسرعة (scavenge)؛ ويرقى الناجون إلى الجيل القديم، الذي يعالج بمسح-وكنس تزايدي. لا تأتي التسريبات الكلاسيكية من جامع القمامة بل من مراجع نسيتها: ذاكرات مؤقتة قائمة على Map، مستمعون لا يفصلون أبدا، مغلفات تلتقط كائنات كبيرة.

JAVASCRIPT
const strongCache = new Map();
const weakCache = new WeakMap();

let session = { user: "ada" };
strongCache.set(session, "data");
weakCache.set(session, "data");

session = null;
// Map: the key retains the object -> memory leak
// WeakMap: the entry becomes collectable
نفس الكود، مصيران معاكسان للذاكرة.

لا يحتفظ WeakMap بمفاتيحه: عندما يصبح مفتاح غير قابل للوصول من أي مكان آخر، يختفي المدخل بأكمله. النتائج التصميمية: يجب أن تكون المفاتيح كائنات (أو رموزا غير مسجلة)، والبنية ليست قابلة للتكرار ولا للقياس - كشف حجمها سيكشف سلوك جامع القمامة، وهو غير حتمي. يذهب WeakRef أبعد: يعطيك مرجعا ضعيفا مباشرا، يفك بـ deref().

JAVASCRIPT
let config = { theme: "dark" };
const ref = new WeakRef(config);

function readTheme() {
  const target = ref.deref();
  return target ? target.theme : "default";
}

console.log(readTheme()); // "dark"
config = null;
// After a GC pass, deref() may
// return undefined: plan a fallback.
deref() قد تعيد الكائن... أو undefined.

من جهة الأداء المدرك، يمكن أن تطلق الأحداث عالية التردد (scroll، resize، input، mousemove) مئات الاستدعاءات في الثانية. استراتيجيتان متكاملتان: debounce لا تشغل الدالة إلا بعد فترة هدوء (مثالي للبحث أثناء الكتابة)، بينما throttle تضمن تنفيذا واحدا على الأكثر لكل فترة (مثالي لتتبع التمرير).

JAVASCRIPT
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(
      () => fn.apply(this, args),
      delay
    );
  };
}
Debounce: الاستدعاء الأخير فقط من الدفقة يحتسب.
JAVASCRIPT
function throttle(fn, interval) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}
Throttle: تنفيذ واحد على الأكثر لكل فترة.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. لماذا يجب أن تكون مفاتيح WeakMap كائنات بدلا من أوليات مثل السلاسل النصية؟
    • لأسباب تتعلق بأداء التجزئة
    • لأن مرجع كائن فقط يمكن أن يصبح غير قابل للوصول ويطلق إزالة المدخل
    • إنه قيد تاريخي رفع منذ ES2021
    • لأن الأوليات لا يمكن استخدامها كمفاتيح في JavaScript
  2. ما الفرق الجوهري بين debounce وthrottle؟
    • debounce تعمل بعد فترة هدوء، throttle تضمن تنفيذا واحدا على الأكثر لكل فترة
    • debounce غير متزامنة، throttle متزامنة
    • throttle تلغي الاستدعاءات السابقة، debounce تصفها في طابور
    • هما اسمان لنفس التقنية
  3. ماذا يمكن أن تعيد ref.deref() على WeakRef؟
    • الكائن الأصلي دائما، ما دام WeakRef موجودا
    • الكائن إن كان لا يزال حيا، أو undefined إن جمع
    • نسخة عميقة من الكائن
    • null إن جمع الكائن