אתגר ריפקטורינג ב JavaScript

13/11/2021

יניב היה צריך פונקציה שמחזירה קוד HTML לאלמנט קישור לפי מספר מאפיינים. הפונקציה מקבלת אוביקט בשם props ותומכת במאפיינים הבאים:

  1. אם האוביקט מכיל את המאפיין href אז ל a יהיה href עם הערך המתאים. אחרת לא יהיה לו.

  2. אם האוביקט מכיל את האלמנט title אז ל a יהיה title עם הערך המתאים. אחרת לא יהיה לו.

  3. אם האוביקט מכיל את האלמנט text אז ל a יהיה טקסט עם הערך שעבר.

זאת הפונקציה שיניב כתב:

function showLink(props) {
  return (
    `<a
      ${props.href ? `href="${props.href}"` : ''}
      ${props.title ? `title="${props.title}"` : ''}
      >${props.text ? props.text : ''}
    </a>`
  );
}

כשדנה הסתכלה על הקוד היא הרגישה שהפונקציה מסורבלת מדי. היא הציעה לשכתב אותה בעזרת שתי פונקציות עזר לקוד הבא:

function showLinkBetter(props) {
  const { href, title } = attrs(props);
  const { text } = values(props);

  return (
    `<a ${href} ${title}>${text}</a>`
  );
}

"ויותר מזה", התלהבה דנה, "עם פונקציות העזר שלי אתה יכול לתמוך במאפיינים חדשים בצורה קלה בלי לשנות את הפונקציות attrs ו values שכתבתי - רק תוך שינוי הפונקציה showLinkBeter".

איך נראה המימוש של attrs ו values שדנה כתבה?

תוכן עניינים

  1. פיתרון

1. פיתרון

הבעיה שדנה היתה צריכה להתמודד איתה היא לא טריוויאלית: יש לשנות את כל הערכים באוביקט, כולל כאלה שבכלל לא נמצאים בו! האוביקט ש attrs מחזירה צריך לדעת להחזיר מחרוזת ריקה עבור כל מפתח שיבקשו ממנו ושלא נמצא באוביקט ה props.

דרך אחת לגשת לפתור את זה היא לדחות את החישוב של ערך ההחזר עד שבאמת מישהו מבקש מפתח כלשהו. ב JavaScript אנחנו יכולים לעשות את זה עם הגדרת Getter (אבל אז נצטרך לדעת איך קוראים למפתח), או עם הגדרת Proxy.

אני לא יודע מה דנה כתבה, אבל זה הפיתרון שאני הייתי כותב בעזרת proxy כדי לממש את attrs ו values:

function returnTextProxy(props, withPrefix) {
  return new Proxy(props, {
    get: function(target, prop, _receiver) {
      if (target[prop] !== undefined) {
        return withPrefix ? `${prop}="${target[prop]}"` : target[prop];
      } else {
        return "";
      }
    }
  });
}

function attrs(props) {
  return returnTextProxy(props, true);
}

function values(props) {
  return returnTextProxy(props, false);
}

יש לכם רעיונות אחרים? אל תתביישו ושתפו בתגובות.