لماذا أصابت التخمة كِلا الهامسترين؟
لدينا هامسترين، واحد سريع speedy وآخر كسول lazy، والاثنين يرثان كائن الهامستر العمومي hamster.
حين نُعطي أحدهما الطعام، نجد الآخر أُتخم أيضًا. لماذا ذلك؟ كيف نُصلح المشكلة؟
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = {
__proto__: hamster
};
let lazy = {
__proto__: hamster
};
// وجد هذا الهامستر الطعامَ قبل الآخر
speedy.eat("apple");
alert( speedy.stomach ); // apple
// هذا أيضًا وجده. لماذا؟ أصلِح الشيفرة.
alert( lazy.stomach ); // apple
لنرى ما يحدث داخل الاستدعاء speedy.eat("apple") بدقّة.
-
نجد التابِع
speedy.eatفي كائن النموذج الأولي الهامستر (=hamster)، وبعدها ننفّذه بقيمةthis=speedy(الكائن قبل النقطة). -
بعدها تأتي مهمة البحث للتابِع
this.stomach.push()ليجد خاصية المعدةstomachويستدعي عليهاpush. يبدأ البحث عنstomachفيthis(أي فيspeedy)، ولكنّه لا يجد شيئًا. -
بعدها يتبع سلسلة الوراثة ويجد المعدة
stomachفيhamster. -
ثمّ يستدعي
pushعليها ويذهب الطعام في معدة النموذج الأولي.
بهذا تتشارك الهامسترات كلها معدةً واحدة!
أكان lazy.stomach.push(...) أم speedy.stomach.push()، لا نجد خاصية المعدة stomach إلّا في كائن النموذج الأولي (إذ ليست موجودة في الكائن نفسه)، بذلك ندفع البيانات الجديدة إلى كائن النموذج الأولي.
لاحظ كيف أنّ هذا لا يحدث لو استعملنا طريقة الإسناد البسيط this.stomach=:
let hamster = {
stomach: [],
eat(food) {
// نُسند إلى this.stomach بدلًا من this.stomach.push
this.stomach = [food];
}
};
let speedy = {
__proto__: hamster
};
let lazy = {
__proto__: hamster
};
// وجد الهامستر السريع الطعام
speedy.eat("apple");
alert( speedy.stomach ); // apple
// معدة ذاك الكسول فارغة
alert( lazy.stomach ); // <nothing>
الآن يعمل كلّ شيء كما يجب، إذ لا تبحث عملية الإسناد this.stomach= عن خاصية stomach، بل تكتبها مباشرةً في كائن الهامستر الّذي وجد الطعام (المستدعى قبل النقطة).
ويمكننا تجنّب هذه المشكلة من الأساس بتخصيص معدة لكلّ هامستر (كما الطبيعي):
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = {
__proto__: hamster,
stomach: []
};
let lazy = {
__proto__: hamster,
stomach: []
};
// وجد الهامستر السريع الطعام
speedy.eat("apple");
alert( speedy.stomach ); // apple
// معدة ذاك الكسول فارغة
alert( lazy.stomach ); // <nothing>
يكون الحلّ العام هو أن تُكتب الخاصيات كلّها الّتي تصف حالة الكائن المحدّد ذاته (مثل stomach أعلاه) – أن تُكتب في الكائن ذاته، وبهذا نتجنّب مشاكل تشارك المعدة.