Die `Object`-Klasse in JavaScript

Die `Object`-Klasse in JavaScript

Dieser Artikel erklärt die Object-Klasse in JavaScript.

Dieser Artikel erklärt die Object-Klasse in JavaScript, einschließlich praktischer Beispiele.

YouTube Video

Die Object-Klasse in JavaScript

Object ist ein eingebautes Objekt, das als Basis für alle JavaScript-Objekte dient. Viele der Kernfunktionen der Sprache, wie z. B. die Verwaltung von Eigenschaften, Vererbung (Prototypenkette), Aufzählung, Klonen und Einfrieren, werden durch das Verhalten von Object bereitgestellt.

Object.create

Es gibt mehrere Möglichkeiten, Objekte zu erstellen, und Sie sollten je nach Verwendungszweck die passende Methode wählen.

Objekt-Literal (am gebräuchlichsten)

Der folgende Code zeigt die einfachste und lesbarste Art, ein Objekt zu erstellen.

 1// Create an object using object literal
 2const user = {
 3  name: "Alice",
 4  age: 30,
 5  greet() {
 6    return `Hello, I'm ${this.name}`;
 7  }
 8};
 9
10console.log(user.greet()); // "Hello, I'm Alice"
  • In diesem Beispiel werden Eigenschaften und Methoden mit Literalen definiert. Es ist einfach und bietet in der Regel eine überlegene Leistung.

new Object()-Konstruktor

Der Object-Konstruktor wird selten verwendet, aber es ist hilfreich, sein Verhalten zu verstehen.

1// Create an object using the Object constructor
2const objFromCtor = new Object();
3objFromCtor.x = 10;
4objFromCtor.y = 20;
5
6console.log(objFromCtor); // { x: 10, y: 20 }
  • new Object() gibt ein leeres Objekt zurück, aber das Literal {} ist kürzer und häufiger.

Den Prototyp mit Object.create festlegen

Object.create wird verwendet, um ein Objekt mit einem bestimmten Prototypen zu erstellen.

1// Create an object with a specified prototype
2const proto = { hello() { return "hi"; } };
3const obj = Object.create(proto);
4obj.name = "Bob";
5
6console.log(obj.hello()); // "hi"
7console.log(Object.getPrototypeOf(obj) === proto); // true
  • Object.create ist ideal für das vererbungsbasierte Objekt-Design, da Sie damit die Prototypen-Kette präzise steuern können.

Eigenschaftsattribute und -deskriptoren

Eigenschaften verfügen über Attribute wie 'value', 'writable', 'enumerable' und 'configurable', die mit Object.defineProperty im Detail gesteuert werden können.

Grundbeispiel für die Verwendung von defineProperty

Als Nächstes folgt ein Beispiel zur Definition nicht-auflistbarer und schreibgeschützter Eigenschaften mit defineProperty.

 1// Define a non-enumerable read-only property
 2const person = { name: "Carol" };
 3
 4Object.defineProperty(person, "id", {
 5  value: 12345,
 6  writable: false,
 7  enumerable: false,
 8  configurable: false
 9});
10
11console.log(person.id); // 12345
12console.log(Object.keys(person)); // ["name"] — "id" is non-enumerable
13person.id = 999; // silently fails or throws in strict mode
14console.log(person.id); // still 12345
  • Mit defineProperty können Sie das Verhalten von Eigenschaften wie Aufzählung, Überschreibung und Löschung genau steuern.

Accessor-Eigenschaften (getter / setter)

Mit Accessoren können Sie Logik beim Lesen und Schreiben von Eigenschaften einfügen.

 1// Use getter and setter to manage internal state
 2const data = {
 3  _value: 1,
 4  get value() {
 5    return this._value;
 6  },
 7  set value(v) {
 8    if (typeof v === "number" && v > 0) {
 9      this._value = v;
10    } else {
11      throw new Error("value must be a positive number");
12    }
13  }
14};
15
16console.log(data.value); // 1
17data.value = 5;
18console.log(data.value); // 5
19// data.value = -1; // would throw
  • Mit getter und setter können Sie Zugriffe auf Eigenschaften wie eine externe API behandeln und Validierungen oder Seiteneffekte hinzufügen.

Prototyp und Vererbung (prototype / __proto__ / Object.getPrototypeOf)

Die Vererbung in JavaScript basiert auf der Prototypen-Kette, nicht auf Klassen. Objekte können auf andere Objekte als ihren Prototypen verweisen.

Object.getPrototypeOf und Object.setPrototypeOf

Das folgende Beispiel zeigt, wie Prototypen geprüft und gesetzt werden können.

1// Inspect and change prototype
2const base = { speak() { return "base"; } };
3const derived = Object.create(base);
4console.log(Object.getPrototypeOf(derived) === base); // true
5
6const other = { speak() { return "other"; } };
7Object.setPrototypeOf(derived, other);
8console.log(derived.speak()); // "other"
  • Object.getPrototypeOf ruft den Prototyp eines Objekts ab.
  • Object.setPrototypeOf ändert den Prototyp eines bestehenden Objekts, sollte aber vorsichtig verwendet werden, da dies die Performance beeinträchtigen kann.

Wichtige eingebaute Methoden

Wir werden die am häufigsten verwendeten und wichtigsten Methoden, die aus den von Object.prototype bereitgestellten Instanzmethoden ausgewählt wurden, sowie die statischen Methoden, die Object gehören, klar erläutern.

hasOwnProperty, isPrototypeOf, toString, valueOf

hasOwnProperty, isPrototypeOf, toString und valueOf definieren das grundlegende Verhalten von Objekten.

 1// Demonstrate prototype methods
 2const base = { greet() { return "hello"; } };
 3const child = Object.create(base);
 4const a = { x: 1 };
 5
 6console.log(a.hasOwnProperty("x")); // true
 7console.log(a.hasOwnProperty("toString")); // false — toString is inherited
 8
 9console.log(a.toString()); // "[object Object]" by default
10
11console.log(base.isPrototypeOf(child)); // true
12console.log(Object.prototype.isPrototypeOf(child)); // true
  • hasOwnProperty ist eine wichtige Methode, um zu prüfen, ob eine Eigenschaft direkt auf dem Objekt vorhanden ist.
  • isPrototypeOf prüft, ob das Zielobjekt diesen Prototypen besitzt.

Object.keys, Object.values, Object.entries

Object.keys, Object.values und Object.entries geben Listen der eigenen aufzählbaren Eigenschaften eines Objekts zurück. Sie sind nützlich für Iteration und Transformation.

 1// Keys, values and entries
 2const item = { id: 1, name: "Widget", price: 9.99 };
 3
 4// ["id", "name", "price"]
 5console.log(Object.keys(item));
 6
 7// [1, "Widget", 9.99]
 8console.log(Object.values(item));
 9
10// [["id",1], ["name","Widget"], ["price",9.99]]
11console.log(Object.entries(item));
  • Diese werden häufig zum Durchlaufen und Umwandeln von Objekten verwendet.

Object.assign

Object.assign wird zum flachen Kopieren und Zusammenführen verwendet. Beachten Sie, dass Prototypen und Accessor-Eigenschaften nicht kopiert werden.

1// Shallow copy / merge using Object.assign
2const target = { a: 1 };
3const source = { b: 2 };
4const result = Object.assign(target, source);
5
6console.log(result); // { a: 1, b: 2 }
7console.log(target === result); // true (merged into target)
  • Bei verschachtelten Objekten werden nur die Referenzen kopiert, daher benötigen Sie für Deep Cloning eine andere Implementierung.

Object.freeze, Object.seal, Object.preventExtensions

Object.freeze, Object.seal und Object.preventExtensions steuern die Mutierbarkeit von Objekten.

 1// Freeze vs seal vs preventExtensions
 2const obj = { a: 1 };
 3Object.freeze(obj);
 4obj.a = 2; // fails silently or throws in strict mode
 5delete obj.a; // fails
 6
 7const obj2 = { b: 2 };
 8Object.seal(obj2);
 9obj2.b = 3; // allowed
10// delete obj2.b; // fails
11
12const obj3 = { c: 3 };
13Object.preventExtensions(obj3);
14obj3.d = 4; // fails
  • freeze ist die strengste Methode; sie verhindert jegliche Änderungen an den Eigenschaften des Objekts.
  • seal verhindert das Hinzufügen oder Löschen von Eigenschaften, erlaubt jedoch das Ändern vorhandener Werte.
  • preventExtensions verhindert lediglich das Hinzufügen neuer Eigenschaften; bestehende Eigenschaften können weiterhin geändert oder gelöscht werden.

Objekt-Aufzählbarkeit, Reihenfolge und for...in / for...of

for...in zählt aufzählbare Eigenschaftsnamen auf, schließt aber auch Eigenschaften der Prototypen-Kette ein, weshalb es oft in Kombination mit hasOwnProperty verwendet wird. Die Kombination von Object.keys() mit for...of ist sicherer und macht Ihre Absichten deutlicher.

 1// Safe enumeration
 2const obj = Object.create({ inherited: true });
 3obj.own = 1;
 4
 5for (const key in obj) {
 6  if (obj.hasOwnProperty(key)) {
 7    console.log("own prop:", key);
 8  } else {
 9    console.log("inherited prop:", key);
10  }
11}
12
13for (const key of Object.keys(obj)) {
14  console.log("key via Object.keys:", key);
15}
  • Die Regeln für die Aufzählung von Eigenschaften sind in der ECMAScript-Spezifikation festgelegt. In manchen Fällen ist die Reihenfolge garantiert, in anderen nicht. Als allgemeine Regel gilt: Schlüssel, die als Zahlen interpretiert werden, sind in aufsteigender Reihenfolge sortiert, während andere Schlüssel der Einfügereihenfolge folgen.

Klonen und tiefes Kopieren

Es gibt zwei Arten des Kopierens von Objekten: das flache Kopieren mit Object.assign oder der Spread-Syntax sowie das tiefe Kopieren mit rekursiven Methoden. Es ist wichtig, sie je nach Situation angemessen zu verwenden.

Flaches Kopieren (Spread-Syntax / Object.assign)

1// Shallow copy with spread operator
2const original = { a: 1, nested: { x: 10 } };
3const shallow = { ...original };
4shallow.nested.x = 99;
5console.log(original.nested.x); // 99 — nested object is shared
  • Beim flachen Kopieren teilen verschachtelte Objekte die Referenz, sodass Änderungen am Originalobjekt auch die Kopie beeinflussen können.

Einfaches Deep Copy (mit Einschränkungen)

Der JSON-Trick ist eine schnelle Möglichkeit für ein Deep Copy, aber dabei gehen Funktionen, Date, zirkuläre Referenzen und undefined-Werte verloren. Für echtes Deep Cloning sollten Sie eine spezialisierte Bibliothek verwenden.

1// Deep clone using JSON methods — limited use-cases only
2const source = { a: 1, d: new Date(), nested: { x: 2 } };
3const cloned = JSON.parse(JSON.stringify(source));
4console.log(cloned); // ok for plain data, but Date becomes string, functions lost
  • JSON-basierte Methoden sind praktisch für die schnelle Verarbeitung einfacher Daten, aber in häufigen Fällen kann ihr Verhalten problematisch sein.

Mixins und Objektkomposition

Statt Mehrfachvererbung wird häufig das Muster verwendet, Verhalten über Mixins zu komponieren.

 1// Simple mixin function
 2const canEat = {
 3  eat() { return `${this.name} eats`; }
 4};
 5const canWalk = {
 6  walk() { return `${this.name} walks`; }
 7};
 8
 9function createPerson(name) {
10  const person = { name };
11  return Object.assign(person, canEat, canWalk);
12}
13
14const p = createPerson("Dana");
15console.log(p.eat()); // "Dana eats"
16console.log(p.walk()); // "Dana walks"
  • Mixins sind flexibel, aber Eigenschaftsnamen-Kollisionen und Testbarkeit müssen beachtet werden.

Häufige Fallstricke und bewährte Praktiken

Hier sind einige häufige Fallstricke und bewährte Praktiken.

  • Mutierbarkeit Objekte sind standardmäßig veränderbar. In Anwendungen mit Zustandsverwaltung sollten Sie unveränderliche Datenstrukturen mittels Object.freeze oder einer Immutable-Bibliothek verwenden.

  • Prototype Pollution Das direkte Zusammenführen externer Daten mit Object.assign oder Schleifen kann bei speziellen Eigenschaften wie __proto__ oder constructor unerwartete Nebeneffekte und Sicherheitsrisiken verursachen. Filtern Sie Benutzereingaben, bevor Sie sie direkt zusammenführen.

  • Fallstricke von for...in for...in zählt auch Prototyp-Eigenschaften auf, daher sollte dies mit hasOwnProperty geprüft werden. Die Verwendung von Object.keys ist übersichtlicher.

  • Fehlerhafte Nutzung von flachem Kopieren Überlegen Sie, ob Sie ein Deep Copy benötigen, um zu verhindern, dass Änderungen in verschachtelten Objekten das Originalobjekt beeinflussen.

Praktisches Beispiel: Unveränderliches Objekt-Update-Muster

Muster, die ein neues Objekt zurückgeben, ohne den Zustand direkt zu ändern, werden häufig in React und ähnlichen Bibliotheken verwendet.

 1// Immutable update example
 2const state = { todos: [{ id: 1, text: "Buy milk", done: false }] };
 3
 4// Toggle todo done immutably
 5function toggleTodo(state, todoId) {
 6  return {
 7    ...state,
 8    todos: state.todos.map(t => t.id === todoId ? { ...t, done: !t.done } : t)
 9  };
10}
11
12const newState = toggleTodo(state, 1);
13console.log(state.todos[0].done); // false
14console.log(newState.todos[0].done); // true
  • Dieser Code ist ein Beispiel dafür, wie ein neues Statusobjekt erstellt wird, ohne das ursprüngliche state-Objekt direkt zu verändern. Die Funktion toggleTodo kopiert das Array todos und gibt ein neues Objekt zurück, bei dem nur das Zielelement verändert wird, sodass das ursprüngliche state unverändert bleibt.
  • Unveränderliche Aktualisierungen reduzieren Nebeneffekte und erleichtern die Zustandsverwaltung.

Praktisches Beispiel: Sicheres Zusammenführen (Achten Sie auf Prototype Pollution)

Beim Zusammenführen von externen JSON-Daten sollte __proto__ ignoriert werden, um Prototype Pollution zu verhindern.

 1// Safe merge ignoring __proto__ keys
 2function safeMerge(target, source) {
 3  for (const key of Object.keys(source)) {
 4    if (key === "__proto__" || key === "constructor") continue;
 5    target[key] = source[key];
 6  }
 7  return target;
 8}
 9
10const target = {};
11const source = JSON.parse('{"a":1,"__proto__":{"polluted":true}}');
12safeMerge(target, source);
13console.log(target.polluted); // undefined — safe
14console.log({}.polluted); // undefined — prototype not polluted
  • Solcher Schutz ist auch in Bibliotheken und Frameworks wichtig.

Performance-Überlegungen

In Bezug auf die Performance sollten folgende Punkte beachtet werden:.

  • Vermeiden Sie häufige Prototyp-Änderungen (Object.setPrototypeOf) oder das dynamische Hinzufügen/Entfernen von Eigenschaften, da dies Optimierungen der Engine behindert.
  • Bei der Erzeugung vieler kleiner Objekte wird die Optimierung effektiver, wenn Sie Objekte mit einer einheitlichen Struktur (dem gleichen Satz von Eigenschaften) verwenden.
  • Tiefes Kopieren ist ressourcenintensiv. Minimieren Sie deren Verwendung oder nutzen Sie diff-basierte Updates.

Zusammenfassung

Object ist zentral für JavaScript und bietet zahlreiche Funktionen wie Objekterstellung, Eigenschaftsverwaltung, Vererbung, Kopieren und Steuerung der Mutierbarkeit. Es ist wichtig, APIs wie Object.defineProperty, Object.assign und Object.freeze zu verstehen und mit Sorgfalt zu entwerfen, um Fallstricke wie Prototypenverschmutzung und flache Kopien zu vermeiden.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video