שימו לב: פלוס שווה ב JavaScript לא באמת מוסיף

02/09/2023

הקוד הבא מבלבל:

let x = "hello, "
x += "world"
console.log(x);

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

קל להיווכח בזה באמצעות הוספת משתנה:

> x = "hello, "
'hello, '
> y = x
'hello, '
> x += "world"
'hello, world'
> x
'hello, world'
> y
'hello, '

אם ה += היה משנה את המחרוזת אז היינו רואים את השינוי גם ב y, בדיוק כמו שקורה בעבודה על מערכים:

> x = [1, 2, 3]
[ 1, 2, 3 ]
> y = x
[ 1, 2, 3 ]
> x.push(4)
4
> x
[ 1, 2, 3, 4 ]
> y
[ 1, 2, 3, 4 ]

ולמי אכפת אם += דורס את הערך הקיים במשתנה ומחליף אותו בערך חדש גדול יותר או מוסיף למשהו קיים? זה כבר תלוי במחרוזת. במחרוזת רגילה זה באמת לא משנה, אבל כשהמחרוזת מייצגת אלמנטים ב DOM, שינוי שלה עם += ידרוס את האלמנטים הקיימים ויחליף אותם באלמנטים חדשים. אם היו על האלמנטים הישנים קודי טיפול באירועים הם כבר לא יעבדו (כי האלמנטים "הישנים" כבר לא על המסמך).

דוגמה? תמיד. הקוד הבא מייצר כפתור שבלחיצה עליו יקפיץ הודעת alert:

const main  = document.querySelector('main');
main.innerHTML += '<button>button 1</button>';
main.querySelector('button').addEventListener('click', () => {
  alert('1');
})

ועכשיו אני לוקח את אותו קוד ומוסיף לו שורה:

const main  = document.querySelector('main');
main.innerHTML += '<button>button 1</button>';
main.querySelector('button').addEventListener('click', () => {
  alert('1');
});

main.innerHTML += '<p>Click for magic</p>';

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