למה מערך ריק שווה 0?

08/11/2022

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

1. קודם העובדות:

זה לקוח מ node.js גירסה 18, גם דפדפנים מתנהגים ככה:

> Number([])
0
> Number([4])
4
> Number([2, 3, 4])
NaN

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

2. אבל למה זה קורה?

בשביל להפוך אוביקטים לפרימיטיביים ג'אווהסקריפט משתמש בשלוש טכניקות (לפי הסדר הזה):

  1. קודם כל הוא בודק אם לאוביקט מוגדר הסימבול Symbol.toPrimitive, אם כן יפעיל אותו.

  2. אם אין toPrimitive מנסים להפעיל את valueOf.

  3. אם גם זה לא עזר מפעילים את toString.

למערכים אין toPrimitive וה valueOf שלהם מחזיר את המערך עצמו, אז נשארנו רק עם toString:

> [].toString()
''
> [4].toString()
'4'
> [2, 3, 4].toString()
'2,3,4'

המחרוזת הראשונה ריקה ולכן הופכת לאפס, השניה הופכת למספר 4 ואת השלישית כבר אי אפשר להפוך למספר ולכן הופכת ל NaN.

3. רגע תזכיר לי מה זה Symbol.toPrimitive

אוקיי אז הבנו למה מערכים מתנהגים ככה אבל מה זה בעצם Symbol.toPrimitive והאם אפשר להשתמש בו כדי לשנות את ההתנהגות?

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

> const x = {
    [Symbol.toPrimitive](hint) { return 7 }
};
undefined

> Number(x)
7

המשתנה hint שעובר לפונקציה אומר לה מה סוג הפרימיטיב שאנחנו מצפים לקבל, ובהמרה למספר יש לו את הערך number.

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

> Array.prototype[Symbol.toPrimitive] = () => 42;
[Function (anonymous)]

> Number([2, 3, 5])
42

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

> Array.prototype[Symbol.toPrimitive] = 
function() {
    return this.length;
}

> Number([2, 3, 5])
3