זהירות, זה מבלבל: ההבדל בין Value ל- Reference ב-JavaScript
כל מפתח JavaScript ברמה די בסיסית אמור לשלוט בהבדלים בין Value ל- Reference. בפועל, מדובר באחד מהנושאים הכי מבלבלים ויותר מכך, כזה שעלול לגרום להמון באגים בקוד שאמור להיות די פשוט.
את הקלות שבה ניתן להתבלבל אסביר באמצעות 3 דוגמאות מגוונות. שנתחיל?
דוגמה מספר 1:
let firstObj = { items : 150 };
let secondObj = firstObj;
firstObj.items = 170;
console.log(secondObj.items);
מה הפלט שיתקבל בקונסול? 150 או 170? יש לשים לב שהאובייקט firstObj הוא זה שעודכן ובקונסול מתבצעת בדיקה של האובייקט secondObj.
וודאי שיתקבל 170.
דוגמה מספר 2:
let firstObj = {items: 10};
let secondObj = {items: 10};
if(firstObj === secondObj) console.log('Equal');
else console.log('Not Equal');
מה יתקבל בקונסול? שני אובייקטים זהים לכאורה.
וודאי שיתקבל Not Equal.
דוגמה מספר 3:
function increaseItems(obj) {
obj.items += 10;
}
let firstObj = { items : 60 };
console.log(firstObj);
increaseItems(firstObj);
console.log(firstObj);
מה יתקבל בקונסול הראשון ומה יתקבל בשני?
בראשון {items: 60} ובשני {items: 70}.
עד לכאן דוגמאות.
אם לא הופתעת, לא אשעמם אותך, תמשיך לקרוא את הפוסט רק אם אין לך משהו טוב יותר לעשות וכבר קראת את שאר הפוסטים שלי על JavaScript 🙂
הופתעת? אל דאגה, עד סוף המאמר הכל יהיה ברור יותר 🙂
Value Type & Reference Type
ב-JavaScript יש לנו types רבים: Object, Number, Sring, Array, Boolean, null, Function, undefined.
רשימה זו מחולקת לשתי קבוצות: קבוצת ה-Value וקבוצת ה-Reference וההבדל ביניהן הוא משמעותי.
קבוצת ה-Value
בקבוצה זו נמנים ה-types הבאים: Number, Sring, Boolean, null, undefined.
קבוצה זו היא מסוג value מכיוון שכאשר מבצעים השמה של נתון מסוג זה, הערך עובר הלאה.
אסביר זאת באמצעות דוגמא פשוטה:
let var1 = 15;
let var2 = var1;
var1 = 20;
המשתנה בשם var1 מקבל את הערך 15.
בשורה השנייה, בוצעה השמה של הערך למשתנה חדש בשם var2.
לאחר מכן, בשורה השלישית, הערך של המשתנה var1 שונה ל-20.
האם הערך במשתנה var2 שונה גם כן או שהוא נשאר 15? בוודאי שהוא נשאר 15, מדוע? כי Number הוא מקבוצת ה-value והערך הוא מה שמועבר בין המשתנים.
ניתן לראות שבדוגמה זו אין "הפתעות", הסיבה היא שקבוצת ה-value מתנהגת באופן "הגיוני" יותר.
קבוצת ה-Reference
בקבוצה זו נמנים 3 סוגים: Object, Array ו-Function.
קבוצה זו היא מסוג Reference מאחר והכתובת בזיכרון היא זו שעוברת כאשר מבצעים השמה.
לאחר שההבדל בין הקבוצות הובהר, אנסה להסביר את 3 הדוגמאות מתחילת המאמר:
הדוגמה הראשונה:
let firstObj = { items : 150 };
let secondObj = firstObj;
firstObj.items = 170;
בשורה הראשונה נוצר אובייקט בשם firstObj. ה-key בשם items קיבל את הערך 150.
בשורה השנייה בוצעה השמה של האובייקט firstObj לתוך האובייקט secondObj.
בשורה השלישית בוצע עדכון של הערך items מ-150 ל-170 באובייקט firstObj.
מאחר ואובייקט הוא מסוג Reference, השינוי בוצע בשני האובייקטים.
אז מהי הכוונה ל-Reference?
Reference הוא הפנייה לכתובת בזיכרון ומאחר שבוצעה השמה של האובייקט firstObj לתוך האובייקט secondObj, שניהם מכוונים לאותה כתובת בזיכרון ולכן כאשר הערך משתנה, שני האובייקטים מפנים לערך המעודכן, במקרה זה 170.
הכנתי איור שימחיש איך זה קורה 🙂

אמשיך עם הדוגמה השנייה:
let firstObj = {items: 10};
let secondObj = {items: 10};
if(firstObj === secondObj) console.log('Equal');
else console.log('Not Equal');
מדוע יתקבל Not Equal? מכיוון שמדובר בשני אובייקטים שונים והם מצביעים על כתובת שונה בזיכרון, זה שתוכנם זהה לחלוטין כלל לא משנה.
אגב, באיזה מקרה כן יתקבל Equal?
במקרה הבא:
let firstObj = {items: 10};
let secondObj = firstObj;
if(firstObj === secondObj) console.log('Equal');
else console.log('Not Equal');
בשורה 2 בוצעה השמה של האובייקט firstObj לתוך האובייקט secondObj ולכן הם מצביעים על אותה הכתובת בזיכרון ובעת ההשוואה נקבל Equal.
אסיים עם הדוגמה השלישית:
function increaseItems(obj) {
obj.items += 10;
}
let firstObj = { items : 60 };
console.log(firstObj);
increaseItems(firstObj);
console.log(firstObj);
אמנם בשורה מספר 8 הערך של האובייקט firstObj שונה מ-60 ל-70 בתוך פונקציה אבל יש לשים לב שלפונקציה הועבר אובייקט כארגומנט ולא מחרוזת ולכן הערך "נשמר" גם לאחר מכן.
בהצלחה!
נהנת ממאמר זה? הירשם לרשימת התפוצה וקבל עדכונים על מאמרים חדשים!
רק רגע! :)
כשאני לא כותב פוסטים ב-CodeBrain אני מספק שרותי פיתוח, ייעוץ והדרכה.
אם נראה לך שאני האיש המתאים עבורך, כדאי שנדבר :)
שים לב לטעות בדוגמא השלישית אתה קורא לפונקציה בשם שונה מזה שהגדרת.
כבר תוקן, תודה!
תודה. עד היום זה היה לי נטו אינטואיציה.
הסבר יפה וממצה. נהניתי לקרוא.
בדוגמא השלישית האובייקט הראשון מכיל את הערך 60 שים לב אומנם העברה של האובייקט לפונקצייה משנה את הערך ולכן לאחר מכן האובייקט משתנה לא בשני המקרים..
בדרך כלל console.log יציג את הערך הסופי ולזו הייתה הכוונה.
השורה נמחקה על מנת למנוע בלבול 🙂
הסבר טוב, עכשיו הבנתי לעומק את ההבדלים. תודה 🙂
אבל לא לגמרי הבנתי את הדוגמא השלישית – למעלה כתבת שבקונסול הראשון נקבל 60 ובשני 70, ובסוף המאמר כתבת שבשני הקונסולים קיבלנו 70?
היי פנינה,
בדומה לתשובה שנתתי לנועם –
בעת הרצת הקוד המלא בדפדפן, בדרך כלל ייפלט אל הקונסול הערך הסופי.
השורה נמחקה על מנת למנוע בלבול.