לג'אווהסקריפט יש מודל ירושה מעניין שנקרא ירושת Prototype. זה אומר שלכל אוביקט במערכת יש את ה Prototype שלו (ולפרוטוטייפ יכול גם להיות פרוטוטייפ משלו) וכך נוצרת שרשרת ארוכה כמה שנרצה. בכל פעם שאנחנו מנסים לגשת לשדה מסוים באוביקט באופן אוטומטי ג'אווהסקריפט מחפש את השדה הזה בכל שרשרת הפרוטוטייפים ומחזיר את הערך הראשון שהוא מוצא.
בכל פעם שאנחנו יוצרים class ב JavaScript באופן אוטומטי נוצר פרוטוטייפ ומקושר לכל אוביקט חדש שיווצר מהקלאס הזה.
בכל פעם שמחלקה יורשת ממחלקה אחרת תיווצר שרשרת של שני פרוטוטייפים לכל אוביקט שנוצר מהמחלקה היורשת (וכך הלאה גם עם ירושה יותר ארוכה).
הפונקציה Object.create
מאפשרת להגדיר אוביקט חדש עם פרוטוטייפ מסוים שאנחנו מעבירים לה בתור פרמטר.
והפונקציה Object.getPrototypeOf
מחזירה את הפרוטוטייפ של אוביקט מסוים.
בואו נראה את זה בפעולה עם קצת קוד. קודם כל מחלקה בודדת:
class A {
hello() {
console.log('hello world');
}
}
const a = new A();
const prototypeOfA = Object.getPrototypeOf(a);
// This works
a.hello();
delete(prototypeOfA.hello);
// But this doesn't
a.hello();
אפשר לראות שכשאני מוחק את המאפיין hello מהפרוטוטייפ של האוביקט, אני כבר לא יכול להפעיל את הפונקציה hello של האוביקט.
לעומת זאת אם אני מנסה למחוק את הפונקציה hello מהאוביקט a עצמו אין שום בעיה להמשיך לקרוא לה (כי ממילא היא לא היתה שמורה על a):
class A {
hello() {
console.log('hello world');
}
}
const a = new A();
const prototypeOfA = Object.getPrototypeOf(a);
// This works
a.hello();
delete(a.hello);
// And this works too
a.hello();
אפשר לעשות משחקים יותר מתוחכמים עם Object.create:
const a = { one: '1' };
const b = Object.create(a);
const c = Object.create(b);
// All 3 work:
console.log(a.one);
console.log(b.one);
console.log(c.one);
b.two = '2';
// undefined
console.log(a.two);
// these two work:
console.log(b.two);
console.log(c.two);
c.three = '3';
// undefined
console.log(a.three);
console.log(b.three);
// but this works
console.log(c.three);
כל פעם שאני מוסיף או מוריד מאפיין מאוביקט, המאפיין הזה "זמין" גם בכל האוביקטים שנמצאים במורד שרשרת הפרוטוטייפים.
עכשיו לשאלה - איך לקבל רשימה של שמות כל הפונקציות מאוביקט JavaScript?
נו אז בהתחשב בכל מה שכתבנו על פרוטוטייפים, אתם כבר בטח מבינים שהשאלה הזאת קצת יותר מתוחכמת מהניסוח הראשוני שלה. רשימת כל הפונקציות שיש על האוביקט כוללת גם את כל הפונקציות מהפרוטוטייפ שלו, ואת אלה מהפרוטוטייפ של הפרוטוטייפ וכך הלאה.
למזלנו ב JavaScript יש פונקציה בשם Object.getOwnPropertyNames שמחזירה את רשימת כל המאפיינים של אוביקט מסוים. שילוב שלה עם Object.getPrototypeOf
יפתור לנו את הבעיה עם לולאה פשוטה:
function getAllMethodNames(obj) {
let res = [];
let p = obj;
while (p !== Object.getPrototypeOf({})) {
const ownKeys = Object.getOwnPropertyNames(p);
const ownMethods = ownKeys.filter(n => n !== 'constructor').filter(n => typeof p[n] === 'function');
res = [...res, ...ownMethods];
p = Object.getPrototypeOf(p);
}
return res;
}
וכך אני יכול להשתמש בפונקציה כדי לקבל את שמות כל הפונקציות במחלקה מסוימת:
class A {
hello() {
console.log('Hello world');
}
}
class B extends A {
byebye() {
console.log('bye');
}
}
const a = new A();
const b = new B();
console.log(getAllMethodNames(a));
console.log(getAllMethodNames(b));
הקוד ידפיס תחילה את hello, בגלל שזו הפונקציה היחידה במחלקה A, ואז בשורה חדשה את hello ו byebye שהן שתי הפונקציות שאפשר להפעיל על האוביקט b מהמחלקה B.