Objekte in JavaScript meistern
Objekte in JavaScript meistern
Objekte sind das Herzstück von JavaScript. Fast alles in JS ist ein Objekt - Arrays, Funktionen, sogar Strings haben Objekt-Methoden. Zeit, Objekte wirklich zu verstehen!
Objekte erstellen
Object Literal (Standard)
const person = {
name: "Max",
alter: 25,
stadt: "Berlin"
};
Mit Variablen (Property Shorthand)
const name = "Lisa";
const alter = 30;
const stadt = "Hamburg";
// Alt
const person1 = { name: name, alter: alter, stadt: stadt };
// Modern - Property Shorthand
const person2 = { name, alter, stadt };
console.log(person2); // { name: "Lisa", alter: 30, stadt: "Hamburg" }
Computed Property Names
const eigenschaft = "dynamisch";
const obj = {
statisch: "fester Name",
[eigenschaft]: "dynamischer Name",
[`prop_${1 + 1}`]: "prop_2"
};
console.log(obj.dynamisch); // "dynamischer Name"
console.log(obj.prop_2); // "prop_2"
Object.create()
const prototyp = {
gruss() {
return `Hallo, ich bin ${this.name}`;
}
};
const person = Object.create(prototyp);
person.name = "Max";
console.log(person.gruss()); // "Hallo, ich bin Max"
Eigenschaften zugreifen
Dot Notation vs. Bracket Notation
const person = {
name: "Max",
"voller-name": "Max Mustermann",
42: "Antwort"
};
// Dot Notation - Standard
console.log(person.name); // "Max"
// Bracket Notation - für spezielle Fälle
console.log(person["name"]); // "Max"
console.log(person["voller-name"]); // "Max Mustermann" (Bindestrich!)
console.log(person[42]); // "Antwort" (Zahl als Key!)
// Dynamischer Zugriff
const key = "name";
console.log(person[key]); // "Max"
Optional Chaining (?.)
const user = {
name: "Max",
adresse: {
stadt: "Berlin"
}
};
// Ohne Optional Chaining - kann crashen!
// console.log(user.kontakt.email); // TypeError!
// Mit Optional Chaining - sicher
console.log(user.kontakt?.email); // undefined
console.log(user.adresse?.stadt); // "Berlin"
console.log(user.adresse?.plz); // undefined
// Verkettung
console.log(user?.adresse?.land?.code); // undefined
// Mit Methodenaufruf
const obj = {
methode() { return "Hallo"; }
};
console.log(obj.methode?.()); // "Hallo"
console.log(obj.nichtExistent?.()); // undefined
Nullish Coalescing (??)
const config = {
timeout: 0,
retries: null
};
// || behandelt 0 und "" als falsy
console.log(config.timeout || 3000); // 3000 (falsch!)
console.log(config.retries || 3); // 3
// ?? nur bei null/undefined
console.log(config.timeout ?? 3000); // 0 (korrekt!)
console.log(config.retries ?? 3); // 3
console.log(config.missing ?? "default"); // "default"
Eigenschaften hinzufügen & ändern
const person = { name: "Max" };
// Hinzufügen
person.alter = 25;
person["stadt"] = "Berlin";
// Ändern
person.name = "Maximilian";
// Mit Object.assign
Object.assign(person, { hobby: "Coding", land: "Deutschland" });
// Mit Spread (neues Objekt!)
const erweitert = { ...person, beruf: "Entwickler" };
console.log(person);
// { name: "Maximilian", alter: 25, stadt: "Berlin", hobby: "Coding", land: "Deutschland" }
Eigenschaften entfernen
const person = {
name: "Max",
alter: 25,
stadt: "Berlin"
};
// delete - verändert Original
delete person.alter;
console.log(person); // { name: "Max", stadt: "Berlin" }
// Destructuring - neues Objekt ohne Eigenschaft
const { stadt, ...rest } = person;
console.log(rest); // { name: "Max" }
console.log(person); // { name: "Max", stadt: "Berlin" } - unverändert!
Objekte prüfen
Eigenschaft existiert?
const person = {
name: "Max",
alter: undefined
};
// in - prüft auch Prototyp-Kette
console.log("name" in person); // true
console.log("alter" in person); // true (auch wenn undefined!)
console.log("toString" in person); // true (geerbt!)
// hasOwnProperty - nur eigene Eigenschaften
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("toString")); // false
// Object.hasOwn (modern)
console.log(Object.hasOwn(person, "name")); // true
Objekt-Typ prüfen
const arr = [1, 2, 3];
const obj = { a: 1 };
const fn = function() {};
console.log(typeof arr); // "object" (nicht hilfreich!)
console.log(typeof obj); // "object"
console.log(typeof fn); // "function"
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
// Konstruktor prüfen
console.log(arr.constructor === Array); // true
console.log(obj.constructor === Object); // true
Objekte iterieren
Object.keys / values / entries
const person = {
name: "Max",
alter: 25,
stadt: "Berlin"
};
// Schlüssel
const keys = Object.keys(person);
console.log(keys); // ["name", "alter", "stadt"]
// Werte
const values = Object.values(person);
console.log(values); // ["Max", 25, "Berlin"]
// Key-Value-Paare
const entries = Object.entries(person);
console.log(entries);
// [["name", "Max"], ["alter", 25], ["stadt", "Berlin"]]
Iterieren
const person = { name: "Max", alter: 25, stadt: "Berlin" };
// for...in (alle enumerable, inkl. geerbt)
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
// Object.keys + forEach
Object.keys(person).forEach(key => {
console.log(`${key}: ${person[key]}`);
});
// Object.entries + for...of (empfohlen)
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
Objekte transformieren
Object.fromEntries
const entries = [
["name", "Max"],
["alter", 25]
];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: "Max", alter: 25 }
// Praktisch: Objekt transformieren
const preise = { apfel: 1.50, banane: 0.80, orange: 1.20 };
const erhoehtePreise = Object.fromEntries(
Object.entries(preise).map(([key, value]) => [key, value * 1.1])
);
console.log(erhoehtePreise);
// { apfel: 1.65, banane: 0.88, orange: 1.32 }
Objekte filtern
const scores = { Max: 85, Lisa: 92, Tom: 45, Anna: 78 };
// Nur bestandene (>= 60)
const bestanden = Object.fromEntries(
Object.entries(scores).filter(([_, score]) => score >= 60)
);
console.log(bestanden); // { Max: 85, Lisa: 92, Anna: 78 }
Objekte kopieren
Shallow Copy (flach)
const original = {
name: "Max",
adresse: { stadt: "Berlin" }
};
// Spread
const kopie1 = { ...original };
// Object.assign
const kopie2 = Object.assign({}, original);
// Problem: Nested Objects sind Referenzen!
kopie1.name = "Lisa";
kopie1.adresse.stadt = "Hamburg";
console.log(original.name); // "Max" (OK)
console.log(original.adresse.stadt); // "Hamburg" (geändert!)
Deep Copy (tief)
const original = {
name: "Max",
adresse: { stadt: "Berlin" }
};
// Mit JSON (einfach, aber limitiert)
const deepCopy1 = JSON.parse(JSON.stringify(original));
// Mit structuredClone (modern)
const deepCopy2 = structuredClone(original);
deepCopy2.adresse.stadt = "Hamburg";
console.log(original.adresse.stadt); // "Berlin" (unverändert!)
Objekte zusammenführen
Object.assign
const defaults = { theme: "light", lang: "de", debug: false };
const userPrefs = { theme: "dark", notifications: true };
const config = Object.assign({}, defaults, userPrefs);
console.log(config);
// { theme: "dark", lang: "de", debug: false, notifications: true }
Spread Operator
const defaults = { theme: "light", lang: "de" };
const userPrefs = { theme: "dark" };
const config = { ...defaults, ...userPrefs };
console.log(config); // { theme: "dark", lang: "de" }
// Tiefe Zusammenführung
const obj1 = { a: { b: 1 } };
const obj2 = { a: { c: 2 } };
// Spread ist flach!
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: { c: 2 } } - b ist weg!
// Tiefe Zusammenführung manuell
const deepMerged = {
a: { ...obj1.a, ...obj2.a }
};
console.log(deepMerged); // { a: { b: 1, c: 2 } }
Objekte einfrieren
Object.freeze
const config = {
apiUrl: "https://api.example.com",
timeout: 5000
};
Object.freeze(config);
config.timeout = 10000; // Ignoriert (strict mode: Error)
config.newProp = "test"; // Ignoriert
delete config.apiUrl; // Ignoriert
console.log(config); // Unverändert!
// Achtung: Nur flach!
const obj = { nested: { value: 1 } };
Object.freeze(obj);
obj.nested.value = 99; // Funktioniert!
Object.seal
const obj = { a: 1, b: 2 };
Object.seal(obj);
obj.a = 10; // OK - Ändern erlaubt
obj.c = 3; // Ignoriert - Hinzufügen verboten
delete obj.b; // Ignoriert - Löschen verboten
Prüfen
const frozen = Object.freeze({ a: 1 });
const sealed = Object.seal({ a: 1 });
const normal = { a: 1 };
console.log(Object.isFrozen(frozen)); // true
console.log(Object.isSealed(sealed)); // true
console.log(Object.isExtensible(normal)); // true
Objekt-Methoden
Method Shorthand
const person = {
name: "Max",
// Alt
gruss: function() {
return `Hallo, ich bin ${this.name}`;
},
// Modern - Method Shorthand
verabschieden() {
return `Tschüss von ${this.name}`;
}
};
Getter & Setter
const person = {
vorname: "Max",
nachname: "Mustermann",
// Getter
get vollername() {
return `${this.vorname} ${this.nachname}`;
},
// Setter
set vollername(name) {
const [vorname, nachname] = name.split(" ");
this.vorname = vorname;
this.nachname = nachname;
}
};
console.log(person.vollername); // "Max Mustermann"
person.vollername = "Lisa Schmidt";
console.log(person.vorname); // "Lisa"
console.log(person.nachname); // "Schmidt"
Praktisches Beispiel
const konto = {
_guthaben: 1000, // Konvention: _ = "privat"
get guthaben() {
return `${this._guthaben.toFixed(2)} €`;
},
set guthaben(betrag) {
if (betrag < 0) {
throw new Error("Guthaben kann nicht negativ sein");
}
this._guthaben = betrag;
},
einzahlen(betrag) {
this._guthaben += betrag;
return this; // Für Chaining
},
abheben(betrag) {
if (betrag > this._guthaben) {
throw new Error("Nicht genug Guthaben");
}
this._guthaben -= betrag;
return this;
}
};
konto.einzahlen(500).abheben(200);
console.log(konto.guthaben); // "1300.00 €"
Nützliche Patterns
Default-Werte
function createUser(options = {}) {
const defaults = {
name: "Gast",
role: "user",
active: true
};
return { ...defaults, ...options };
}
const user1 = createUser();
// { name: "Gast", role: "user", active: true }
const user2 = createUser({ name: "Max", role: "admin" });
// { name: "Max", role: "admin", active: true }
Pick - Nur bestimmte Keys
const person = { name: "Max", alter: 25, stadt: "Berlin", email: "max@test.de" };
function pick(obj, keys) {
return Object.fromEntries(
keys.filter(key => key in obj).map(key => [key, obj[key]])
);
}
const nameUndEmail = pick(person, ["name", "email"]);
console.log(nameUndEmail); // { name: "Max", email: "max@test.de" }
Omit - Bestimmte Keys weglassen
function omit(obj, keys) {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key))
);
}
const ohneEmail = omit(person, ["email"]);
console.log(ohneEmail); // { name: "Max", alter: 25, stadt: "Berlin" }
Zusammenfassung
| Operation | Syntax |
|---|---|
| Erstellen | { } oder Object.create() |
| Zugriff | obj.key oder obj["key"] |
| Sicherer Zugriff | obj?.nested?.value |
| Default | value ?? default |
| Keys | Object.keys(obj) |
| Values | Object.values(obj) |
| Entries | Object.entries(obj) |
| Kopieren | { ...obj } oder structuredClone() |
| Zusammenführen | { ...obj1, ...obj2 } |
| Einfrieren | Object.freeze(obj) |
Übung: Erstelle ein createStore Funktion, die einen einfachen State-Store mit get(), set(key, value) und subscribe(callback) Methoden zurückgibt!