ECMAScript 6 proxy

איך שולטים באובייקט אחד על ידי אובייקט אחר.

אחד הפיצ'רים המעניינים ב-ECMAScript 6 הוא פרוקסי. פרוקסי הוא פיצ'ר שפחות מתייחסים אליו בהרבה מדריכים של ES6 כי התמיכה בו היתה עד לא מזמן קלושה למדי, אפילו עם ב-TypeScript\Babel וגם כי הוא קשור למושג האפלולי-משהו Metaprogramming שמשמעותו היא תוכנות שמשתמשות בתוכנות אחרות כקלט שלהן.

טוב, מספיק עם הפחדות. פרוקסי ב-JavaScript הוא לא מאוד מסובך. בגדול מדובר בדרך לעטוף אובייקט A באובייקט B וכך לשלוט או לנטר דברים שמשתנים בו. כלומר אני יכול לשלוט לחלוטין על אובייקט ב-JavaScript, התכונות והמתודות שלו באמצעות הפרוקסי.

יש לנו שלושה מונחים שאנחנו צריכים לזכור:

1. target – המטרה. זה האובייקט שאותו אנחנו עוטפים.
2. hanlder – אובייקט שמכיל את הפעולות שאנחנו רוצים לתפוס במטרה – למשל set, get, apply.
3. trap -זו הפעולה. אני אגדיר get trap אם אני רוצה לתפוס את get למשל.

יותר מדי תיאוריה בלי קוד זה לא טוב לבריאות. הנה דוגמת ה-hello world:


var data = {  
  name: 'Billy Bob',
  age: 15
};

var handler = {  
  get(target, key, proxy) { //This is the trap
    const today = new Date();
    console.log(`GET request made for ${key} at ${today}.`);
  }
}

var data = new Proxy(data, handler);

// This will execute our handler, log the request, and set the value of the `name` variable.
var name = data.name;

מה הולך פה? אני מגדיר אובייקט חביב שקוראים לו data. זה האובייקט שאני הולך 'לעטוף'. מייד אחר כך אני יוצא handler שיש בו trap. ה-handler הוא אובייקט פשוט שמכיל את כל ה-traps. במקרה הזה יש לי רק אחד שהוא ה-get. כלומר, כל פעם שמישהו יקרא לאיזושהי תכונה באובייקט המקורי, פונקצית ה-get ב-handler תופעל.

הקשירה עצמה של הפרוקסי נעשית בשורה
var data = new Proxy(data, handler);

בשורה האחרונה אני מבצע סתם קריאה לאחת התכונות. מי שיריץ את זה בדפדפן שלו, יראה שה-get יופעל.

בוא נדגים את זה עם trap אחר. למשל set שמופעל בכל פעם שמישהו משנה את תכונות האובייקט שלי.


var object = {};

var handler = {  
  set(target, key, value, receiver) { //This is the trap
    console.log(`SET request made for ${key}, value ${value}`);
  }
}

var object = new Proxy(object, handler);

object.someProp = 'Moshe'; //"SET request made for someProp, value Moshe"

כאן אני משתמש ב-trap אחר ולוכד את ה-set. כלומר כל פעם שמישהו ינסה לשנות תכונה כלשהי באובייקט שלי. כל תכונה שהיא – הפרוקסי שלי ידע מזה.

יש לנו לא מעט traps. ראינו עכשיו את get ו-set. כאשר לכל אחד מהם יש ארגומנטים אחרים. יש גם למשל construct, שמופעל בכל פעם שיש לנו new. למשל:


var myFunc = function() {};

var handler = {  
  construct(target, argumentsList, newTarget) { //This is the trap
    console.log('activated!')
    console.log(argumentsList)
  }
}

var myFunc = new Proxy(myFunc, handler);
var whatever = new myFunc('arg1', 'arg2'); //"activated!", ["arg1", "arg2"]

רשימה נאה של כל ה-traps אפשר למצוא כרגיל ב-MDN.

עכשיו נשאלת השאלה מה אפשר לעשות עם זה. הו הו הו. השימוש הראשון הוא ללוגים/דיבאגינג שקט בלי להפריע לאף אחד. אם יש לכם משהו שאתם רוצים לעשות לו לוגינג, עם פרוקסי זה ממש קל. גם בדיקות/ולידציות למיניהן הן ממש פשוטות עם פרוקסי.
מה שלא כדאי לעשות עם פרוקסי זה לנסות לעשות private ל-class. למרות שפרוקסי יכול בהחלט לדמות (סוג של) כימוס בג'אווהסקריפט, זו לא המטרה שלו והייתי נמנע מזה.

פוסטים נוספים שכדאי לקרוא

תמונה מצוירת של רובוט שמנקה HTML
יסודות בתכנות

סניטציה – למה זה חשוב

הסבר על טכניקה פשוטה וידועה מאד שאנו מפעילים על מידע לפני שאנחנו מציגים אותו ב-HTML באפליקציה או באתר.

תמונת תצוגה של מנעול על מחשב
פתרונות ומאמרים על פיתוח אינטרנט

הגנה מפני XSS עם Trusted Types

תכונה ב-CSP שמאפשרת מניעה כמעט הרמטית להתקפות XSS שכל מפתח ווב צריך להכיר וכדאי שיכיר.

גלילה לראש העמוד