2.7 אובייקטים הרצאה
אובייקטים - Objects¶
אובייקטים הם אחד מטיפוסי הנתונים הכי חשובים ב-JavaScript. הם מאפשרים לנו לאחסן אוסף של זוגות מפתח-ערך, ומייצגים ישויות מורכבות.
אם אתם מכירים מילונים (dictionaries) בפייתון - אובייקטים דומים מאוד, אבל יש כמה הבדלים חשובים.
יצירת אובייקטים - Object Literal¶
הדרך הנפוצה ביותר ליצור אובייקט היא עם סוגריים מסולסלים:
const person = {
name: "Alice",
age: 25,
city: "Tel Aviv"
};
console.log(person); // { name: "Alice", age: 25, city: "Tel Aviv" }
המפתחות (keys) הם תמיד מחרוזות (או Symbols). הערכים (values) יכולים להיות כל דבר - מספרים, מחרוזות, מערכים, אובייקטים אחרים, פונקציות.
const student = {
name: "Bob",
age: 22,
grades: [90, 85, 92], // array as value
address: { // nested object
street: "Herzl 10",
city: "Haifa"
},
isActive: true // boolean
};
בפייתון הייתם כותבים:
ההבדל העיקרי: בפייתון המפתחות חייבים להיות בתוך גרשיים. ב-JavaScript לא צריך (אלא אם המפתח מכיל תווים מיוחדים).
גישה למאפיינים - Accessing Properties¶
נקודה - Dot Notation¶
const person = { name: "Alice", age: 25 };
console.log(person.name); // "Alice"
console.log(person.age); // 25
סוגריים מרובעים - Bracket Notation¶
console.log(person["name"]); // "Alice"
console.log(person["age"]); // 25
// useful when the key is in a variable
const key = "name";
console.log(person[key]); // "Alice"
// useful when the key has special characters
const obj = { "full-name": "Alice Smith" };
console.log(obj["full-name"]); // "Alice Smith"
// obj.full-name would cause an error!
בפייתון אפשר לגשת רק עם סוגריים מרובעים (person["name"]). ב-JavaScript יש את שתי הדרכים, והנקודה מועדפת כשאפשר.
הוספה, עדכון ומחיקה של מאפיינים¶
const car = { brand: "Toyota" };
// adding properties
car.model = "Corolla";
car.year = 2024;
car["color"] = "blue";
console.log(car);
// { brand: "Toyota", model: "Corolla", year: 2024, color: "blue" }
// updating properties
car.year = 2025;
// deleting properties
delete car.color;
console.log(car);
// { brand: "Toyota", model: "Corolla", year: 2025 }
שימו לב שאפשר להוסיף ולמחוק מאפיינים גם אם האובייקט הוגדר עם const. ה-const מגן רק על ההפניה (reference), לא על התוכן. זה בדיוק כמו בפייתון.
קיצור שמות מאפיינים - Shorthand Property Names¶
כשיש משתנה בשם זהה למפתח שרוצים ליצור, אפשר לקצר:
const name = "Alice";
const age = 25;
const city = "Tel Aviv";
// long version
const person1 = { name: name, age: age, city: city };
// shorthand - same result
const person2 = { name, age, city };
console.log(person2); // { name: "Alice", age: 25, city: "Tel Aviv" }
זה מאוד נפוץ ב-JavaScript מודרני, במיוחד בפונקציות שמחזירות אובייקטים.
שמות מאפיינים מחושבים - Computed Property Names¶
אפשר להשתמש בביטוי כשם של מאפיין:
const key = "name";
const obj = { [key]: "Alice" };
console.log(obj); // { name: "Alice" }
// useful for dynamic keys
const field = "email";
const value = "alice@test.com";
const data = { [field]: value };
console.log(data); // { email: "alice@test.com" }
// can use expressions
const prefix = "user";
const userObj = {
[`${prefix}Name`]: "Alice",
[`${prefix}Age`]: 25
};
console.log(userObj); // { userName: "Alice", userAge: 25 }
מתודות - Methods¶
מתודות הן פונקציות שמוגדרות בתוך אובייקט:
const calculator = {
// method - short syntax
add(a, b) {
return a + b;
},
// method - longer syntax (also works)
subtract: function(a, b) {
return a - b;
},
// arrow function as method (be careful with "this"!)
multiply: (a, b) => a * b
};
console.log(calculator.add(5, 3)); // 8
console.log(calculator.subtract(10, 4)); // 6
console.log(calculator.multiply(3, 7)); // 21
מילת המפתח this¶
בתוך מתודה של אובייקט, this מתייחס לאובייקט עצמו:
const person = {
name: "Alice",
age: 25,
greet() {
console.log("Hi, I'm " + this.name);
},
haveBirthday() {
this.age += 1;
console.log(this.name + " is now " + this.age);
}
};
person.greet(); // "Hi, I'm Alice"
person.haveBirthday(); // "Alice is now 26"
שימו לב - this עובד רק עם פונקציות רגילות (function declaration / method shorthand). עם arrow functions, this לא מתייחס לאובייקט:
const person2 = {
name: "Bob",
// this works
greet() {
console.log("Hi, I'm " + this.name);
},
// this does NOT work as expected
greetArrow: () => {
console.log("Hi, I'm " + this.name); // this is NOT the object!
}
};
person2.greet(); // "Hi, I'm Bob"
person2.greetArrow(); // "Hi, I'm undefined"
כלל אצבע: כשרוצים להשתמש ב-this - משתמשים בפונקציה רגילה, לא arrow function.
בפייתון המקביל הוא self, אבל שם חייבים להעביר אותו כפרמטר ראשון. ב-JavaScript this זמין אוטומטית.
Object.keys, Object.values, Object.entries¶
מתודות סטטיות שמחזירות מערכים של המפתחות, הערכים, או הזוגות:
const person = { name: "Alice", age: 25, city: "Tel Aviv" };
// keys - array of all keys
console.log(Object.keys(person)); // ["name", "age", "city"]
// values - array of all values
console.log(Object.values(person)); // ["Alice", 25, "Tel Aviv"]
// entries - array of [key, value] pairs
console.log(Object.entries(person));
// [["name", "Alice"], ["age", 25], ["city", "Tel Aviv"]]
זה מאוד שימושי למעבר על אובייקט:
// iterate over keys and values
Object.entries(person).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// name: Alice
// age: 25
// city: Tel Aviv
בפייתון זה מקביל ל:
// Python equivalent:
// person.keys() -> dict.keys()
// person.values() -> dict.values()
// person.items() -> dict.items() (not "entries"!)
Object.assign - מיזוג אובייקטים¶
מאפשר להעתיק מאפיינים מאובייקט אחד לאחר:
const defaults = { color: "blue", size: "medium", theme: "dark" };
const userPrefs = { color: "red", size: "large" };
// merge - userPrefs overrides defaults
const settings = Object.assign({}, defaults, userPrefs);
console.log(settings);
// { color: "red", size: "large", theme: "dark" }
הארגומנט הראשון הוא האובייקט היעד. כל שאר הארגומנטים מועתקים אליו. מאפיינים עם אותו שם נדרסים על ידי האובייקט האחרון.
אופרטור הפיזור לאובייקטים - Spread Operator¶
דרך מודרנית יותר למזג אובייקטים:
const defaults = { color: "blue", size: "medium", theme: "dark" };
const userPrefs = { color: "red", size: "large" };
// spread - same result as Object.assign
const settings = { ...defaults, ...userPrefs };
console.log(settings);
// { color: "red", size: "large", theme: "dark" }
// can add new properties too
const fullSettings = { ...settings, language: "he", fontSize: 16 };
ב-spread, הסדר חשוב - מי שבא אחרון "מנצח". אפשר לחשוב על זה כאילו כל אובייקט "נשפך" לתוך החדש, ומה שנשפך אחרון דורס את מה שהיה לפניו.
Object.freeze ו-Object.seal¶
Object.freeze - הקפאה מוחלטת¶
const config = { host: "localhost", port: 3000 };
Object.freeze(config);
config.port = 8080; // does nothing (silently fails)
config.newProp = "hello"; // does nothing
delete config.host; // does nothing
console.log(config); // { host: "localhost", port: 3000 } - unchanged
Object.seal - חסימת מבנה¶
const settings = { color: "blue", size: "medium" };
Object.seal(settings);
settings.color = "red"; // works - can update existing
settings.newProp = "hello"; // does nothing - can't add
delete settings.color; // does nothing - can't delete
console.log(settings); // { color: "red", size: "medium" }
ההבדל: freeze לא מאפשר שום שינוי. seal מאפשר לעדכן ערכים קיימים אבל לא להוסיף או למחוק מאפיינים.
שימו לב - שניהם עובדים ברמה שטחית בלבד (shallow). אובייקטים מקוננים לא מוגנים.
אופרטור in ו-hasOwnProperty¶
אופרטור in¶
בודק אם מפתח קיים באובייקט:
const person = { name: "Alice", age: 25 };
console.log("name" in person); // true
console.log("email" in person); // false
hasOwnProperty¶
בודק אם מפתח קיים באובייקט עצמו (לא בשרשרת הפרוטוטייפים):
const person = { name: "Alice", age: 25 };
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("toString")); // false (inherited)
console.log("toString" in person); // true (exists on prototype)
ברוב המקרים in מספיק, אבל hasOwnProperty שימושי כשרוצים לבדוק רק את המאפיינים הישירים של האובייקט.
בפייתון הייתם בודקים עם "name" in person למילון, או hasattr(obj, "name") לאובייקט.
השוואת אובייקטים - Comparison¶
אובייקטים מושווים לפי הפניה (reference), לא לפי ערך:
const a = { name: "Alice" };
const b = { name: "Alice" };
const c = a;
console.log(a === b); // false - different objects in memory
console.log(a === c); // true - same reference
זה בדיוק כמו בפייתון:
// Python:
// a = {"name": "Alice"}
// b = {"name": "Alice"}
// a == b -> True (compares values)
// a is b -> False (compares references)
אבל יש הבדל חשוב: בפייתון == משווה ערכים, ו-is משווה הפניות. ב-JavaScript, === משווה הפניות לאובייקטים - אין דרך מובנית להשוות ערכים של אובייקטים.
אם רוצים להשוות תוכן, צריך לעשות את זה ידנית:
// simple shallow comparison
function areEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
return keys1.every((key) => obj1[key] === obj2[key]);
}
// quick hack for deep comparison (not recommended for production)
JSON.stringify(a) === JSON.stringify(b); // true - but has limitations
אובייקטים מקוננים - Nested Objects¶
const company = {
name: "TechCorp",
address: {
street: "Rothschild 1",
city: "Tel Aviv",
country: "Israel"
},
employees: [
{ name: "Alice", role: "developer" },
{ name: "Bob", role: "designer" }
]
};
// accessing nested properties
console.log(company.address.city); // "Tel Aviv"
console.log(company.employees[0].name); // "Alice"
console.log(company.employees[1].role); // "designer"
שרשור אופציונלי - Optional Chaining¶
מה קורה כשמנסים לגשת למאפיין שלא קיים?
const user = { name: "Alice" };
console.log(user.address); // undefined
console.log(user.address.city); // ERROR! Cannot read property 'city' of undefined
אופרטור ?. (optional chaining) פותר את הבעיה:
const user = { name: "Alice" };
console.log(user.address?.city); // undefined (no error)
console.log(user.profile?.avatar); // undefined (no error)
// can chain multiple levels
const company = {
ceo: {
name: "Bob"
// no address property
}
};
console.log(company.ceo?.address?.city); // undefined (no error)
אפשר להשתמש גם עם מתודות ומערכים:
const obj = {};
obj.someMethod?.(); // won't crash if someMethod doesn't exist
obj.arr?.[0]; // won't crash if arr doesn't exist
בפייתון אין מקביל ישיר. הייתם צריכים:
// Python equivalent:
// city = user.get("address", {}).get("city")
// or
// city = user["address"]["city"] if "address" in user else None
ההבדל בין אובייקטים ל-דיקשנרי בפייתון¶
| JavaScript Object | Python Dict | |
|---|---|---|
| תחביר | { name: "Alice" } |
{ "name": "Alice" } |
| גישה | obj.name או obj["name"] |
dict["name"] בלבד |
| מפתחות | מחרוזות (או Symbols) | כל טיפוס hashable |
| מתודות | יכול להכיל פונקציות | בדרך כלל לא |
| prototype | יש שרשרת ירושה | אין |
| השוואה | לפי הפניה בלבד | == משווה ערכים |
ההבדל הכי גדול: אובייקט ב-JavaScript יכול להכיל מתודות ומתנהג כמו מחלקה קטנה. מילון בפייתון הוא בעיקר מבנה נתונים.
דפוסים נפוצים¶
המרת מערך של זוגות לאובייקט¶
const pairs = [["name", "Alice"], ["age", 25], ["city", "Tel Aviv"]];
const obj = Object.fromEntries(pairs);
console.log(obj); // { name: "Alice", age: 25, city: "Tel Aviv" }
מעבר על אובייקט עם for...in¶
const person = { name: "Alice", age: 25, city: "Tel Aviv" };
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
// name: Alice
// age: 25
// city: Tel Aviv
שכפול אובייקט¶
const original = { name: "Alice", age: 25 };
// shallow copy with spread
const copy = { ...original };
// shallow copy with Object.assign
const copy2 = Object.assign({}, original);
copy.name = "Bob";
console.log(original.name); // "Alice" - not affected
בדיקה אם אובייקט ריק¶
סיכום¶
- אובייקטים הם אוספים של זוגות מפתח-ערך, דומים למילונים בפייתון
- גישה עם נקודה (
obj.key) או סוגריים (obj["key"]) - ניתן להוסיף, לעדכן ולמחוק מאפיינים בכל רגע
- קיצורים: shorthand properties, computed properties
- מתודות הן פונקציות בתוך אובייקט,
thisמתייחס לאובייקט Object.keys(),.values(),.entries()למעבר על אובייקט- spread (
{...obj}) ו-Object.assign()למיזוג והעתקה Object.freeze()ו-Object.seal()למניעת שינויים- אובייקטים מושווים לפי הפניה, לא לפי ערך
?.(optional chaining) למניעת שגיאות בגישה למאפיינים מקוננים