العثور على حالات تسرُّب الذاكرة الصعبة وإصلاحها الناتجة عن النوافذ المنفصلة
ما هو تسرب الذاكرة في JavaScript؟
تسرُّب الذاكرة هو زيادة غير مقصودة في مقدار الذاكرة التي يستخدمها التطبيق بمرور الوقت. في JavaScript، تحدث عمليات تسرُّب الذاكرة عندما لا تكون العناصر مطلوبة بعد الآن، ولكن لا تزال تتم الإشارة إليها من قِبل الدوالّ أو العناصر الأخرى. وتمنع هذه الإشارات جمع العناصر غير المطلوبة من قِبل أداة جمع المهملات.
وظيفة أداة جمع المهملات هي تحديد العناصر التي لم يعُد بالإمكان الوصول إليها من التطبيق واستعادتها. يعمل هذا الإجراء حتى عندما تشير العناصر إلى نفسها أو تشير إلى بعضها بشكل دوري. وبعد أن لا تتبقّى أيّ إحالات يمكن للتطبيق من خلالها الوصول إلى مجموعة من العناصر، يمكن جمع المهملات.
let A = {};
console.log(A); // local variable reference
let B = {A}; // B.A is a second reference to A
A = null; // unset local variable reference
console.log(B.A); // A can still be referenced by B
B.A = null; // unset B's reference to A
// No references to A are left. It can be garbage collected.
تحدث فئة صعبة بشكل خاص من تسرُّب الذاكرة عندما يشير أحد التطبيقات إلى عناصر لها دورة حياة خاصة بها، مثل عناصر DOM أو النوافذ المنبثقة. من الممكن أن تصبح هذه الأنواع من العناصر غير مستخدَمة بدون علم التطبيق، ما يعني أنّ رمز التطبيق قد يحتوي على سوى المراجع المتبقية لعنصر يمكن جمع المهملات منه.
ما هي النافذة المنفصلة؟
في المثال التالي، يتضمّن تطبيق عرض الشرائح أزرارًا لفتح ملف ملف شخصي
وإغلاقه. لنفترض أنّ أحد المستخدمين ينقر على عرض الملاحظات، ثم يغلق النافذة المنبثقة مباشرةً
بدلاً من النقر على الزر إخفاء الملاحظات. سيظل المتغيّر notesWindow
يحتوي على إشارة
إلى النافذة المنبثقة التي يمكن الوصول إليها، حتى إذا لم تعُد قيد الاستخدام.
<button id="show">Show Notes</button>
<button id="hide">Hide Notes</button>
<script type="module">
let notesWindow;
document.getElementById('show').onclick = () => {
notesWindow = window.open('/presenter-notes.html');
};
document.getElementById('hide').onclick = () => {
if (notesWindow) notesWindow.close();
};
</script>
هذا مثال على نافذة منفصلة. تم إغلاق النافذة المنبثقة، ولكن يحتوي الرمز البرمجي على مرجع يمنع المتصفّح من إغلاقها واسترداد هذه الذاكرة.
عندما تستدعي صفحة window.open()
لإنشاء نافذة أو علامة تبويب جديدة في المتصفّح، يتم عرض عنصر
Window
يمثّل النافذة أو علامة التبويب. حتى بعد إغلاق هذه النافذة أو تنقّل المستخدم بعيدًا عنها، سيظلّ بإمكانه استخدام عنصر Window
الذي تم إرجاعه من window.open()
للوصول إلى معلومات عنه. هذا هو أحد أنواع النوافذ المنفصلة: لأنّ رمز JavaScript لا يزال بإمكانه الوصول إلى
الخصائص في عنصر Window
المغلق، يجب الاحتفاظ به في الذاكرة. إذا كانت النافذة تتضمّن الكثير من ملفّات div أو مثيل برمجة JavaScript، لا يمكن استرداد هذه الذاكرة إلى أن تصبح مراجع برمجة JavaScript المتبقية غير متعلّقة بخصائص النافذة.
يمكن أن تحدث المشكلة نفسها أيضًا عند استخدام عناصر <iframe>
. تتصرّف إطارات iframe مثل النوافذ المضمّنة
التي تحتوي على مستندات، وتوفّر السمة contentWindow
إمكانية الوصول إلى العنصر Window
المضمّن، تمامًا مثل القيمة التي تعرضها window.open()
. يمكن أن يحتفظ رمز JavaScript بإشارة إلى contentWindow
أو contentDocument
عنصر iframe حتى إذا تمت إزالة عنصر iframe من نموذج DOM أو تغيّر عنوان URL الخاص به، ما يمنع جمع المهملات من المستند لأنّه لا يزال بإمكانه الوصول إلى خصائصه.
في الحالات التي يتم فيها الاحتفاظ بإشارة إلى document
ضمن نافذة أو إطار iframe من JavaScript،
سيتم الاحتفاظ بهذا المستند في الذاكرة حتى إذا انتقلت النافذة أو إطار iframe المُضمّن إلى عنوان URL
جديد. يمكن أن يكون ذلك مزعجًا بشكل خاص عندما لا يرصد JavaScript الذي يحتوي على هذا المرجع
أنّ النافذة أو الإطار قد انتقلا إلى عنوان URL جديد، لأنّه لا يعرف متى يصبح المرجع
المتبقّي الذي يحتفظ بمستند في الذاكرة.
كيفية تسبب النوافذ المنفصلة في تسرب الذاكرة
عند العمل مع النوافذ وإطارات iframe في النطاق نفسه للصفحة الأساسية، من الشائع الاستماع
للأحداث أو الوصول إلى المواقع على مستوى حدود المستند. على سبيل المثال، لنلقِ نظرة على أحد الصيغ
لمثال "عارض العرض التقديمي" من بداية هذا الدليل. يفتح المشاهد نافذة
ثانية لعرض ملاحظات المحاضر. ترصد نافذة "ملاحظات المتحدّث" أحداثclick
كإشارة
للانتقال إلى الشريحة التالية. إذا أغلق المستخدم نافذة الملاحظات هذه، سيظلّ بإمكان JavaScript الذي يتم تشغيله في
النافذة الرئيسية الأصلية الوصول الكامل إلى مستند ملاحظات المتحدّث:
<button id="notes">Show Presenter Notes</button>
<script type="module">
let notesWindow;
function showNotes() {
notesWindow = window.open('/presenter-notes.html');
notesWindow.document.addEventListener('click', nextSlide);
}
document.getElementById('notes').onclick = showNotes;
let slide = 1;
function nextSlide() {
slide += 1;
notesWindow.document.title = `Slide ${slide}`;
}
document.body.onclick = nextSlide;
</script>
لنفترض أنّنا أغلقنا نافذة المتصفّح التي أنشأها showNotes()
أعلاه. لا يتوفّر معالِج أحداث
يستمع إلى رصد إغلاق النافذة، لذا لا يُعلم أيّ رمز بأنّه يجب
محو أيّ إشارات إلى المستند. لا تزال الدالة nextSlide()
"نشطة" لأنّه
تم ربطها كمعالج نقرة في صفحتنا الرئيسية، ولأنّ nextSlide
يحتوي على إشارة إلى
notesWindow
، يعني ذلك أنّه لا يزال يتمّ الإشارة إلى النافذة ولا يمكن جمع المهملات.
هناك عدد من السيناريوهات الأخرى التي يتم فيها الاحتفاظ بالمراجع عن طريق الخطأ، ما يمنع النوافذ المنفصلة من أن تكون مؤهّلة لجمع المهملات:
يمكن تسجيل معالجات الأحداث في المستند الأولي لإطار iframe قبل أن يبدأ الإطار في الانتقال إلى عنوان URL المقصود، ما يؤدي إلى استمرار الإشارات غير المقصودة إلى المستند وإلى إطار iframe بعد إزالة الإشارات الأخرى.
يمكن أن يتم عن طريق الخطأ الاحتفاظ بمستند يستهلك مساحة كبيرة في الذاكرة تم تحميله في نافذة أو إطار iframe بعد الانتقال إلى عنوان URL جديد. ويحدث ذلك غالبًا بسبب احتفاظ الصفحة الرئيسية بالإشارات إلى المستند للسماح بإزالة أداة معالجة الحدث.
عند تمرير عنصر JavaScript إلى نافذة أو إطار iframe آخر، تشمل سلسلة النماذج الأولية للعنصر مراجع إلى البيئة التي تم إنشاؤه فيها، بما في ذلك النافذة التي تم إنشاؤه فيها. وهذا يعني أنّه من المهمّ تجنب الاحتفاظ بإشارات إلى عناصر من نوافذ أخرى، تمامًا كما هو مهم تجنُّب الاحتفاظ بإشارات إلى النوافذ نفسها.
index.html:
<script> let currentFiles; function load(files) { // this retains the popup: currentFiles = files; } window.open('upload.html'); </script>
upload.html:
<input type="file" id="file" /> <script> file.onchange = () => { parent.load(file.files); }; </script>
رصد حالات تسرُّب الذاكرة الناتجة عن النوافذ المنفصلة
قد يكون من الصعب تتبُّع تسرُّب الذاكرة. غالبًا ما يكون من الصعب إنشاء نُسخ منعزلة من هذه المشاكل، لا سيما عندما تكون هناك عدة مستندات أو نوافذ. لزيادة الصعوبة، قد يؤدي فحص الإشارات المحتملة التي تم تسريبها إلى إنشاء إشارات إضافية تمنع جمع المهملات من العناصر التي تم فحصها. لتحقيق ذلك، من المفيد البدء باستخدام أدوات تتجنّب بشكلٍ خاص تقديم هذه الإمكانية.
من الأماكن الجيدة لبدء تصحيح أخطاء الذاكرة هو أخذ لقطة ذاكرة عشوائية. يقدّم ذلك عرضًا في وقت معيّن للذاكرة المستخدَمة حاليًا من قِبل أحد التطبيقات، أي كل العناصر التي تم إنشاؤها ولكن لم يتم جمعها بعد. تحتوي لقطات Heap على معلومات مفيدة عن الكائنات، بما في ذلك حجمها وقائمة بالمتغيّرات والإغلاقات التي تشير إليها.
لتسجيل لقطة ذاكرة عشوائية، انتقِل إلى علامة التبويب الذاكرة في "أدوات مطوّري البرامج في Chrome" واختَر لقطة ذاكرة عشوائية في قائمة أنواع التحليل المتاحة. بعد انتهاء التسجيل، تعرض طريقة العرض الملخّص الكائنات الحالية في الذاكرة، مجمّعة حسب المنشئ.
يمكن أن يكون تحليل عمليات تفريغ الذاكرة من القمامة مهمة شاقّة، وقد يكون من الصعب جدًا العثور على المعلومات الصحيحة كجزء من تصحيح الأخطاء. للمساعدة في ذلك، طوّر مهندسا Chromium، yossik@ وpeledni@، أداة منظف الذاكرة مستقلة يمكنها المساعدة في تمييز node معيّنة، مثل نافذة منفصلة. يؤدي تشغيل أداة "منظف الذاكرة العشوائية" على أحد عمليات التتبُّع إلى إزالة معلومات غير ضرورية أخرى من الرسم البياني للاحتفاظ بالبيانات، ما يجعل عملية التتبُّع أكثر وضوحًا وسهولة في القراءة.
قياس الذاكرة آليًا
توفّر لقطات الذاكرة العشوائية مستوى عالٍ من التفاصيل وهي ممتازة لمعرفة مواضع حدوث عمليات تسرُّب الذاكرة،
ولكنّ أخذ لقطة ذاكرة عشوائية هي عملية يدوية. هناك طريقة أخرى للتحقّق من تسرُّب الذاكرة وهي الحصول على
حجم ذاكرة JavaScript المحجوزة حاليًا من واجهة برمجة التطبيقات performance.memory
API:
لا تقدّم performance.memory
API معلومات إلا عن حجم حِزمة JavaScript، ما يعني
أنّها لا تتضمّن الذاكرة المستخدَمة من قِبل المستند والموارد في النافذة المنبثقة. للحصول على الصورة الكاملة، يجب
استخدام واجهة برمجة التطبيقات الجديدة performance.measureUserAgentSpecificMemory()
API التي يتم تجربتها حاليًا في Chrome.
حلول لتجنّب تسرُّب المياه من النوافذ المنفصلة
إنّ الحالتَين الأكثر شيوعًا اللتين تتسبّب فيهما النوافذ المنفصلة في تسرُّب الذاكرة هما عندما يحتفظ المستند الرئيسي بمراجع لإطار منبثق مغلق أو iframe تمّت إزالته، وعندما يؤدّي التنقّل غير المتوقّع في نافذة أو iframe إلى عدم إلغاء تسجيل معالجات الأحداث مطلقًا.
مثال: إغلاق نافذة منبثقة
في المثال التالي، يتم استخدام زرَّين لفتح نافذة منبثقة وإغلاقها. لكي يعمل الزر إغلاق النافذة المنبثقة، يتم تخزين إشارة إلى النافذة المنبثقة المفتوحة في متغيّر:
<button id="open">Open Popup</button>
<button id="close">Close Popup</button>
<script>
let popup;
open.onclick = () => {
popup = window.open('/login.html');
};
close.onclick = () => {
popup.close();
};
</script>
يبدو للوهلة الأولى أنّ الرمز البرمجي أعلاه يتجنّب المشاكل الشائعة: لا يتم الاحتفاظ بأيّ إشارات إلى مستند النافذة المنبثقة، ولا يتم تسجيل أيّ معالجات أحداث في نافذة النافذة المنبثقة. ومع ذلك، بعد النقر على زر
فتح نافذة منبثقة، يشير المتغيّر popup
الآن إلى النافذة المفتوحة، ويمكن الوصول إلى هذا المتغيّر من نطاق معالِج النقر على الزر إغلاق النافذة المنبثقة. ما لم تتم إعادة تعيين popup
أو إزالة معالِج النقر، يعني المرجع المضمّن لهذا المعالِج إلى popup
أنّه لا يمكن جمعه في "المهملات".
الحلّ: إلغاء ضبط المراجع
تؤدي المتغيّرات التي تشير إلى نافذة أخرى أو مستندها إلى الاحتفاظ بها في الذاكرة. بما أنّ
الكائنات في JavaScript هي دائمًا مراجع، يؤدي منح قيمة جديدة للمتغيّرات إلى إزالة
الإشارة إلى الكائن الأصلي. لـ "إزالة" الإشارات إلى عنصر، يمكننا إعادة تعيين تلك
المتغيّرات إلى القيمة null
.
عند تطبيق ذلك على مثال النافذة المنبثقة السابق، يمكننا تعديل معالج زر الإغلاق لإلغاء مرجعه إلى النافذة المنبثقة:
let popup;
open.onclick = () => {
popup = window.open('/login.html');
};
close.onclick = () => {
popup.close();
popup = null;
};
يساعد ذلك، ولكنّه يكشف عن مشكلة أخرى خاصة بالنوافذ التي تم إنشاؤها باستخدام open()
: ماذا لو أغلَق العميل النافذة بدلاً من النقر على زر الإغلاق المخصّص؟ بالإضافة إلى ذلك، ماذا لو بدأ المستخدم في تصفُّح مواقع إلكترونية أخرى في النافذة التي فتحناها؟ على الرغم من أنّه كان يبدو في البداية أنّه يكفي
إلغاء ضبط مرجع popup
عند النقر على زر الإغلاق، لا يزال هناك تسرُّب للذاكرة عندما
لا يستخدم المستخدمون هذا الزرّ المحدّد لإغلاق النافذة. ويتطلّب حلّ هذه المشكلة رصد هذه الحالات
لإيقاف المراجع العالقة عند حدوثها.
الحلّ: المراقبة والتخلص
في العديد من الحالات، لا تملك JavaScript المسؤولة عن فتح النوافذ أو إنشاء الإطارات
التحكّم الحصري في دورة حياتها. يمكن للمستخدم إغلاق النوافذ المنبثقة، أو يمكن أن يؤدي الانتقال إلى مستند
جديد إلى فصل المستند الذي كان يحتوي عليه إطار أو نافذة في السابق. في كلا الحالتَين، يُطلق المتصفّح حدث pagehide
للإشارة إلى أنّه يتمّ تفريغ المستند.
يمكن استخدام الحدث pagehide
لرصد النوافذ المغلقة والتنقّل بعيدًا عن
المستند الحالي. ومع ذلك، هناك تحذير مهم: تحتوي جميع النوافذ وإطارات iframe التي تم إنشاؤها حديثًا على
مستند فارغ، ثم يتم التنقّل بشكل غير متزامن إلى عنوان URL المحدّد إذا تم تقديمه. ونتيجةً لذلك، يتم تشغيل حدث
pagehide
الأولي بعد وقت قصير من إنشاء النافذة أو الإطار، قبل تحميل مستند
الهدف مباشرةً. بما أنّه يجب تنفيذ رمز تنظيف المرجع عند إزالة تحميل المستند الهدف، علينا تجاهل حدث pagehide
الأول هذا. هناك عدد من الأساليب لتنفيذ ذلك، وأبسطها هو تجاهل أحداث إخفاء الصفحة التي تأتي من عنوان URL
about:blank
للمستند الأولي. في ما يلي كيفية ظهورها في مثال النافذة المنبثقة:
let popup;
open.onclick = () => {
popup = window.open('/login.html');
// listen for the popup being closed/exited:
popup.addEventListener('pagehide', () => {
// ignore initial event fired on "about:blank":
if (!popup.location.host) return;
// remove our reference to the popup window:
popup = null;
});
};
من المهمّ الإشارة إلى أنّ هذه التقنية لا تعمل إلا على النوافذ والإطارات التي لها مصدر فعال مماثل للمصدر الفعال للصفحة الرئيسية التي يتم تشغيل الرمز البرمجي فيها. عند تحميل محتوى من مصدر مختلف، لن يكون حدث location.host
وحدث pagehide
متاحَين لأسباب تتعلق بالأمان. على الرغم من أنّه
من الأفضل بشكل عام تجنُّب الاحتفاظ بمراجع لمصادر أخرى، إلا أنّه في الحالات النادرة التي يكون فيها
هذا مطلوبًا، من الممكن مراقبة المواقع window.closed
أو frame.isConnected
. عندما تتغيّر هذه
السمات للإشارة إلى نافذة مغلقة أو إطار iframe تمت إزالته، من الأفضل إلغاء ضبط أي
إشارات إليه.
let popup = window.open('https://example.com');
let timer = setInterval(() => {
if (popup.closed) {
popup = null;
clearInterval(timer);
}
}, 1000);
الحل: استخدام WeakRef
تمّت مؤخرًا إضافة طريقة جديدة إلى JavaScript للإشارة إلى العناصر التي تتيح جمع القمامة، ويُطلق على هذه الطريقة اسم WeakRef
. إنّ WeakRef
الذي تم إنشاؤه لكائن ما ليس مرجعًا direct
، بل هو كائن منفصل يقدّم طريقة .deref()
خاصة تُعرِض مرجعًا
للكائن ما دام لم يتم جمع المهملات. باستخدام WeakRef
، يمكن
الوصول إلى القيمة الحالية للنافذة أو المستند مع السماح بجمع القمامة. بدلاً من الاحتفاظ بإشارة إلى النافذة التي يجب إلغاء ضبطها يدويًا استجابةً
لأحداث مثل pagehide
أو خصائص مثل window.closed
، يتم الحصول على إذن الوصول إلى النافذة
حسب الحاجة. عند إغلاق النافذة، يمكن جمع المهملات، ما يؤدي إلى بدء عرض القيمة undefined
من خلال طريقة .deref()
.
<button id="open">Open Popup</button>
<button id="close">Close Popup</button>
<script>
let popup;
open.onclick = () => {
popup = new WeakRef(window.open('/login.html'));
};
close.onclick = () => {
const win = popup.deref();
if (win) win.close();
};
</script>
من التفاصيل المثيرة للاهتمام التي يجب أخذها في الاعتبار عند استخدام WeakRef
للوصول إلى النوافذ أو المستندات أنّه عادةً ما يظل المرجع متاحًا لفترة قصيرة بعد إغلاق النافذة أو إزالة الإطار المضمّن. ويعود السبب في ذلك إلى أنّ WeakRef
يستمر في عرض قيمة إلى أن يتم جمع المهملات من العنصر المرتبط، وهو ما يحدث بشكل غير متزامن في JavaScript وبشكل عام أثناء وقت التوقف. لحسن الحظ، عند التحقّق من النوافذ المنفصلة في لوحة الذاكرة في "أدوات مطوّري البرامج في Chrome"، يؤدي أخذ
لقطة ذاكرة عشوائية إلى بدء عملية جمع المهملات وإزالة النافذة التي تتم الإشارة إليها بشكل ضعيف. وبإمكانك
أيضًا التحقّق من أنّه تم التخلص من عنصر تمت الإشارة إليه من خلال WeakRef
من JavaScript،
إما عن طريق رصد الحالات التي يعرض فيها deref()
القيمة undefined
أو باستخدام واجهة برمجة التطبيقات الجديدة
FinalizationRegistry
API:
let popup = new WeakRef(window.open('/login.html'));
// Polling deref():
let timer = setInterval(() => {
if (popup.deref() === undefined) {
console.log('popup was garbage-collected');
clearInterval(timer);
}
}, 20);
// FinalizationRegistry API:
let finalizers = new FinalizationRegistry(() => {
console.log('popup was garbage-collected');
});
finalizers.register(popup.deref());
الحل: التواصل عبر postMessage
إنّ رصد حالات إغلاق النوافذ أو إزالة مستند أثناء التنقّل يمنحنا طريقة لإزالة معالجات الطلبات وإزالة المراجع حتى يمكن جمع المهملات في النوافذ المنفصلة. ومع ذلك، فإنّ هذه التغييرات هي إصلاحات محدّدة لحلّ مشكلة أساسية في بعض الأحيان، وهي الربط المباشر بين الصفحات.
يتوفّر نهج بديل أكثر شمولية يتجنّب الإشارات القديمة بين النوافذ والملفّات: إنشاء فصل من خلال حصر التواصل بين الملفّات على
postMessage()
. بالرجوع إلى مثال ملاحظات المدرّس الأصلي، كانت الدوالّ
مثل nextSlide()
تعدّل نافذة الملاحظات مباشرةً من خلال الإشارة إليها وتعديل
محتواها. بدلاً من ذلك، يمكن للصفحة الأساسية تمرير المعلومات اللازمة إلى نافذة الملاحظات
بشكل غير متزامن وغير مباشر عبر postMessage()
.
let updateNotes;
function showNotes() {
// keep the popup reference in a closure to prevent outside references:
let win = window.open('/presenter-view.html');
win.addEventListener('pagehide', () => {
if (!win || !win.location.host) return; // ignore initial "about:blank"
win = null;
});
// other functions must interact with the popup through this API:
updateNotes = (data) => {
if (!win) return;
win.postMessage(data, location.origin);
};
// listen for messages from the notes window:
addEventListener('message', (event) => {
if (event.source !== win) return;
if (event.data[0] === 'nextSlide') nextSlide();
});
}
let slide = 1;
function nextSlide() {
slide += 1;
// if the popup is open, tell it to update without referencing it:
if (updateNotes) {
updateNotes(['setSlide', slide]);
}
}
document.body.onclick = nextSlide;
على الرغم من أنّ هذا الإجراء لا يزال يتطلّب أن تشير النوافذ إلى بعضها، لا تحتفظ أيّ من النوافذ بإشارة إلى
المستند الحالي من نافذة أخرى. وتشجع استراتيجية تمرير الرسائل أيضًا على استخدام التصميمات التي يتم فيها الاحتفاظ بمراجع
النوافذ في مكان واحد، ما يعني أنّه يجب إلغاء مرجع واحد فقط عند
إغلاق النوافذ أو الانتقال بعيدًا عنها. في المثال أعلاه، يحتفظ العنصر showNotes()
فقط بإشارة إلى
نافذة الملاحظات، ويستخدم الحدث pagehide
لضمان إزالة هذه الإشارة.
الحل: تجنُّب الإشارات التي تستخدم noopener
في الحالات التي يتم فيها فتح نافذة منبثقة لا تحتاج صفحتك إلى التواصل معها أو التحكّم فيها،
قد تتمكّن من تجنُّب الحصول على مرجع إلى النافذة. ويُعدّ ذلك مفيدًا بشكل خاص
عند إنشاء نوافذ أو إطارات iframe ستحمّل محتوى من موقع إلكتروني آخر. في هذه الحالات، يقبل سمة
window.open()
خيار "noopener"
الذي يعمل تمامًا مثل سمة
rel="noopener"
للروابط بتنسيق HTML:
window.open('https://example.com/share', null, 'noopener');
يؤدي الخيار "noopener"
إلى عرض window.open()
على النحو التالي: null
، ما يجعل من المستحيل
تخزين مرجع للنافذة المنبثقة عن طريق الخطأ. ويمنع ذلك أيضًا النافذة المنبثقة من الحصول على
إشارة إلى النافذة الرئيسية، لأنّ سمة window.opener
ستكون null
.
ملاحظات
نأمل أن تساعدك بعض الاقتراحات الواردة في هذه المقالة في العثور على عمليات تسرُّب الذاكرة وإصلاحها. إذا كان لديك أسلوب آخر لتصحيح أخطاء النوافذ المنفصلة أو إذا ساعدت هذه المقالة في رصد عمليات تسرُّب البيانات في تطبيقك، يُرجى إعلامي بذلك. يمكنك التواصل معي على Twitter على @_developit.