אובייקט ה-reflect הוא אחת מהתוספות החשובות והמשמעותיות ל-ES6 ולא רבים שמים אליו לב וחבל. בגדול, מדובר בדרך משמעותית וטובה יותר לקבל מידע על אובייקט.
אני חושב שאני אתחיל עם דוגמה ואז אני אחפור מעט יותר:
Object.getOwnPropertyDescriptor(1, 'foo')
getOwnPropertyDescriptor הוא מתודה ידועה שבה מקבלים מידע על תכונה מסוימת של אובייקט. במקרה הזה, העברתי 1 בתור אובייקט. 1 הוא לא ממש אובייקט והייתי מצפה לשגיאה מסוימת. אבל במקרה של object יש לנו undefined. לעומת זאת, אם אני אשתמש ב-reflect, אני אקבל שגיאה יותר נורמלית:
Reflect.getOwnPropertyDescriptor(1, 'foo')
אפשר לראות ששם המתודה של Reflect הוא זהה לזה של Object. בעצם, ה-Object מתיישן ו-Reflect מחליף אותו בתור ספק הגישה העיקרי לפעולות של אובייקטים, אבל לא רק. הנה דוגמה נאה ל-apply:
var ages = [11, 33, 12, 54, 18, 96];
// Function.prototype style:
var youngest = Math.min.apply(Math, ages);
var oldest = Math.max.apply(Math, ages);
כאן אני מוצא את המינימום והמקסימום באמצעות apply, אבל כאמור Reflect אמורה להכיל את כל עניין המטא-תכנות – כלומר פעולות על קטעי תוכנה אחרים (כמו פונקציות apply למשל). אם אני רוצה להשתמש ב-Reflect, אני יכול להשתמש ב-apply באופן מאוד אינטואיטיבי:
var ages = [11, 33, 12, 54, 18, 96];
// Reflect style:
var youngest = Reflect.apply(Math.min, Math, ages);
var oldest = Reflect.apply(Math.max, Math, ages);
לא משהו מפיל, אבל בהחלט משהו יעיל. הרציונל מאחורי ה-Reflect הוא כאמור לרכז את כל הפעולות שאנו עושים על תוכנה או חלק מתוכנה כמו apply, get, set, call וחבריהם במקום אחד וכן למנוע אפשרות תיאורטית שמישהו שינה את call או את get או כל פעולה אחרת ב-prototype וחירב לכם את העסק.
הנה דוגמה נוספת במשהו קצת יותר מוחשי: סינגלטון. לפני עידן ה-Reflect, ככה זה נראה:
class Greeting {
constructor(name) {
this.name = name;
}
greet() {
return `Hello ${name}`;
}
}
// ES5 style factory:
function greetingFactory(name) {
var instance = Object.create(Greeting.prototype);
Greeting.call(instance, name);
return instance;
}
יש לנו כאן קלאס, שעליו למדנו במאמר הקודם ופונקציה שאיתו אני יוצא את הקלאס. עם ES5 אני משתמש ב-object.create לעומת זאת, אני יכול להשתמש ב-Reflect.construct:
class Greeting {
constructor(name) {
this.name = name;
}
greet() {
return `Hello ${name}`;
}
}
// ES6 style factory
function greetingFactory(name) {
return Reflect.construct(Greeting, [name], Greeting);
}
אחד הדברים החזקים הוא שב-Reflect יש גם אפשרות למחוק תכונה, דבר שעד כה הייתי צריך לעשות עם המילה השמורה delete:
//ES5
var myObj = { foo: 'bar' };
delete myObj.foo;
assert(myObj.hasOwnProperty('foo') === false);
//ES6
myObj = { foo: 'bar' };
Reflect.deleteProperty(myObj, 'foo');
assert(myObj.hasOwnProperty('foo') === false);
כאמור, לא משהו שיפיל אתכם מהכיסא. אבל סוף סוף לכל הפעולות 'מטא תכנות' – כלומר כאלו שהאינפוט שלהן הוא אובייקט/פונקציה יש בית אחד שהוא Reflect. רשימה מלאה של תכונות ה-Reflect אפשר למצוא ב-MDN.
Reflect אמור לשחק יפה מאוד עם proxy שעליו כבר דיברנו. למען האמת הפלט של Reflect מאוד דומה לפלט של ה-trap המתאים של proxy. אם אתם משתמשים ב-proxy, סביר להניח שתצטרכו להחזיר Reflect כי זה יהיה לכם יותר קל. הנה דוגמה של מימוש proxy ו-reflect:
var handler = {
get () {
return Reflect.get(...arguments)
}
}
var target = { a: '111' }
var proxy = new Proxy(target, handler);
console.log(proxy.a); //111
במקרה הזה ה-trap הוא על get ואני מחזיר את מה שאני מקבל באמצעות Reflect.get שהוא זהה ל-trap.