2.9 דסטרקצ׳רינג וספרד הרצאה
דסטרקצ'רינג וספרד - Destructuring and Spread¶
דסטרקצ'רינג הוא תחביר מיוחד שמאפשר "לפרק" מערכים ואובייקטים לתוך משתנים בודדים.
זה נשמע מסובך, אבל בפועל זה עושה את הקוד הרבה יותר קריא ונקי.
דסטרקצ'רינג הוא פיצ'ר שתשתמשו בו כל יום - במיוחד ב-React.
דסטרקצ'רינג של מערכים - Array Destructuring¶
הבסיס¶
במקום לגשת לאלמנטים לפי אינדקס, אפשר לפרק את המערך ישירות למשתנים:
const colors = ["red", "green", "blue"];
// without destructuring
const first = colors[0];
const second = colors[1];
const third = colors[2];
// with destructuring
const [red, green, blue] = colors;
console.log(red); // "red"
console.log(green); // "green"
console.log(blue); // "blue"
בפייתון זה מקביל ל-tuple unpacking:
דילוג על אלמנטים¶
const numbers = [1, 2, 3, 4, 5];
// skip the second element
const [first, , third] = numbers;
console.log(first); // 1
console.log(third); // 3
// skip first two
const [, , thirdNum] = numbers;
console.log(thirdNum); // 3
ערכי ברירת מחדל - Default Values¶
const arr = [1];
const [a, b] = arr;
console.log(a); // 1
console.log(b); // undefined
// with defaults
const [x = 0, y = 0, z = 0] = arr;
console.log(x); // 1
console.log(y); // 0
console.log(z); // 0
החלפת ערכי משתנים - Swapping¶
טריק נחמד שמאפשר להחליף ערכים בלי משתנה זמני:
let a = 1;
let b = 2;
// without destructuring
// let temp = a;
// a = b;
// b = temp;
// with destructuring - one line!
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1
בפייתון: a, b = b, a - אותו עיקרון בדיוק.
דסטרקצ'רינג של אובייקטים - Object Destructuring¶
הבסיס¶
const person = { name: "Alice", age: 25, city: "Tel Aviv" };
// without destructuring
const name = person.name;
const age = person.age;
// with destructuring
const { name, age, city } = person;
console.log(name); // "Alice"
console.log(age); // 25
console.log(city); // "Tel Aviv"
שימו לב להבדל ממערכים: במערכים חשוב הסדר, באובייקטים חשוב השם. אפשר לפרק בכל סדר:
שינוי שם - Alias¶
לפעמים רוצים לתת למשתנה שם אחר מהמפתח באובייקט:
const person = { name: "Alice", age: 25 };
const { name: fullName, age: years } = person;
console.log(fullName); // "Alice"
console.log(years); // 25
// console.log(name); // Error - name is not defined!
שימושי כשיש התנגשות שמות:
ערכי ברירת מחדל¶
const person = { name: "Alice" };
const { name, age = 0, city = "Unknown" } = person;
console.log(name); // "Alice"
console.log(age); // 0 (default)
console.log(city); // "Unknown" (default)
שילוב alias עם ברירת מחדל¶
const { name: fullName = "Anonymous", age: years = 0 } = {};
console.log(fullName); // "Anonymous"
console.log(years); // 0
דסטרקצ'רינג מקונן - Nested Destructuring¶
const user = {
name: "Alice",
address: {
street: "Herzl 10",
city: "Tel Aviv",
country: "Israel"
}
};
// nested destructuring
const { name, address: { city, country } } = user;
console.log(name); // "Alice"
console.log(city); // "Tel Aviv"
console.log(country); // "Israel"
// console.log(address); // Error! address is not defined as a variable
אם רוצים גם את address כמשתנה וגם לפרק אותו:
const { name, address, address: { city } } = user;
console.log(address); // { street: "Herzl 10", city: "Tel Aviv", country: "Israel" }
console.log(city); // "Tel Aviv"
דסטרקצ'רינג בפרמטרים של פונקציות¶
זה אחד השימושים הכי חשובים, במיוחד ב-React.
בלי דסטרקצ'רינג¶
עם דסטרקצ'רינג¶
function greet({ name, age }) {
console.log(`Hello ${name}, you are ${age} years old`);
}
greet({ name: "Alice", age: 25 }); // "Hello Alice, you are 25 years old"
עם ברירות מחדל¶
function createUser({ name, age = 18, role = "user" } = {}) {
return { name, age, role };
}
createUser({ name: "Alice" }); // { name: "Alice", age: 18, role: "user" }
createUser({ name: "Bob", age: 30 }); // { name: "Bob", age: 30, role: "user" }
createUser(); // { name: undefined, age: 18, role: "user" }
ה-= {} בסוף מבטיח שהפונקציה לא תקרוס אם קוראים לה בלי ארגומנט.
דסטרקצ'רינג עם מערכים בפרמטרים¶
function getFirstAndLast([first, ...rest]) {
const last = rest[rest.length - 1];
return { first, last };
}
console.log(getFirstAndLast([1, 2, 3, 4, 5]));
// { first: 1, last: 5 }
ב-React תשתמשו בדסטרקצ'רינג של props כל הזמן:
// in React:
// function UserCard({ name, age, avatar }) {
// return <div>{name} ({age})</div>;
// }
אופרטור Rest - שאר האלמנטים¶
אופרטור ה-rest (...) אוסף את "השאר" לתוך משתנה אחד.
Rest במערכים¶
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
Rest באובייקטים¶
const person = { name: "Alice", age: 25, city: "Tel Aviv", email: "alice@test.com" };
const { name, ...rest } = person;
console.log(name); // "Alice"
console.log(rest); // { age: 25, city: "Tel Aviv", email: "alice@test.com" }
שימושי מאוד כשרוצים להפריד מאפיינים מסוימים מהשאר:
// remove a property from an object (without mutating)
const { email, ...personWithoutEmail } = person;
console.log(personWithoutEmail); // { name: "Alice", age: 25, city: "Tel Aviv" }
Rest בפרמטרים של פונקציות¶
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
בפייתון זה מקביל ל:
// Python equivalent:
// first, second, *rest = [1, 2, 3, 4, 5]
//
// def sum(*numbers):
// return sum(numbers)
//
// name, **rest = not possible in Python for dicts
// (Python uses **kwargs for function parameters)
אופרטור Spread - פיזור¶
אופרטור ה-spread (...) הוא ההפך מ-rest - הוא "מפזר" אלמנטים.
Spread עם מערכים¶
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// combine arrays
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// add elements at specific positions
const withMiddle = [...arr1, 99, ...arr2];
console.log(withMiddle); // [1, 2, 3, 99, 4, 5, 6]
// copy an array
const copy = [...arr1];
copy.push(4);
console.log(arr1); // [1, 2, 3] - original unchanged
console.log(copy); // [1, 2, 3, 4]
Spread עם אובייקטים¶
const person = { name: "Alice", age: 25 };
const contact = { email: "alice@test.com", phone: "050-1234567" };
// merge objects
const fullPerson = { ...person, ...contact };
console.log(fullPerson);
// { name: "Alice", age: 25, email: "alice@test.com", phone: "050-1234567" }
// override properties
const updated = { ...person, age: 26 };
console.log(updated); // { name: "Alice", age: 26 }
console.log(person); // { name: "Alice", age: 25 } - original unchanged
Spread כארגומנטים לפונקציה¶
const numbers = [5, 2, 8, 1, 9];
// Math.max expects individual arguments, not an array
console.log(Math.max(...numbers)); // 9
console.log(Math.min(...numbers)); // 1
// equivalent to:
console.log(Math.max(5, 2, 8, 1, 9)); // 9
בפייתון:
// Python equivalent:
// combined = [*arr1, *arr2]
// full_person = {**person, **contact}
// max(*numbers)
Rest מול Spread - מה ההבדל?¶
אותו סינטקס (...), אבל שימוש שונה:
// SPREAD - spreading elements OUT (right side / argument)
const copy = [...original]; // spreading into new array
const merged = { ...obj1, ...obj2 }; // spreading into new object
Math.max(...numbers); // spreading as function arguments
// REST - collecting elements IN (left side / parameter)
const [first, ...rest] = arr; // collecting remaining into array
const { name, ...others } = obj; // collecting remaining into object
function sum(...nums) {} // collecting arguments into array
כלל אצבע:
- בצד ימין (ערך) = spread (פיזור)
- בצד שמאל (הגדרה) = rest (איסוף)
העתקה רדודה מול העתקה עמוקה - Shallow vs Deep Copy¶
העתקה רדודה - Shallow Copy¶
Spread ו-Object.assign יוצרים העתקה רדודה - הם מעתיקים רק את הרמה הראשונה:
const original = {
name: "Alice",
address: { city: "Tel Aviv", street: "Herzl 10" }
};
// shallow copy
const copy = { ...original };
copy.name = "Bob"; // doesn't affect original
copy.address.city = "Haifa"; // DOES affect original!
console.log(original.name); // "Alice" - OK
console.log(original.address.city); // "Haifa" - changed!
למה? כי ב-shallow copy, אובייקטים מקוננים לא מועתקים - רק ההפניה שלהם מועתקת. שני האובייקטים מצביעים על אותו אובייקט פנימי.
// same problem with arrays
const matrix = [[1, 2], [3, 4]];
const matrixCopy = [...matrix];
matrixCopy[0][0] = 99;
console.log(matrix[0][0]); // 99 - changed!
העתקה עמוקה - Deep Copy¶
const original = {
name: "Alice",
address: { city: "Tel Aviv", street: "Herzl 10" },
hobbies: ["reading", "coding"]
};
// modern way - structuredClone (recommended)
const deepCopy = structuredClone(original);
deepCopy.address.city = "Haifa";
deepCopy.hobbies.push("swimming");
console.log(original.address.city); // "Tel Aviv" - not affected!
console.log(original.hobbies); // ["reading", "coding"] - not affected!
// old trick - JSON parse/stringify
const deepCopy2 = JSON.parse(JSON.stringify(original));
JSON.parse(JSON.stringify(...)) עובד ברוב המקרים, אבל יש לו מגבלות:
- לא עובד עם פונקציות
- לא עובד עם Date objects (ממיר למחרוזת)
- לא עובד עם undefined (מוחק את המאפיין)
- לא עובד עם Map, Set, RegExp
structuredClone הוא הפתרון המודרני והמומלץ. הוא עובד עם רוב הטיפוסים (אבל לא עם פונקציות).
בפייתון:
// Python equivalent:
// import copy
// shallow = copy.copy(original) -> { ...original }
// deep = copy.deepcopy(original) -> structuredClone(original)
דוגמאות מעשיות¶
חילוץ ערכים מתשובת API¶
const apiResponse = {
status: 200,
data: {
user: { id: 1, name: "Alice", email: "alice@test.com" },
token: "abc123"
},
timestamp: "2024-01-01"
};
const { data: { user: { name, email }, token } } = apiResponse;
console.log(name); // "Alice"
console.log(email); // "alice@test.com"
console.log(token); // "abc123"
עדכון אובייקט בצורה immutable¶
const state = {
user: { name: "Alice", age: 25 },
settings: { theme: "dark", language: "he" },
count: 0
};
// update just the count (immutable)
const newState = { ...state, count: state.count + 1 };
// update nested object (immutable)
const newState2 = {
...state,
user: { ...state.user, age: 26 }
};
// the original is unchanged
console.log(state.count); // 0
console.log(state.user.age); // 25
זה הדפוס המרכזי לעדכון state ב-React.
הסרת מאפיין מאובייקט¶
const user = { name: "Alice", password: "secret", email: "alice@test.com" };
// remove password without mutating
const { password, ...safeUser } = user;
console.log(safeUser); // { name: "Alice", email: "alice@test.com" }
מיזוג עם ברירות מחדל¶
function createConfig(userConfig) {
const defaults = {
host: "localhost",
port: 3000,
debug: false,
timeout: 5000
};
return { ...defaults, ...userConfig };
}
const config = createConfig({ port: 8080, debug: true });
console.log(config);
// { host: "localhost", port: 8080, debug: true, timeout: 5000 }
דסטרקצ'רינג בלולאה¶
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 35 }
];
for (const { name, age } of users) {
console.log(`${name} is ${age}`);
}
// or with forEach
users.forEach(({ name, age }) => {
console.log(`${name} is ${age}`);
});
סיכום¶
דסטרקצ'רינג¶
- מערכים:
const [a, b, c] = [1, 2, 3]- לפי סדר - אובייקטים:
const { name, age } = person- לפי שם - שינוי שם:
const { name: fullName } = person - ברירת מחדל:
const { name = "unknown" } = person - מקונן:
const { address: { city } } = person - בפרמטרים:
function greet({ name, age }) {}
Rest¶
- מערכים:
const [first, ...rest] = arr - אובייקטים:
const { name, ...rest } = obj - פרמטרים:
function sum(...nums) {}
Spread¶
- מערכים:
[...arr1, ...arr2] - אובייקטים:
{ ...obj1, ...obj2 } - ארגומנטים:
Math.max(...numbers)
העתקה¶
- רדודה:
{ ...obj }או[...arr] - עמוקה:
structuredClone(obj)