tobi85: JavaScript Array und Object

Hallo,

ich habe zwar bereits etwas gegooglt, aber den Unterschied, zwischen Array und Object habe ich noch nicht verstanden. Wann nehme ich ein Object und wann ein Array.

Ist der eigentliche Unterschied, dass ein Array in JS keinen KEY hat?

var person = ["John", "Doe", 46]; 
var person = {firstName:"John", lastName:"Doe", age:46}; 
  1. Tach!

    ich habe zwar bereits etwas gegooglt, aber den Unterschied, zwischen Array und Object habe ich noch nicht verstanden. Wann nehme ich ein Object und wann ein Array.

    Ein Array verwaltet eine mehr oder weniger unbekannte Anzahl üblicherweise gleichartiger Dinge über einen numerischen Index. Ein Objekt hat meist eine genau feststehende Anzahl benannter Eigenschaften, die beliebigen Inhalt haben können.

    var person = ["John", "Doe", 46];
    var person = {firstName:"John", lastName:"Doe", age:46};

    Das zweite ist kein Array sondern ein Objekt.

    Arrays sind aber lediglich spezialisierte Objekte. Man kann sie auch mit Eigenschaften erweitern, und dann sieht es wie ein assoziatives Array aus. Einen Vorteil gegenüber einem Nicht-Array-Objekt hat man nicht.

    Zur Verwirrung trägt auch noch bei, dass man Eigenschaften von Objekten wie bei einem assoziativen Array über einen String als Namen ansprechen kann, also person['firstname']. Das macht aber auch kein Array aus dem Objekt.

    dedlfix.

    1. @@dedlfix

      Ein Array verwaltet eine mehr oder weniger unbekannte Anzahl üblicherweise gleichartiger Dinge über einen numerischen Index.

      Da kommt’s also auf die Reihenfolge an; ["John", "Doe", 46] ist was anderes als ["Doe", 46, "John"].

      Bei Objekten hingegen nicht; {firstName:"John", lastName:"Doe", age:46} ist dasselbe wie {lastName:"Doe", age:46, firstName:"John"}.

      Ein Objekt hat meist eine genau feststehende Anzahl benannter Eigenschaften

      ??

      var jane = {firstName:"Jane", lastName:"Roe"};
      

      Bei Frauen gibt man das Alter nicht an.

      jane.age = 42;
      

      Oder doch. So feststehend ist die Anzahl benannter Eigenschaften nun auch nicht.

      LLAP 🖖

      --
      “You might believe there are benefits for the developer, but first of all, you should put those behind the interest of the user.” —Stefan Tilkov
      Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
      1. Hallo, dann habe ich mal noch eine Frage zum Objekt. Wie kann ich ein Objekt multidimensional erstellen. Mir gelingt es nur so...

        var data = {};
        data["1"] = "Pulli";
        data["2"] = "Hose";
        
        var dataAll = {data:{auftragnr:auftragnr,knr:knr},artikel:data};
        

        kann ich das in JS nicht wie bei PHP aufbauen/zusammenführen

        $dataAll["data"]["auftragnr"] = auftragnr;
        $dataAll["data"]["knr"] = knr;
        $dataAll["artikel"]["1"] = pulli;
        $dataAll["artikel"]["2"] = hose;
        
        1. @@tobi85

          var data = {};
          data["1"] = "Pulli";
          data["2"] = "Hose";
          

          Das ist ein sicheres Zeichen, dass du ein Array willst: var data = ["Pulli", "Hose"];.

          $dataAll["data"]["auftragnr"] = auftragnr;
          $dataAll["data"]["knr"] = knr;
          $dataAll["artikel"]["1"] = pulli;
          $dataAll["artikel"]["2"] = hose;
          

          Das Objekt könnte dann so aussehen:

          var data = {
            auftragnr: 12345,
            knr: 6789,
            artikel: ["Pulli", "Hose"]
          };
          

          LLAP 🖖

          --
          “You might believe there are benefits for the developer, but first of all, you should put those behind the interest of the user.” —Stefan Tilkov
          Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
          1. @@Gunnar Bittersmann

            var data = {}

            Da hat der dedlfix natürlich recht; data kein guter Bezeichner. Das wäre wohl sowas wie auftrag.

            Und mehrere davon wären wieder ein Array:

            var aufträge = [
              {
                auftragnr: 12345,
                knr: 6789,
                  artikel: ["Pulli", "Hose"]
              },
              {
                auftragnr: 56789,
                knr: 1234,
                  artikel: ["Socken"]
              }
            ];
            

            LLAP 🖖

            PS: Für kaputte Syntax-Highlighter kann ich nichts. Die Ellipse '…' oben wäre natürlich ein Syntaxfehler im Code; das soll ja hier Pseudocode sein. Aber was bitteschön gibt es an dem 'ä' rumzumäkeln?

            --
            “You might believe there are benefits for the developer, but first of all, you should put those behind the interest of the user.” —Stefan Tilkov
            Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
            1. Hallo,

              var aufträge = [
                ...
              ];
              

              PS: Für kaputte Syntax-Highlighter kann ich nichts. [...] Aber was bitteschön gibt es an dem 'ä' rumzumäkeln?

              hab ich mich beim Lesen auch gefragt. Eigentlich nichts. Allerdings dachte ich bis vor nicht allzu langer Zeit auch noch, dass nur Buchstaben und Ziffern aus dem ASCII-Bereich für Bezeichner erlaubt wären.

              Ciao,
               Martin

              --
              Nothing travels faster than the speed of light with the possible exception of bad news, which obeys its own special laws.
              - Douglas Adams, The Hitchhiker's Guide To The Galaxy
            2. Tach!

              PS: Für kaputte Syntax-Highlighter kann ich nichts. Die Ellipse '…' oben wäre natürlich ein Syntaxfehler im Code; das soll ja hier Pseudocode sein. Aber was bitteschön gibt es an dem 'ä' rumzumäkeln?

              Wenn es Pseudocode ist, warum zeichnest du ihn dann mit dem Namen einer konkreten Sprache aus?

              Am ä ist auszusetzen, dass sich die deutsche Sprache generell schlechter für Code eignet als Englisch. Als ein Beispiel sei das Wort "Hersteller" genannt. Das wird sowohl in Einzahl als auch Mehrzahl jeweils gleich geschrieben. Wie macht man nun damit deutlich, ob einer oder mehrere gemeint sind, ohne dass man das aus dem umgebenden Kontext herauslesen muss? Ja, da gibt es einige Varianten, DerHersteller, DieHersteller, Liste_der_Hersteller, eine aufwändiger als die andere. In Englisch gibt es sehr einfach klar unterscheidbare Formen, ohne Hilfskonstrukte: Manufacturer, Manufacturers. Zudem ist das Liste in Liste_der_Hersteller auch unnötig redundant, weil, dass es eine Liste ist, aus dem Kontext klar und deutlich hervorgehen sollte. (Ja, hier muss man auf den Kontext zurückgreifen, was im Widerspruch zu meiner eben erst aufgestellten Forderung steht. Javascript hat kein in dieser Hinsicht hilfreiches Typkonzept wie beispielsweise TypeScript. Dort ist die IDE im Zweifelsfall in der Lage, mit Mausdraufzeigen konkrete Hinweise zu geben.)

              dedlfix.

        2. Tach!

          Wie kann ich ein Objekt multidimensional erstellen.

          Das gibt es nicht. Die Eigenschaftennamen eines Objekts streben nicht konstant in eine bestimmte Richtung, wie die Werte auf der x- oder y-Achse in einem Diagramm. Sie stellen keien Dimension dar. Was du sicherlich meinst ist eine Verschachtelung von Objekten, oder anders ausgedrückt, dass der Wert einer Eigenschaft eines Objekts ein anderes Objekt ist (auf ein anderes Objekt referenziert).

          Natürlich kann man auch als Eigenschaftennamen fortlaufende Nummern nehmen. Das System lässt so etwas zu. Aber da sollte man sich eine ganz gute Begründung für diese Vorgehensweise einfallen lassen, und warum man dafür kein Array verwendet hat.

          var data = {};
          data["1"] = "Pulli";
          data["2"] = "Hose";
          

          "data" ist ein ganz ungünstiger Name für eine Variable. Menschliche Verständlichkeit ist sehr wichtig beim Code schreiben. Man sollte wenigstens die Intention hinter den Dingen aus deren Namen ableiten können, erläuternde Kommentare schreibt man ja eh keine ...

          Was also ist dein konkreter Anwendungsfall für dieses Beispiel? Ist das eine Liste von irgendwelchen Kleidungsstücken oder ist sind das die eigentlich konkret benennbaren Bestandteile eines bestimmten Anzugs? Du erstellst da gerade einen Mischmasch aus Objekt und den bei Arrays üblichen für Index-Nummern.

          var dataAll = {data:{auftragnr:auftragnr,knr:knr},artikel:data};
          

          kann ich das in JS nicht wie bei PHP aufbauen/zusammenführen

          $dataAll["data"]["auftragnr"] = auftragnr;
          $dataAll["data"]["knr"] = knr;
          $dataAll["artikel"]["1"] = pulli;
          $dataAll["artikel"]["2"] = hose;
          

          Doch, genauso kann man das auch in Javascript. PHP lässt einem hier die Wahl. Man kann das, was eigentlich ein Objekt darstellt, auch mit Verwendung eines assoziativen Arrays implementieren. In Javascript gibt es ja keine assoziativen Arrays (nur eine Zugriffsweise auf Objekteigenschaften, sie so ähnlich aussieht), da nimmt man gleich Objekte.

          var dataAll = {
            data:{
              auftragnr:auftragnr,
              knr:knr
            },
            artikel:data
          };
          

          Man sollte das neben der Verwendung aussagekräftiger Bezeichner auch leichter erfassbar formatieren.

          dedlfix.

          1. Hallo dedlfix

            In Javascript gibt es ja keine assoziativen Arrays.

            Dem wage ich zu widersprechen. ;-)

            Seit ECMAScript 2015 gibt es eine native Implementierung dieses Konzepts mit dem Namen Map, bei der Instanzen durch den Aufruf des gleichnamigen Konstruktors erzeugt werden. Bei einer Map können sowohl die Werte als auch die Schlüssel von einem beliebigen Datentyp sein, da die Einträge, anders als bei Arrays oder planen Objekten, nicht als Objekteigenschaften angelegt werden.

            Maps

            Vielmehr ist es so, dass die Einträge einer Map in einer internen, mit der jeweiligen Map verknüpften Liste gespeichert werden und entsprechend erfolgt der Zugriff auf die hinterlegten Einträge hier auch nicht wie bei Objekteigenschaften über die Punkt- oder Klammernotation, sondern über bestimmte, für diesen Zweck vorgesehene Methoden.


            Anders als bei einigen anderen Features dieser Edition des Standards, sieht es bei der Kompatibilität auch schon relativ gut aus, aber es ist dennoch anzuraten, bei der gegenwärtigen Verwendung von Maps ein Polyfill bereitzustellen. Da zu vermuten ist, dass Ausführungsumgebungen, die diese Datenstruktur nicht unterstützten, auch keine Kompatibilität mit Sprachelementen bieten, die das Iteration Protocol implementieren, sollte jedoch auf den Gebrauch der entsprechenden Funktionalität im Zusammenhang mit Maps vorerst vermutlich besser verzichtet werden. Der Nachbau der Basisfunktionalität für ein Polyfill ist jedenfalls nicht übermäßig kompliziert, weshalb ich davon ausgehe, dass eine entsprechende Suche zu dem Thema einige Ergebnisse zu Tage bringen würde.


            Sehen wir uns nun aber einmal etwas genauer an, wie mit Maps gearbeitet werden kann, welche Methoden von der Standardbibliothek bereitgestellt werden und was bei der Verwendung zu beachten ist.

            const instance = new Map;
            
            console.log(instance); // [object Map]
            

            Hier wäre zunächst zu erwähnen, dass Maps durch den Aufruf des Konstruktors Map erzeugt werden, wobei aber zu beachten ist, dass Map auch wirklich als Konstruktor aufgerufen werden muss, also entweder über den Operator new oder über die Methode construct des Standardobjektes Reflect, welches jedoch ebenfalls erst seit der sechsten Edition der Sprache ein Teil des Standards ist. Wird der Konstruktor Map als gewöhnliche Funktion aufgerufen, dann wird hierdurch ein Typfehler produziert.

            const crew = new Map([
              ['Picard', 'Jean-Luc'],
              ['Riker', 'William']
            ]);
            

            Soll die Mapinstanz bei ihrer Erzeugung mit Einträgen initialisiert werden, dann können diese beim Aufruf des Konstruktors in einem iterierbaren Objekt übergeben werden, also etwa wie in dem Beispiel oben als Elemente eines Arrays. Dabei sind die einzelnen Einträge wiederum in Array-ähnlichen Objekten zu übergeben, deren erstes Element den Schlüssel spezifiziert und das zweite Element den Wert. Die Einträge werden hierbei prinzipiell in der selben Reihenfolge in die interne Liste der Map eingefügt, wie sie in der als Argument übergebenen Datenstruktur vorliegen, bei der es sich übrigens auch um eine andere Map handeln kann, da Maps ebenfalls iterierbare Objekte sind.

            const classes = new Map( )
            .set('Galaxy', [
              'Enterprise',
              'Yamato',
              'Odyssey'
            ])
            .set('Nebula', [
              'Endeavour',
              'Phoenix',
              'Sutherland'
            ]);
            

            Nach der Erzeugung der Map können Einträge nur noch einzeln hinzugefügt werden, durch den Aufruf der Mapmethode set, welche als erstes Argument den Schlüssel und als zweites Argument den Wert erwartet. Der Rückgabewert der Methode ist dabei grundsätzlich die Map auf der sie aufgerufen wurde, was recht praktisch ist, weil hierdurch wie in dem Beispiel oben mehrere Aufrufe verkettet werden können.

            const engineers = new Map([
              ['Scott', 'Montgomery'],
              ['La Forge', 'Geordi']
            ]);
            
            console.log(engineers.get('Scott')); // Montgomery
            

            Das Pendant zu set für den lesenden Zugriff auf einen Eintrag ist die Methode get, die als Argument den Schlüssel erwartet und falls vorhanden den dazugehörigen Wert zurückgibt. Gibt es keinen Eintrag für den angegebenen Schlüssel, dann wird der Wert undefined zurückgegeben.

            const medics = new Map([
              ['McCoy', 'Leonard'],
              ['Crusher', 'Beverly']
            ]);
            
            console.log(medics.has('McCoy')); // true
            

            Ob in einer Map ein bestimmter Eintrag existiert, kann mit der Methode has ermittelt werden, welcher der jeweilige Schlüssel als Argument übergeben wird und die als Ergebnis der Prüfung einen booleschen Wert zurückgibt.

            const androids = new Map([
              ['Dr.Soong', 'Lore']
              ['Dr.Soong', 'Data']
            ]);
            
            console.log(androids.get('Dr.Soong')); // Data
            

            Es ist allerdings grundsätzlich zu beachten, dass alle in einer Map verwendeten Schlüssel individuell sein müssen. Wenn bei der Initialisierung der Map oder beim späteren Hinzufügen eines Eintrags durch die Methode set ein Schlüssel angegeben wird, zu dem in der Map bereits ein Eintrag existiert, dann wird der alte Eintrag durch den neuen Eintrag überschrieben.

            const states = new Map( )
            .set('Federation', [
              'Humans',
              'Vulcans',
              'Bolians'
            ])
            .set('Dominion', [
              'Founders',
              'Jem’Hadar',
              'Vorta'
            ]);
            
            console.log(states.size); // 2
            

            Darüber hinaus gibt es bei Maps – ähnlich der Eigenschaft length bei Arrays – eine Eigenschaft mit dem Namen size, welche die Anzahl der in einer Map enthaltenen Einträge zurückgibt. Dabei handelt es sich jedoch nicht um eine eigene Eigenschaft der Instanzen, sondern size ist als Getter auf Map.prototype definiert und wird lediglich im Kontext der jeweiligen Map aufgerufen. Da es keinen dazugehörigen Setter gibt, kann die Eigenschaft nicht gesetzt sondern nur gelesen werden.

            const crew = new Map([
              ['Tasha', 'Yar']
            ]);
            
            const Armus = member => crew.delete(member);
            
            console.log(Armus('Tasha')); // true
            

            Soll ein einzelner Eintrag aus einer Map entfernt werden, dann ist hierfür die Methode delete zu verwenden, welche den Schlüssel für den zu löschenden Eintrag als Argument erwartet. Abhängig davon, ob es in der Map einen Eintrag für den angegebenen Schlüssel gab der gelöscht werden konnte, wird entweder true oder false zurückgegeben.

            const starfleet = new Map([
              [57301, 'Chekov'],
              [65491, 'Kyushu'],
              [62043, 'Melbourne'],
              [31911, 'Saratoga'],
              [62095, 'Tolstoy']
            ]);
            
            function wolf359 (federation, borg) {
              if (borg) {
                federation.clear( );
              }
            }
            
            wolf359(starfleet, 'Cube');
            
            console.log(starfleet.size); // 0
            

            Wird die Methode clear auf einer Map aufgerufen, dann werden alle Einträge der Map auf einmal gelöscht. Der Rückgabewert von clear ist grundsätzlich der Wert undefined.

            Damit hätten wir im Prinzip den Großteil der eingebauten Funktionalität abgehandelt. Was nun noch bleibt, ist das Thema Iteration, das ja auch von einigem Interesse ist. Hierbei ist anzumerken, dass für Maps standardmäßig drei verschiedene Iterable Interfaces bereitsgestellt werden, und zwar durch die Methoden entries, keys und values, wobei sich die hierbei ausgegebenen Werte aus den Namen der Methoden herleiten lassen. Das Default Interface wird bei Maps übrigens durch die Methode entries gestellt, das heißt, wenn etwa bei der Verwendung einer Schleife mit for und of nur eine Referenz auf eine Map übergeben wird, dann wird die Schleifenvariable mit den Einträgen der Map initialisiert, in Form von Arrays mit zwei Elementen.

            const ships = new Map([
              [2021, 'Farragut'],
              [2893, 'Stargazer']
            ]);
            
            for (let name of ships.values( )) {
              console.log(name); // Farragut, Stargazer
            }
            

            Soll also mit einer for-of-Schleife beispielsweise nur über die Werte einer Map iteriert werden, dann ist die Methode values aufzurufen, welche ein Iteratorobjekt zurückgibt, dessen Methode next beim internen Aufruf durch die Schleife nur die Werte weiterreicht. Soll hingegen nur über die Schlüssel der Map iteriert werden, wäre entsprechend die Methode keys aufzurufen.

            Die for-of-Schleife stellt aber natürlich nicht die einzige Möglichkeit dar, um über eine Map zu iterieren, denn darüber hinaus gibt es auch noch eine Mapmethode forEach, welche im Prinzip genauso funktioniert wie die gleichnamige Methode, die von Array.prototype vererbt wird.

            map.forEach(function (value, key, map) { }, thisArg);
            

            Die Methode forEach erwartet als erstes Argument die Rückruffunktion und als optionales zweites Argument den Wert, in dessen Kontext die Funktion aufgerufen werden soll, was für jeden Eintrag der Map einmal passiert, in der Reihenfolge in der die Einträge der Map hinzugefügt wurden. Dabei wird die Rückruffunktion von der Methode forEach mit drei Argumenten aufgerufen, nämlich dem Wert des Eintrags, dem Schlüssel und einer Referenz auf die Map, über die iteriert wird.

            const assimilated = new Map( )
            .set('Jean-Luc', 'Picard')
            .set('Annika', 'Hansen');
            
            assimilated.forEach(function (value, key, map) {
              switch (value) {
                case 'Picard' :
                  map.delete(key);
                  map.set('Locutus', 'of Borg');
                  break;
                case 'Hansen' :
                  map.delete(key);
                  map.set('Seven', 'of Nine');
                  break;
                default : console.log(key); // Locutus, Seven
              }
            });
            

            Wird von der an forEach übergebenen Rückruffunktion ein Eintrag gelöscht, bevor dieser erreicht wurde, dann wird die Funktion für diesen Eintrag nicht mehr aufgerufen, es sei denn, er wird vor Beendingung der Methodenausführung der Map wieder hinzugefügt. Bei der Iteration werden also grundsätzlich auch solche Einträge berücksichtigt, die während der Ausführung an die Map übergeben wurden.


            Es bleibt also festzuhalten, dass es durchaus auch in ECMAScript eine native Implementierung von assoziativen Datenfeldern gibt, dass jedoch im Moment noch nicht alle für ein aktuelles Projekt relevanten Ausführungsumgebungen diesen Objekttyp unterstützen, weshalb Maps noch nicht ohne Netz und doppelten Boden verwendet werden können.

            Viele Grüße,

            Orlok

            1. Tach!

              In Javascript gibt es ja keine assoziativen Arrays.

              Seit ECMAScript 2015 gibt es eine native Implementierung dieses Konzepts mit dem Namen Map, bei der Instanzen durch den Aufruf des gleichnamigen Konstruktors erzeugt werden. Bei einer Map können sowohl die Werte als auch die Schlüssel von einem beliebigen Datentyp sein, da die Einträge, anders als bei Arrays oder planen Objekten, nicht als Objekteigenschaften angelegt werden.

              Mit anderen Worten, man hat mit viel Aufwand etwas implementiert, was gefühlt 99,9% der Anwender bereits mit herkömmlichen Javascript-Objekten realisieren können und das zu einem ebenso hoch vermuteten Anteil auch weiterhin so realisiert werden wird, weil für diese Anwendungsfälle Strings als Keys reichen.

              dedlfix.

              1. Hallo dedlfix

                In Javascript gibt es ja keine assoziativen Arrays.

                Seit ECMAScript 2015 gibt es eine native Implementierung dieses Konzepts mit dem Namen Map, bei der Instanzen durch den Aufruf des gleichnamigen Konstruktors erzeugt werden. Bei einer Map können sowohl die Werte als auch die Schlüssel von einem beliebigen Datentyp sein, da die Einträge, anders als bei Arrays oder planen Objekten, nicht als Objekteigenschaften angelegt werden.

                Mit anderen Worten, man hat mit viel Aufwand etwas implementiert, was gefühlt 99,9% der Anwender bereits mit herkömmlichen Javascript-Objekten realisieren können und das zu einem ebenso hoch vermuteten Anteil auch weiterhin so realisiert werden wird, weil für diese Anwendungsfälle Strings als Keys reichen.

                Das ist ziemlicher Unsinn und das wüsstest du selbst, wenn du dir die Mühe gemacht hättest, mehr als bloß den ersten Absatz meines Beitrags zu lesen und ein wenig darüber nachzudenken. Schließlich gibt es eine Menge Unterschiede zwischen Maps und gewöhnlichen Objekten und es ist keinesfalls so, dass die Möglichkeit, Schlüssel mit anderen Datentypen als String und Symbol zu verwenden, der einzige Vorteil wäre, den Maps gegenüber planen Objekten bieten würden.

                Die Frage, wann eine Map und wann ein gewöhnliches Objekt zu verwenden ist, lässt sich nicht pauschal beantworten, sondern die Antwort hängt von den Voraussetzungen des konkreten Anwendungsfalls ab, wobei der Datentyp der Schlüssel nur ein Aspekt ist, den es hierbei zu berücksichtigen gilt. Andere Aspekte wären zum Beispiel Performanz oder die Fragen, ob der Datensatz zur Laufzeit manipuliert werden soll oder nicht, ob gegebenenfalls nur auf einzelnen Einträgen operiert werden soll oder ob entsprechende Operationen mehrere oder gar alle Einträge betreffen und falls letzteres, ob die Reihenfolge der Einträge dabei eine Rolle spielt.

                Nur weil assoziative Arrays bislang in Ermangelung einer nativen Implementierung in der Regel über plane Objekte realisiert wurden, bedeutet das nicht, dass eine solche Lösung unproblematisch wäre, das heißt, die Verwendung gewöhnlicher Objekte zu diesem Zweck war eigentlich schon immer eine Notlösung, die mit einigen Makeln behaftet ist. Zwar lassen sich die meisten Klippen umschiffen, aber eben nicht alle, und entsprechende Maßnahmen sind zwangsläufig entweder mit einer Einschränkung der Nutzbarkeit oder einer Erhöhung der Komplexität verbunden. Das ist der Grund, weshalb Maps eingeführt wurden.

                So ist bei der Verwendung von gewöhnlichen Objekten zum Beispiel zu berücksichtigen, dass diese standardmäßig Eigenschaften und Methoden über die Prototypenkette erben. Es besteht also grundsätzlich das Risiko, dass der Datensatz auf diese Weise kontaminiert wird. Natürlich sind die Eigenschaften und Methoden, die von Object.prototype vererbt werden, nicht abzählbar und es ist auch unwahrscheinlich, dass in einem Datensatz Schlüssel verwendet werden, die mit deren Namen identisch sind, aber es ist in vielen Fällen dennoch geboten, diesem Umstand Rechnung zu tragen.

                // dictionary pattern
                
                const object = Object.create(null);
                

                Will man das Risiko also ausschließen, dass der Datensatz durch geerbte Eigenschaften kontaminiert wird, dann bleiben einem nur zwei Handlungsweisen, nämlich entweder bei jeder Operation auf den Daten zunächst zu prüfen, ob es sich um eine eigene Eigenschaft des jeweiligen Objektes handelt, was mit der Methode hasOwnProperty bewerkstelligt werden kann, oder aber bei der Erzeugung des Objektes explizit den Wert null als Prototyp zu bestimmen, was unter Verwendung der Methode create umzusetzen wäre. Letzteres hat jedoch den Nachteil, dass die komfortable Notierung der Einträge in einem Objektliteral ausscheidet und die Befüllung des Objektes mit Einträgen auf andere Weise erfolgen muss.

                Wird hingegen eine Map verwendet, dann braucht man sich über dieses Thema grundsätzlich keine Gedanken zu machen, da die Einträge hier wie gesehen nicht als Objekteigenschaften angelegt werden. Abhängig von den Umständen des konkreten Anwendungsfalls, kann dies also durchaus ein gewichtiges Argument für den Einsatz von Maps darstellen.

                Ein weiterer Unterschied zwischen Maps und gewöhnlichen Objekten besteht darin, dass es für plane Objekte keine eingebaute Eigenschaft oder Methode gibt, um auf direktem Wege die Anzahl der Einträge, also der eigenen Eigenschaften zu ermitteln. Zwar existieren verschiedene Möglichkeiten, um an diese Information zu gelangen, aber die entsprechende Funktionalität muss eben eigenhändig implementiert werden, wohingegen es bei Maps genügt, den Wert der Eigenschaft size zu lesen.

                Darüber hinaus ist zu berücksichtigen, dass bei planen Objekten nicht garantiert werden kann, dass die Eigenschaften in derselben Reihenfolge ausgegeben werden, in der sie angelegt wurden. Man kann sich also nicht darauf verlassen, dass beispielsweise von einer for in Schleife oder einer der Methoden zur Examinierung der Schlüssel eines Objektes, die ursprüngliche Reihenfolge eingehalten wird.

                Das ist bei Maps grundsätzlich anders, denn wie ich in meiner ersten Antwort bereits schrieb, werden die Einträge hier prinzipiell in der Reihenfolge in der internen Liste gespeichert, in welcher sie der Map hinzugefügt wurden, und bei einer Ausgabe, entweder beim Kopieren in ein Array oder eine andere Datenstruktur, oder bei der Iteration mit einer for of Schleife, bleibt die Reihenfolge erhalten.

                Ist die Reihenfolge der Einträge eines entsprechenden Datensatzes also von Belang, dann ist dies ein sehr starkes Argument, das für dein Einsatz einer Map anstelle eines planen Objektes spricht.

                Aber auch unabhängig von diesem Aspekt, gestaltet sich die Arbeit mit Maps wesentlich einfacher, wenn Operationen auf dem ganzen Datensatz durchgeführt werden sollen und entsprechend über die Einträge zu iterieren ist. Anders als bei gewöhnlichen Objekten, gibt es wie bereits erwähnt für Maps nämlich eine eingebaute Methode forEach, sodass hier eine Funktionalität bereitgestellt wird, die sonst nur für Arrays und Sets zur Verfügung steht, nicht jedoch für plane Objekte. Das heißt, auch in diesem Fall kann natürlich dasselbe Ziel erreicht werden, wenn man ein gewöhnliches Objekt verwendet, aber die Umsetztung ist weit weniger komfortabel.

                Dies gilt allerdings generell für den Fall, dass Operationen nicht nur auf einzelnen Einträgen durchgeführt werden sollen. So ist auch zu berücksichtigen, dass es sich bei Maps, ebenso wie bei Arrays und Sets um iterierbare Objekte handelt, wohingegen gewöhnliche Objekte standardmäßig nicht iterierbar sind. Das heißt, es ist ein Kinderspiel, etwa unter Verwendung des Spreadoperators die Einträge in ein Array zu kopieren, darauf zu operieren und mit dem Ergebnis dann wieder eine Map zu befüllen, während dieselben Aktionen bei einem gewöhnlichen Objekt mit ungleich größerem Aufwand verbunden wären.

                Schließlich scheinen Maps bereits jetzt bei den meisten Ausführungsumgebungen die sie unterstützen performanter zu sein als es plane Objekte sind und es ist zu erwarten, dass dieser Vorsprung in Zukunft eher noch wachsen wird, zumal in Bezug auf Maps vermutlich noch ein größeres Potential für Optimierungen besteht, als dies bei gewöhnlichen Objekten der Fall ist.

                Zusammenfassend lässt sich also festhalten, dass sich pauschale Aussagen darüber verbieten, welcher der beiden Techniken der Vorzug zu geben ist, da dies immer von den jeweiligen Anforderungen im Einzelfall abhängt. Als Faustregel lässt sich jedoch sagen, dass gewöhnlichen Objekten immer dann der Vorzug zu geben ist, wenn es sich nur um vergleichsweise wenige Daten handelt, die hinterlegt werden sollen und diese nach der Initialisierung auch nicht mehr groß verändert werden. Sind jedoch größere Operationen auf den Daten notwendig, dann spricht viel dafür, statt planer Objekte Maps zu verwenden, da sich die Arbeit mit diesen speziellen Objekten in vielen Punkten wesentlich einfacher gestaltet.

                Gruß,

                Orlok

      2. Tach!

        Die Frage lautete: "Wann nehme ich ein Object und wann ein Array." Natürlich kann man dazu auch eine Menge Dinge aufzählen, die ebenfalls mit Arrays möglich sind. Empfehlenswert sind diese Verwendungen jedoch nicht unbedingt.

        Ein Array verwaltet eine mehr oder weniger unbekannte Anzahl üblicherweise gleichartiger Dinge über einen numerischen Index.

        Da kommt’s also auf die Reihenfolge an; ["John", "Doe", 46] ist was anderes als ["Doe", 46, "John"].

        Für diese Speicherung unterschiedlicher Eigenschaften sollte man ein Array nicht verwenden. Die Verständlichkeit leidet sehr darunter, weil aus einem person[0] nicht hervorgeht, welche Eigenschaft das ist. person.name ist deutlich einfacher zu verstehen und damit weniger fehleranfällig. Bei diesem Beispiel kommt es nicht auf die Reihenfolge an, sondern dass man solch eine Verwendung vermeidet.

        Ein Objekt hat meist eine genau feststehende Anzahl benannter Eigenschaften

        ??

        42. 23.

        (Unverständliche Antwort? Hmm, vielleicht habe ich die beiden Fragen missinterpretiert.)

        Bei Frauen gibt man das Alter nicht an. Oder doch. So feststehend ist die Anzahl benannter Eigenschaften nun auch nicht.

        Unsinnige Beispiele kann ich auch eine Menge anführen. In meiner Aussage repräsentiert das Wörtchen "meist" eine Einschränkung der Allgemeingültigkeit. Natürlich kann man auch eine unbekannte Anzahl Eigenschaften hinzufügen, das ist bei Javascript im Gegensatz zu kompilierten Sprachen kein Problem. Anderswo nimmt man dann Dictionarys/Hashmaps. Die Beschränkung auf eine bestimmte Anzahl an Elementen ergibt sich dann aus einem höheren Kontext.

        Für einen allgemein gehaltenen HTTP-Client ist die Anzahl der Headerzeilen auf den ersten Blick unbegrenzt. Die Beschränkung ergibt sich jedoch aus der RFC, in der nur eine konkrete Anzahl an Headern definiert ist. Beziehungsweise aus der konkreten Anwendung, die eine begrenzte Anzahl selbst definierter Header hinzufügt. Natürlich kann man noch weitere Phantasie-Header hinzufügen, aber wo wäre da der Anwendungsfall?

        dedlfix.

      3. Hallo Gunnar Bittersmann,

        Ein Objekt hat meist eine genau feststehende Anzahl benannter Eigenschaften

        ??

        Da die Objekte von Klassen erzeugt werden und in dieser Klasse die entsprechenden Eigenschaften „vorbereitet“ sind, liegt die Anzahl der Eigenschaften tatsächlich fest. Dass es (möglicherweise von Sprache zu Sprache unterschiedlich) mehrere Konstruktoren geben kann, die dem Objekt nur bestimmte Eigenschaften mitgeben, ist davon unabhängig.

        var jane = {firstName:"Jane", lastName:"Roe"};
        

        Wenn man es nicht darauf anlegt, hat das Objekt jane dennoch eine Eigenschaft age.

        Bis demnächst
        Matthias

        --
        Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
        1. @@Matthias Apsel

          Da die Objekte von Klassen erzeugt werden

          Klassen? Wir reden hier schon über JavaScript, oder?

          und in dieser Klasse die entsprechenden Eigenschaften „vorbereitet“ sind,

          Wo ist da was „vorbereitet“?

          var jane = {firstName:"Jane", lastName:"Roe"};
          

          Wenn man es nicht darauf anlegt, hat das Objekt jane dennoch eine Eigenschaft age.

          console.log(jane.age); // undefined
          

          LLAP 🖖

          --
          “You might believe there are benefits for the developer, but first of all, you should put those behind the interest of the user.” —Stefan Tilkov
          Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
          1. Hallo Gunnar Bittersmann,

            Da die Objekte von Klassen erzeugt werden

            Klassen? Wir reden hier schon über JavaScript, oder?

            var jane = {firstName:"Jane", lastName:"Roe"};
            

            Wenn man es nicht darauf anlegt, hat das Objekt jane dennoch eine Eigenschaft age.

            console.log(jane.age); // undefined
            

            Dass hier Variablen, die eigentlich vom Datentyp record sind, als Objekte bezeichnet werden, ist aber gar nicht schön. Wahrscheinlich weil: „Ein Verbund (englisch object composition) ist ein Datentyp der aus einem oder mehreren Datentypen zusammengesetzt wurde.“ (Wikipedia)

            Bis demnächst
            Matthias

            --
            Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
          2. Tach!

            Da die Objekte von Klassen erzeugt werden

            Klassen? Wir reden hier schon über JavaScript, oder?

            und in dieser Klasse die entsprechenden Eigenschaften „vorbereitet“ sind,

            Wo ist da was „vorbereitet“?

            Die Klasse ergibt sich aus der Aufgabenstellung. Sie ist nicht physisch vorhanden, aber im Prinzip entspricht dieses Objekt in dem konkreten Beispiel einem aus einer Klasse mit definierten Eigenschaften abgeleiteten Objekt. In modernem Javascript (und in TypeScript) würde man das als Klasse implementieren. Ein als Dictionary/Hashmap verwendetes Objekt würde ich im Gegensatz dazu wohl eher nicht als Klasse ansehen, weil das eher einem assoziativen Array entspricht. Allerdings kann man auch ein Dictionary als Klasse implementieren, die außer dem "Array"-Teil (ja, in Anführungszeichen) noch ein paar feststehende Eigenschaften und Methoden hat.

            dedlfix.