אינטרנט ישראל
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
  • ראשי
  • אודות רן בר-זיק ואינטרנט ישראל
  • ערוץ טלגרם
  • מסטודון
  • התחברו אלי בטוויטר
  • התחברו אלי בלינקדאין
  • ספר ג'אווהסקריפט
ראשי » פיתוח אינטרנט » פתרונות ומאמרים על פיתוח אינטרנט » העולם המופלא של this ב-JavaScript

העולם המופלא של this ב-JavaScript

רן בר-זיק ספטמבר 11, 2016 7:07 am 2 תגובות

הסבר מקיף על this ועל איך קובעים קונטקסט של פונקציה או מתודה

כדאי תמיד להשאר מעודכנים! אם יש לכם טלגרם, בדקו את ערוץ הטלגרם של האתר שבו אני מעדכן על פוסטים חדשים 🙂 אם אתם רוצים ללמוד תכנות באופן מקיף ומסודר, הצטרפו לאלפי הלומדים בפרויקט "ללמוד ג'אווהסקריפט בעברית" שמלמד לתכנת בג'אווהסקריפט, ב-Node.js ובריאקט וגם מלמד על תרומה לקוד פתוח. גם ספרים דיגיטליים וגם ספרים מודפסים. בשיתוף הקריה האקדמית אונו ובתמיכת חברות מובילות כגון Wix, Outbrain, Elementor, Iron Source, Chegg, Really Good ועוד.

במאמר הזה אני אצלול קצת לעומק ואנתח את this בג'אווהסקריפט ואיך הוא מתנהג ואיך אפשר לשלוט עליו.

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


var person = {
  firstName: 'Moshe',
  lastName: 'Cohen',
  giveMeName: function () {

    console.log(person.firstName + " " + person.lastName); //Or person
    console.log(this.firstName + " " + this.lastName); //We can use this

  }
}

אני מגדיר אובייקט person שיש לו שתי תכונות, שם ושם משפחה. מתודה משתמשת בתכונות האלו. אנחנו יכולים לקחת את שם האובייקט נקודה ואז התכונה או להשתמש ב-this, זה אותו הדבר. ה-this הוא הקונטקסט שבו הבלוק של הקוד, במקרה הזה אובייקט חי בו.
כדי להמחיש את הקונטקסט, אני אציג גם פונקציה וגם אובייקט:


this.firstName = 'Sarah';
this.lastName = 'Levi';

function giveMeName() {
  console.log(this.firstName + " " + this.lastName); 
}

var person = {
  firstName: 'Moshe',
  lastName: 'Cohen',
  giveMeName: function () {
    console.log(this.firstName + " " + this.lastName); 
  }
}

person.giveMeName();//Moshe Cohen
giveMeName(); //Sarah Levi

שימו לב שבמקרה של הפונקציה, הקונטקסט הוא גלובלי (או יותר נכון של אובייקט window שהוא האובייקט הגלובלי בג'אווהסקריפט). ה-this נקבל את הערכים של 'שרה' ו'לוי'. במקרה של האובייקט, הקונטקסט הוא של האובייקט בלבד.

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


var person = {
  firstName: 'Moshe',
  lastName: 'Cohen',
  giveMeName: function () {
    console.log(this.firstName + " " + this.lastName); 
  }
}

var otherObject = {
  myMethod: function(callback) {
    callback();
  }
}

otherObject.myMethod(person.giveMeName);

כאן אפשר לראות שהפונקציה myMethod שנמצאת בתוך otherObject מקבלת callback והיא מפעילה אותו. זה משהו מאוד טריוויאלי ב-JavaScript ואנחנו עושים אותו כל הזמן. במקרה הזה myMethod לא עושה דבר, אבל היא יכולה לעשות המון דברים: קריאה ל-API בשרת אחר, פעולות על קבצים, קריאה למסד נתונים – יו ניים איט. אבל מה שחשוב הוא שהקולבק ירוץ בסוף הפעולה.
מה הבעיה? מה לפי דעתכם יודפס בקונסולה של הדפדפן? הייתם מצפים ש'משה כהן', נכון? אז זהו! שלא! מה שנקבל זה undefined undefined. למה? אמרתי שה-this קשור לקונטקסט. בג'אווהסקריפט הקונטקסט משתנה בהתאם למי שהפעיל את המתודה. כיוון ש-otherObject הופעל מהקונטקסט הגלובלי, אז ה-this יכוון לקונטקסט הגלובלי – שם אין לנו lastName ו-firstName ואז אנחנו נקבל undefined. אם היו שם – היינו מקבלים את הערכים שלהם! שימו לב לקוד הזה:


var person = {
  firstName: 'Moshe',
  lastName: 'Cohen',
  giveMeName: function () {
    console.log(this.firstName + " " + this.lastName); 
  }
}

var firstName = 'Zeev';
var lastName = 'Revah';

var otherObject = {
  myMethod: function(callback) {
    callback();
  }
}

otherObject.myMethod(person.giveMeName); //Zeev Revah

מה שיודפס זה זאב רווח ולא משהו אחר. זה נראה פשוט כי מדובר פה ב-hello world ובקוד מאוד פשוט. אבל ההתבלבלות בקונטקסט יכולה להטריף מתכנתי ג'אווהסקריפט לא מנוסים (גם מנוסים). נשאלת השאלה איך שולטים בקונטקסט? איך אני יכול למשל לדאוג שאם אני מעביר מתודה מסוימת, היא תקבל את הקונטקסט של האובייקט המקורי ולא של האובייקט שהפעיל אותה?

הכירו את bind שמאפשרת לי לקבוע את הקונטקסט של כל פונקציה/מתודה שאני מפעיל מחוץ לפונקציה. איך אני עושה את זה? בקלות:


var person = {
  
  firstName: 'Moshe',
  lastName: 'Cohen',
  giveMeName: function () {
    console.log(this.firstName + " " + this.lastName); 
  }
}

var firstName = 'Zeev';
var lastName = 'Revah';

var otherObject = {
  myMethod: function(callback) {
    callback();
  }
}

otherObject.myMethod(person.giveMeName.bind(person)); //Moshe Cohen

מה שחשוב פה הוא ה-bind שמופיע לקראת הסוף, אני מעביר דרכו את הקונטקסט. נחמד וברור, לא? bind מאפשר לי לקבוע את ה-this לכל פונקציה.

ה-bind יפעיל את ה-this ברגע שהפונקציה תיקרא. אז הוא מאוד שימושי אם אני הולך לקרוא למתודה יותר מאוחר, כמו למשל בקולבקים או אירועים. יש עוד דרך לקבוע את הקונטקסט וזה באמצעות call או apply שמופעלות ברגע הקריאה למתודה. נשמע מסובך? ממש לא!


var person = {
  
  firstName: 'Moshe',
  lastName: 'Cohen',
  giveMeName: function () {
    console.log(this.firstName + " " + this.lastName); 
  }
}

var firstName = 'Zeev';
var lastName = 'Revah';

var otherObject = {
  myMethod: function(callback) {
    callback.apply(person);
  }
}

otherObject.myMethod(person.giveMeName); //Moshe Cohen

מה ההבדל בין call ל-apply? בדרך שבה אנחנו מעבירים ארגומנטים נוספים. במקרה שלנו ל-giveMeName אין ארגומנטים, אבל אם היו הייתי יכול להעביר אותם ככה:


function add(a, b){ 
  return a + b; 
} 
assert( add.call(this, 1, 2) == 3, ".call() takes individual arguments" ); 
assert( add.apply(this, [1, 2]) == 3, ".apply() takes an array of arguments" );

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


var person = {
  
  firstName: 'Moshe',
  lastName: 'Cohen',
  moods: ['happy', 'angry', 'sad'],
  giveMeName: function () {
    this.moods.forEach(function(mood) {
      console.log(this.firstName + " " + this.lastName + ' Can be ' + mood); 
    });
  }
}

person.giveMeName();

//"undefined undefined Can be happy"
//"undefined undefined Can be angry"
//"undefined undefined Can be sad"

אם נריץ את זה, אנחנו נקבל undefined undefined Can be happy, undefined undefined Can be angry… למה? למה מה שיש בקוד ה-foreach המסכן הזה לא יודע מה הוא ה-this? הקונטקסט הוא אותו קונטקסט, לא? התשובה היא לא. בתוך ה-foreach, אם תשימו לב, יש פונקציה אנונימית שמקבלת this משלה.
מה הפתרון? להשתמש ב-closure כדי להעביר לפונקציה הפנימית את משתנה ה-this שאנחנו רוצים להעביר. למי שלא יודע, closure זה המנגנון ב-JavaScript שמאפשר לנו שיתוף משתנים בין פונקצית האב לפונקצית הבן. במקרה הזה, אני אכניס את ה-this למשתנה אחר, שהוא this_ אבל אפשר לקרוא לו בכל שם שרוצים (הרבה קוראים לזה that). הנה הדוגמה המיוחלת:


var person = {
  
  firstName: 'Moshe',
  lastName: 'Cohen',
  moods: ['happy', 'angry', 'sad'],
  giveMeName: function () {
    var _this = this;
    this.moods.forEach(function(mood) {
      console.log(_this.firstName + " " + _this.lastName + ' Can be ' + mood); 
    });
  }
}

person.giveMeName();

//"Moshe Cohen Can be happy"
//"Moshe Cohen Can be angry"
//"Moshe Cohen Can be sad"

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

כדאי תמיד להשאר מעודכנים! אם יש לכם טלגרם, בדקו את ערוץ הטלגרם של האתר שבו אני מעדכן על פוסטים חדשים 🙂 אם אתם רוצים ללמוד תכנות באופן מקיף ומסודר, הצטרפו לאלפי הלומדים בפרויקט "ללמוד ג'אווהסקריפט בעברית" שמלמד לתכנת בג'אווהסקריפט, ב-Node.js ובריאקט וגם מלמד על תרומה לקוד פתוח. גם ספרים דיגיטליים וגם ספרים מודפסים. בשיתוף הקריה האקדמית אונו ובתמיכת חברות מובילות כגון Wix, Outbrain, Elementor, Iron Source, Chegg, Really Good ועוד.
JavaScript

2 תגובות

  1. Ronen Meiri הגב ספטמבר 12, 2016 בשעה 4:39 pm

    Hey Ran ,
    Thanks for the post.

    It is worth mentioning that ES6 has figured that out already.
    using newly arrow functions will bind _this to `this` automatically.

    Arrow functions have two additional minor differences from regular functions: a. they can’t be used as object constructors b. the special arguments variable isn’t available in arrow functions.

  2. Michael הגב ספטמבר 13, 2016 בשעה 4:14 pm

    בדוגמה אחרונה אפשר אפשר להמנה מלהישתמש ב-_this בשתי דרכים: בפונקציית חץ או בארגומנט נוסף של forEach: https://gist.github.com/michacom/25c238a05ae5e08df11cbeeb7ac59a66

השארת תגובה

ביטול

ללמוד ג'אווהסקריפט בעברית

ללמוד לתכנת ג'אווהסקריפט בעברית שגייס יותר משלוש מאות אלף שקל ולמעלה מ-2000 תומכים - בואו ללמוד עכשיו איך לתכנת.

רשימת הנושאים
  • מדריכים
    • ריאקט
    • טייפסקריפט
    • ECMAScript 6
    • ES20XX
    • Node.js
    • Express
    • רספברי פיי
    • Babel
    • docker
    • MongoDB
    • Git
    • לימוד MySQL
    • SASS
    • jQuery
    • CSS3
    • HTML 5
    • SVN
    • LESS
  • פיתוח אינטרנט
    • פתרונות ומאמרים על פיתוח אינטרנט
    • jQuery Scripts
    • jQuery למתקדמים
    • יסודות בתכנות
    • נגישות אינטרנט
  • חדשות אינטרנט
  • מידע כללי על אינטרנט
    • רשת האינטרנט
    • בניית אתרי אינטרנט
  • rss logo

    לכל המאמרים

    לכל המאמרים שפורסמו באינטרנט ישראל משנת 2008 ועד עכשיו.
  • rss logo

    RSS Feed

    משתמשים בקורא RSS? אם כן, עקבו אחרי אינטרנט ישראל באמצעות פיד ה-RSS!
    מה זה RSS?
  • Twitter logo

    עקבו אחרי בטוויטר

    בחשבון הטוויטר שלי אני מפרסם עדכונים מהירים על חדשות בתחום התכנות והיזמות, התרעות על מצבי חירום ורכילות בוערת על תחום הווב.
    מה זה טוויטר?
  • facebook like image

    ערוץ הטלגרם של אינטרנט ישראל

    בערוץ הטלגרם של אינטרנט ישראל אני מפרסם את הפוסטים של באתר וכן עדכונים טכנולוגיים נוספים.
    מה זה טלגרם?
  • github logo

    הפרויקטים שלי בגיטהאב

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

כל הזכויות שמורות לרן בר-זיק ולאינטרנט ישראל | מדיניות הפרטיות של אתר אינטרנט ישראל | אתר אינטרנט ישראל נגיש לפי תקן WCAG 2.0 AA | הצהרת הנגישות של האתר | אבטחת מידע ודיווח על בעיית אבטחת מידע

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