בר שינוי ולא בר שינוי (Mutable ו-Immutable) בג'אווהסקריפט

בר שינוי ולא בר שינוי (Mutable ו-Immutable) בג'אווהסקריפט

כתבה זו מסבירה את המושגים של בר שינוי (Mutable) ולא בר שינוי (Immutable) בג'אווהסקריפט.

YouTube Video

בר שינוי ולא בר שינוי (Mutable ו-Immutable) בג'אווהסקריפט

מהו בר שינוי?

בר שינוי (Mutable) פירושו שערך ניתן לשינוי. אובייקטים ומערכים, שהם טיפוסי הפניות (Reference Types), הם דוגמאות אופייניות למבני נתונים ברי שינוי.

דוגמה לאובייקט בר שינוי

1let person = { name: "Alice", age: 25 };
2person.age = 26;
3console.log(person); // { name: "Alice", age: 26 }

בקוד זה, תכונת ה-age של אובייקט ה-person משתנה מ-25 ל-26. מכיוון שאובייקטים מועברים על ידי הפניה (Reference), תוכן הכתובת בזיכרון השמור במשתנה person משתנה.

דוגמה למערך בר שינוי

1let numbers = [1, 2, 3];
2numbers.push(4);
3console.log(numbers); // [1, 2, 3, 4]

בקוד זה נעשה שימוש במתודת push כדי להוסיף את האלמנט 4 למערך המקורי. פעולה זו משנה את המערך המקורי, מה שהופך אותה לפעולה ברת שינוי.

דוגמה בתוך פונקציה

 1// Function to append a value to an array
 2function append(arr, value) {
 3    arr.push(value); // Modify the original array
 4    console.log(arr);
 5}
 6
 7let numbers = [1, 2, 3];
 8append(numbers, 4);
 9
10console.log(numbers); // [1, 2, 3, 4] (original array is modified)

כשמבצעים פעולות ברות שינוי בתוך פונקציה, גם המערך המקורי משתנה.

מהו לא בר שינוי?

לא בר שינוי (Immutable) פירושו שערך אינו ניתן לשינוי. טיפוסים פרימיטיביים הם מבחינה בסיסית לא ברי שינוי.

דוגמה לטיפוס פרימיטיבי לא בר שינוי

1let str = "hello";
2str[0] = "H";
3console.log(str); // "hello"

ניסיון לשנות את התו הראשון של המחרוזת str ל-H נכשל כיוון שמחרוזות הן לא ברות שינוי.

דוגמה בתוך פונקציה

 1// Function to increment a number
 2function increment(num) {
 3    num++; // This modifies only the local copy of num
 4    console.log(num);
 5}
 6
 7let number = 10;
 8increment(number);
 9
10console.log(number); // 10 (original number remains unchanged)

כיוון שמספרים הם לא ברי שינוי, פעולות בתוך פונקציה אינן משפיעות על המשתנה המקורי.

ביצוע פעולות לא ברות שינוי על מערכים

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

1// Create an array of numbers
2let numbers = [1, 2, 3];
3
4// Create a new array by spreading the original and adding a new element
5let newNumbers = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)

כאן, תחביר הפיזור (...) משמש ליצירת מערך חדש newNumbers. מכיוון שמערך ה-numbers המקורי אינו משתנה, זו פעולה בלתי ניתנת לשינוי.

יתרונות השימוש במבני נתונים בלתי ניתנים לשינוי

תחזיות משופרות

מכיוון שבלתי ניתן לשנות נתונים בלתי ניתנים לשינוי, שינויים בלתי צפויים פחות סבירים, מה שמפחית את הסיכון לבעיות.

תאימות עם ספריות המבוססות על אי-שינוי

ספריות כגון React ו-Redux לרוב מעוצבות מראש עם נתונים בלתי ניתנים לשינוי, מה שמאפשר ניהול מצבים בצורה קלה יותר כשמשתמשים בהן כראוי.

תהליך הפיכת אובייקטים לבלתי ניתנים לשינוי באמצעות Object.freeze

ניתן להשתמש ב-Object.freeze כדי למנוע שינויים באובייקט.

1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }

עם זאת, Object.freeze מבצע קיפאון רדוד, כלומר המאפיינים של אובייקטים משולבים נשארים ניתנים לשינוי.

1// Create a frozen object with a nested object
2const user = Object.freeze({ profile: { name: "Bob" } });
3
4// Attempt to modify a nested property (this works because Object.freeze() is shallow)
5user.profile.name = "Charlie";
6
7console.log(user.profile.name); // "Charlie" (nested object is still mutable)

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

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze(obj) {
 3  Object.keys(obj).forEach(key => {
 4    if (typeof obj[key] === "object" && obj[key] !== null) {
 5      deepFreeze(obj[key]); // Recursively freeze nested objects
 6    }
 7  });
 8  return Object.freeze(obj); // Freeze the top-level object
 9}
10
11// Create a deeply frozen object
12const user = deepFreeze({ profile: { name: "Bob" } });
13
14// Attempt to modify a nested property (ignored)
15user.profile.name = "Charlie";
16
17console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

סיכום

  • נתונים ניתנים לשינוי יכולים להשתנות, כולל אובייקטים ומערכים.
  • נתונים בלתי ניתנים לשינוי אינם יכולים להשתנות, כולל סוגים פרימיטיביים כגון מחרוזות ומספרים.
  • שימוש בתחביר הפיזור או map מאפשר פעולות נתונים בלתי ניתנות לשינוי..
  • ניתן להשתמש ב-Object.freeze וב-deepFreeze כדי למנוע שינויים באובייקטים.
  • שימוש בנתונים בלתי ניתנים לשינוי מאפשר קוד צפוי יותר ופחות רגיש לטעויות.

תכנון בלתי ניתן לשינוי משפר את בטיחות הקריאה של הקוד ואת הבהירות, אז השתמשו בו בחוכמה!

תוכלו לעקוב אחר המאמר שלמעלה באמצעות Visual Studio Code בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.

YouTube Video