JavaScript Prototype

מה זה JavaScript Prototype, למה צריך את זה, איך זה משפיע על הביצועים?

במאמר הקודם דיברנו מעט על closure ב-JavaScript ועל היתרונות שלו (נוחות כתיבה משמעותית) והחסרונות שלו (יכול לפגוע משמעותית בביצועים). בסוף המאמר הבטחתי שאני אדבר על חלופה ל-closure שבמקרים מסוימים יכולה להאיץ את הביצועים ושווה להכיר אותה. החלופה הזו היא שימוש מסיבי ב-prototype במקום ב-closure.

נשאלת השאלה, מה זה prototype? האם זה פריימוורק חדש ומגניב של JavaScript? אלו מכם שמכירים היטב Vanilla JavaScript אמורים להכיר היטב מה זה prototype, אבל לכל השאר, ובמיוחד לפוחחי jQuery, אני אסביר שמדובר במנגנון של JavaScript שמאפשר להוסיף מתודות או תכונות לאובייקטים קיימים. נשמע סתום ולא קשור? בואו ונדגים. אני מניח שיש לכם ידע ולו בסיסי ב-JavaScript מונחה עצמים. אם לא, זה הזמן ללחוץ על הקישור ולקרוא קצת לפני שתמשיכו הלאה.

בואו וניצור אובייקט בסיסי ופשוט, לא ליטרלי בשם Person וניצור אותו עם האופרטור new:


  function Person(name) {
      this.name = name;
  }
var c = new Person('John');

עד כאן הכל פשוט ויפה, יצרנו אובייקט ויצרנו לו אפילו איזה פעולת construct חמודה שרצה בהתחלה ויוצרת תכונת name לאובייקט שלנו, אם נריץ את הקוד הבא, נוכל לקבל את התכונה הזו:


console.log(c.name);

נניח ואני גם רוצה להוסיף מתודה לאובייקט החביב שלי. כפי שלמדנו קודם לכן, אני יכול להוסיף מתודה לאובייקט ובאמצעות closure אני יכול להיות בטוח שלמתודה הזו כל התכונות והמשתנים בקונטקסט יהיו זמינים. בואו וניצור get ו-set לשם:


  var person = function(name) {
          return {
              'get_name': function() {
                  return name;
              },
              'set_name': function(newName) {
                  name = newName;
              }
          };
      };
var c = new person('John');
console.log(c.get_name());

התוצאה בקונסולה תהיה השם שהכנסתי לקונסטרקטור, במקרה הזה John. איך זה עובד? כי ה-closure ידאג שלכל מתודה באובייקט תהיה גישה לכל המשתנים בקונטקסט, בדיוק כמו שלמדנו במאמר הקודם. כל עוד אני יכול לקרוא למתודות האלו מבחוץ (ואני יכול) מנוע הג'אווהסקריפט ישמור את כל המשתנים בקונטקסט בזכרון לכל instance ולא ישחרר אותם לעולם.

אבל יש דרך נוספת – להוסיף מתודות לאובייקט שלי לאחר ההגדרה שלו באמצעות prototype וכך בעצם לאפשר למנוע הג'אווהסקריפט לנהל את הזכרון יותר ביעילות, כיוון שאנו לא משתמשים ב-closure ומנוע הג'אווהסקריפט יכול למחוק את המשתנים המיותרים. איך זה עובד? שימו לב לדוגמה הבאה:


  function Person(name) {
      this.name = name;
  }
  
  Person.prototype.get_name = function() {
      return this.name;
  };
  
  Person.prototype.set_name = function(name) {
      this.name = name;
  };
var c = new Person('John');
console.log(c.get_name());

קל לראות איך להוסיף מתודות באמצעות prototype – במקום להגדיר אותן בתוך האובייקט, אנו מעניקים לאובייקט שלנו את המתודות באמצעות JavaScript Prototype. האם יש לכך השלכה על הביצועים? כן, באופן דרמטי. אל תאמינו לי! שימו לב למבחן הרלוונטי שיש ב-jspref בנוגע ל-closure מול prototype:

jQuery vs prototype
jQuery vs prototype

זה אמור לשכנע אתכם. בטח תשאלו את השאלה – איך בדיוק אפשר להשתמש בזה ב-jQuery? התשובה היא שלמרבה הצער אי אפשר, jQuery מסתמכת באופן רציני ביותר על closure ופחות על prototype, אבל אם יש לכם בעית ביצועים רצינית בסקריפט, או הרבה אובייקטים, אתם יכולים לצאת קצת מאיזור הנוחות ולהתחיל לעבוד ב-vanilla JavaScript. כך למשל, במקום להשתמש באירוע click של jQuery בדוגמה במאמר הקודם ששומר את כל המשתנים באמצעות closure, אנחנו יכולים להשתמש ב-prototype. אני אדגים באופן גס ביותר.

הנה הקוד המקורי שאותו הבאתי במאמר הקודם, הקוד הזה משתמש ב-closure, על פניו הוא טוב:


// When the DOM loads, initialized the user interface.
$(

function() {
    // Gather all the links in the document.
    var jLinks = $("a");
    // Loop over each link.
    jLinks.each(

    function(intLinkIndex, objLink) {
        // Get a jQuery reference to this link.
        var jThis = $(this);
        // Bind the click handler.
        jThis.click(

        function(objEvent) {
            // Alert the link index in the context of the
            // greater document.
            alert("I an link " + (intLinkIndex + 1) + " of " + jLinks.length);
            // Prevent default link behavior.
            return (false);
        });
    });
});

​

אגב, למי שמעוניין, מקור הקוד נמצא גם ב-jsFiddle.

יש כמה דברים טובים לומר על הקוד הזה – הוא קריא, הוא נחמד, קל לשנות אותו וקל להבין מה מתרחש בו. אבל בואו ונניח שיש לי בעית ביצועים קשה בדף שבו הוא נמצא. אם אני הבנתי את עניין ה-closure, ואני יודע לעבוד עם אובייקטים ועם prototype, אני יכול לייעל את הקוד באופן הבא:


//creating jThis object blue print


function jThis(_this) {

}

//Creating methods with protypes for handling two properties
jThis.prototype.getIndex = function() {
    return this.index;
}
jThis.prototype.setIndex = function(index) {
    this.index = index;
}
jThis.prototype.getLength = function() {
    return this.len;
}
jThis.prototype.setLength = function(len) {
    this.len = len;
}

// When the DOM loads, initialized the user interface.
$(

function() {
    // Gather all the links in the document.
    var jLinks = $("a");
    // Loop over each link.
    jLinks.each(

    function(intLinkIndex, objLink) {
        // Get a jQuery reference to this link.

        // Bind the click handler.
        var _jThis = new jThis(this);
        _jThis.setIndex(intLinkIndex);
        _jThis.setLength(jLinks.length);

        this.onclick =

        function(objEvent) {
            // Alert the link index in the context of the
            // greater document.
            alert("I an link " + (_jThis.getIndex() + 1) + " of " + _jThis.getLength());
            // Prevent default link behavior.
            return (false);
        }
    });
});​

לא הלכתי כאן ממש עד הסוף עם הייעול כמובן, אבל בשביל הדוגמה זה יהיה מצוין. למרות שהשארתי את ה-closure, כן שיניתי את הפונקציה האחרונה ובמקום להשתמש ב-jQuery הבזבזני וה-closure שלו, יצרתי, בתחילת הקוד, אובייקט משלי עם שתי תכונות שנשלטות כל אחת על ידי set ו-get בצורה מסודרת וכך חסכתי closure אחד ואיפשרתי ל-garbage collector לעבוד בצורה יעילה יותר. והתוצאה? אתם יכולים לראות באמצעות המבחן הרלוונטי שיצרתי באמצעות jsperf שמעמיד את שני הקודים למבחן.

prototype יותר יעיל
prototype יותר יעיל

אתם מוזמנים כמובן להכנס ל-jsFiddle ולעשות פורק לקוד 'המשופר' שלי ולנסות לשפר אותו עוד יותר, יש שם המון מה לעשות כמובן והדוגמה שהבאתי היא דוגמה פשוטה לשם לימוד והמחשה בלבד. אם אתם מעוניינים להתנסות, זו דרך נהדרת.

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

כמובן שעם כל הכבוד ליעילות, עדיף שלא לרוץ ולנסות ליעל סקריפטים באופן כזה, הקוד החדש שיצרתי הוא הרבה פחות קריא ומובן ומתכנתים חדשים יתקשו להכנס אליו אלא אם כן הם ממש יודעים מה הם עושים. אחד היתרונות הגדולים ב-jQuery היא הקריאות הרבה שלה שמקילה על התחזוקה. אם אין לכם בעית ביצועים, אני ממליץ להניח לסקריפט לנפשו. טוב להכיר את prototype ואת closures והמחיר שלהן, אבל הידע הזה מעולה לשימושים אחרים, ולאו דווקא לסבך דברים פשוטים שלא לצורך.

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

DALL·E 2024-09-06 12.34.24 - A visually engaging post about Python and cryptographic problems. The image should feature a dark-themed background with a glowing, futuristic Python
פתרונות ומאמרים על פיתוח אינטרנט

בעיות במימוש של פונקציות קריפטוגרפיות בפייתון

היום (16 לספטמבר) ממש, אני מעביר הרצאה ב-PyconIL 2024 על בעיות קריפטוגרפיות באפליקציות פייתון. לצערי אי אפשר להכניס את כל הבעיות הקריפטוגרפיות להרצאה אחת או

רספברי פיי

הרצת גו על רספברי פיי

עולם הרספברי פיי והמייקרים ניתן לתפעול בכל שפה – לא רק פייתון או C – כאן אני מסביר על גו

ספריות ומודולים

מציאת PII באמצעות למידת מכונה

כך תגנו על משתמשים שלכם שמעלים מידע אישי רגיש כמו תעודות זהות באמצעות שירות אמאזוני.

DALL·E 2023-10-21 22.28.58 - Photo of a computer server room with red warning lights flashing, indicating a potential cyber threat. Multiple screens display graphs showing a sudde
יסודות בתכנות

מבוא לאבטחת מידע: IDOR

הסבר על התקפה אהובה ומוצלחת שבאמצעותה שואבים מידע מאתרים

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