שכפול אובייקטים ב-JavaScript

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

נתחיל מהבסיס…

אם ארצה לשכפל אובייקט אוכל לעשות את הדבר הפשוט הבא:

let obj1 = {
    'name': 'John',
    'age': 30
};
let obj2 = obj1;
console.log(obj1, obj2);

והפלט:

ניתן לראות שהאובייקט obj1 הועתק אל האובייקט obj2.
ניתן לראות שהאובייקט obj1 הועתק אל האובייקט obj2.

וכמובן שלאחר העתקה ניתן לשנות את ערך של property מסוים, כך:

let obj1 = {
    'name': 'John',
    'age': 30
};
let obj2 = obj1;
obj1.age = 32;
console.log(obj1);
console.log(obj2);

ובוודאי שזה מה שנקבל בפלט:

ניתן לראות שה-property בשם age שונה עבור שני האובייקטים.
ניתן לראות שה-property בשם age שונה עבור שני האובייקטים.

רגע..מה?
מדוע age השתנה מ-30 ל-32 בשני האובייקטים? פשוט מאוד, כי אובייקטים הם Reference ולא Primitive.
אם זה לא ברור, זה בדיוק הזמן לעצור עם מאמר זה ולקרוא את המאמר שכתבתי בנושא: זהירות, זה מבלבל: ההבדל בין Value ל- Reference ב-JavaScript

יש? בואו נמשיך.

אז מה ניתן לעשות אתם שואלים?
להשתמש ב-Object.assign למשל:

let obj1 = {
    'name': 'John',
    'age': 30
};
let obj2 = Object.assign({}, obj1);
obj1.age = 32;
console.log(obj1);
console.log(obj2);

ועכשיו הפלט נראה כך:

לאחר השימוש ב-Object.assign ה-age השתנה רק עבור obj1.
לאחר השימוש ב-Object.assign ה-age השתנה רק עבור obj1.

ויש אפילו דרך פשוטה יותר…

באמצעות Spread Operator (לא יודעים מה זה Spread Operator? לא נורא, גם על זה כתבתי פוסט: Spread Operator ב-JavaScript) ניתן לעשות את אותה הפעולה בצורה פשוטה יותר:

let obj1 = {
    'name': 'John',
    'age': 30
};
let obj2 = {...obj1};

מה טריקי פה?

אז כן, Object.assign או Spread Operator לא פותרים את הבעיה לגמרי, למה?
בבקשה:

let obj1 = {
    'name': 'John',
    'age': 30,
    'food': ['Pizza', 'Salad', 'Burger']
};
let obj2 = {...obj1};
obj1.food[0] = 'Pasta';
console.log(obj1);
console.log(obj2);

והפלט:

האיבר הראשון המערך food שונה מ-Pizza ל-Pasta עבור שני האובייקטים.
האיבר הראשון המערך food שונה מ-Pizza ל-Pasta עבור שני האובייקטים.

חזרנו אחורה.
הכוונה הייתה לשנות את האיבר הראשון במערך food של האובייקט obj1 בלבד, אז למה זה שוב השתנה בשני האובייקטים?
שוב, כי גם מערכים הם Reference ולא Primitive, ו-food הוא מערך כמובן, והכי חשוב – העובדה שהמערך נמצא בתוך אובייקט לא משנה את זה.

אז מהו הפיתרון?

פשוט מאוד:

let obj1 = {
    'name': 'John',
    'age': 30,
    'food': ['Pizza', 'Salad', 'Burger']
};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.food[0] = 'Pasta';
console.log(obj1);
console.log(obj2);

והפלט:

לאחר השימוש ב-JSON.stringify ו-JSON.parse האובייקט שוכפל ללא קשר ל-References.
לאחר השימוש ב-JSON.stringify ו-JSON.parse האובייקט שוכפל ללא קשר ל-References.

על ידי הפיכת האובייקט obj1 למחרוזת באמצעות JSON.stringify ולאחר מכן להחזיר אותו להיות אובייקט באמצעות JSON.parse גורמת לכך שה-References של המערכים "נאבדו" ובכך האובייקט obj2 הוא שכפול מושלם של obj1.

בהצלחה!

נהנת ממאמר זה? הירשם לרשימת התפוצה וקבל עדכונים על מאמרים חדשים!


רק רגע! :)
כשאני לא כותב פוסטים ב-CodeBrain אני מספק שרותי פיתוח, ייעוץ והדרכה.
אם נראה לך שאני האיש המתאים עבורך, כדאי שנדבר :)

3 תגובות בנושא “שכפול אובייקטים ב-JavaScript”

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

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

  3. לא יעבוד עם אובייקטים שמכילים הצבעה לעצמם כי אי אפשר לעשות stringify.
    שלא לדבר על הביצועים הגרועים פה עם מערכים/אובייקטים גדולים.
    פתרון שעובר על המפתחות של האובייקט/מערך ויורד עד לרמת הprimitive ומעתיק לפי value בצורה רקורסיבית הרבה יותר נכון.
    יש המון חבילות npm שכבר עושות את זה כמו deepClone.
    והlet בהתחלה מיותר כי אתה לא עושה reassign, זה יכול להיות const.
    כשאתה כותב פוסט לימודי, תוודא שאתה יודע את החומר ברמה גבוהה לפני הכתיבה.

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *