בר שינוי ולא בר שינוי (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 בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.