ebody: Javascript Objekt wird immer überschrieben

problematische Seite

Hallo,

ich habe ein Objekt:

const objTableHeadings = {
  Title: '',
  Genre: '',
  Tags: ''
};

Dieses Objekt füge ich als Element einem Array zu arrData.push(objTableHeadings);

Anschließend füge ich dem Key 'Genre' einen Wert zu.

Ich füge dem Array ein weiteres Objekt als Element zu: arrData.push(objTableHeadings);

Anschließend füge ich diesem 2. Element und dem Key 'Genre' einen Wert zu.

Gebe ich dann arrData[] aus, enthalten die Keys beider Objekte den gleichen Wert. Der Wert vom 1. Objekt wurde überschrieben. Warum?

Gruß ebody

  1. problematische Seite

    printf('Hallo %s!', ['Du', 'ihr', 'Welt', 'zusammen'][rand(0, 3)]);

    Das Objekt referenziert immer objTableHeadings, egal wie oft du es ins Array einfügst. Daher werden Änderungen auch dort vorgenommen und von allen Referenzen diese Werte aufgerufen.
    Zu wenig Zeit für eine genaue Erklärung, aber das sollte gehen:

    const newTableHeadinsObj = Object.create(objTableHeadings);
    arrData.push(newTableHeadinsObj);
    

    /K

    --
    Klingonen sind doof. Sie rufen ständig nach einem Kaplan und wollen nach dem Tod in Styropor® sein.
    1. problematische Seite

      Hallo kai345,

      newTableHeadinsObj = Object.create(objTableHeadings);

      Ich bin nicht so sicher, ob das hier zielführend ist. Auf diesem Weg erzeugst Du ein leeres Objekt, das das objTableHeadings Objekt als Prototyp besitzt.

      Das ist etwas anderes als eine Kopie. Die wird von Dedlfix' Beispiel erzeugt, setzt aber einen aktuellen Browser voraus (d.h. nicht den Internet Explorer). Dedlfix verwendet den ... Operator, der je nach Zusammenhang "Rest" oder "Spread" Operator genannt wird. Im vorliegenden Fall ist es "Spread" - ausbreiten. Die Eigenschaften von objTableHeadings werden innerhalb von { } "ausgebreitet", d.h. verteilt, was dazu führt, dass JavaScript das wie ein Objektliteral auffasst, wo jede einzelne Eigenschaft aus objTableHeadings im Objektliteral zugewiesen wird.

      const foo = { a: 1, b: 2 };
      
      const bar1 = { a: foo.a, b: foo.b },
            bar2 = { ...foo };
      
      console.log(bar1.a, bar1.b);             // 1 2
      console.log(bar1.hasOwnProperty("a"));   // true
      console.log(bar1.hasOwnProperty("b"));   // true
      
      console.log(bar2.a, bar2.b);             // 1 2
      console.log(bar2.hasOwnProperty("a"));   // true
      console.log(bar2.hasOwnProperty("b"));   // true
      
      foo.a = 99;
      console.log(bar1.a, bar2.a);             // 1 1
      

      Das, was JavaScript bei bar1 und bar2 tut, ist exakt das gleiche. Die direkt in foo definierten Eigenschaften werden hergenommen und ins Objektliteral ausgebreitet. Deswegen gibt hasOwnProperty auch true aus.

      Aber Object.create macht etwas anderes:

      const foo = { a: 1, b: 2 };
      const bar = Object.create(foo);
      
      console.log(bar.a, bar.b);              // 1 2
      console.log(bar.hasOwnProperty("a"));   // FALSE
      console.log(bar.hasOwnProperty("b"));   // FALSE
      
      foo.a = 77;
      foo.b = 88;
      bar.a = 44;
      console.log(bar.a, bar.b);              // 44 88
      console.log(bar.hasOwnProperty("a"));   // TRUE
      console.log(bar.hasOwnProperty("b"));   // FALSE
      

      Hier wird foo zum Prototypen für bar - die JavaScript-Grundlage für Objektvererbung. Das bedeutet: Wenn in bar eine Eigenschaft oder Methode gesucht wird, die in bar nicht existiert, schaut JavaScript in foo nach und holt sich den Wert von dort. Bei einer Zuweisung wird aber immer nach bar geschrieben. Deswegen gibt hasOwnProperty zunächst FALSE für a und b aus, und deswegen schlägt die Änderung an foo.b auf bar durch.

      Rolf

      --
      sumpsi - posui - obstruxi
  2. problematische Seite

    @@ebody

    Gebe ich dann arrData[] aus, enthalten die Keys beider Objekte den gleichen Wert. Der Wert vom 1. Objekt wurde überschrieben. Warum?

    Dein Array arrData enthält zwei Zeiger auf einunddasselbe Objekt objTableHeadings.

    😷 LLAP

    --
    „Dann ist ja auch schrecklich, dass wir in einem Land leben, in dem nicht nur Bildungswillige leben, sondern auch hinreichende Zahlen von Bekloppten. Das darf ich so locker formulieren, ich bin ja jetzt Rentner und muss nicht mehr auf jedes Wort achten.“
    — Joachim Gauck über Impfgegner
  3. problematische Seite

    Tach!

    const objTableHeadings = {
      Title: '',
      Genre: '',
      Tags: ''
    };
    

    Dieses Objekt füge ich als Element einem Array zu arrData.push(objTableHeadings);

    Anschließend füge ich dem Key 'Genre' einen Wert zu.

    Genauer gesagt: Der hat schon einen Wert, du änderst ihn nur. Hinzufügen wäre, wenn der alte Wert erhalten bliebe, was nicht der Fall ist.

    Ich füge dem Array ein weiteres Objekt als Element zu: arrData.push(objTableHeadings);

    Wie bereits gesagt, du fügt das Objekt ein weiteres mal hinzu. Wenn du ein neues haben möchtest, musst du ein neues anlegen. Es kommt dabei darauf an, bis in welcher Tiefe es eine Kopie sein muss.

    arrData.push({...objTableHeadings});
    

    Das ist der einfachste Weg, eine Shallow Copy anzulegen. In deinem Beispiel sind die Eigenschaften alles Strings, also primitive Werte, die werden kopiert. Wenn es aber Objekte wären, würde lediglich eine weitere Referenz erstellt, die weiterhin auf das Original verweist.

    Warum?

    Das erklärt sich, wenn man die Hintergründe kennt, wie mit Variablen umgegangen wird. Eine Variable ist ein Container, in dem das System die Dinge verwaltet, die es dazu wissen muss. Primitive Werte steht direkt in diesem Container. Sie benötigen nur ein bis wenige Byte Speicherplatz und sind damit klein genug, direkt dort abgelegt zu werden. String können zwar länger sein, werden aber auch wie primitive Werte behandelt.

    Objekte und Arrays sind keine primitiven Werte. Die eigentlichen Inhalte werden üblicherweise irgendwo in einem anderen Speicherbereich abgelegt. Im Variablencontainer kommt dann nur eine Referenz auf diese andere Speicherstelle zu liegen. Damit nicht unnötig Speicher verbraucht wird, ist es in den meisten Systemen so, dass lediglich die Referenzen weitergegeben werden, wenn man mit der Variable etwas anstellt. Sie zeigen ohne weitere Handlung also immer auf denselben Speicherbereich. Änderst du daran etwas, bekommst du diese Änderung natürlich auch über alle anderen Referenzen mit, die auf denselben Speicherbereich zeigen.

    dedlfix.