mb: variationen an Objekten-deklaration! Wann ist welche Sinnvoll?

schönen guten Morgen,

wann ist es sinniger eines der beiden im Quellcode zunehmen???

var irgendwas = { eins : "eins", zwei : "zwei" }

oder

var irgendwas = ["eins","zwei","..."];

oder

var irgendwas = new Array();
irgendwas[0]  = "irgendwer";
irgendwas[1]  = "...";

Ich denke man kann alles in allen variationen lösen aber wie ist es eleganter? Danke im voraus. Liebe Grüße MB

PS: ich hab gehört das n numerisches Array auch assoziatives Array ist eben einfach n numerischer String-Index

array["nummer"] = "Wert";
  1. Hallo

    wann ist es sinniger eines der beiden im Quellcode zunehmen???

    var irgendwas = { eins : "eins", zwei : "zwei" }
    

    oder

    var irgendwas = ["eins","zwei","..."];
    

    Jeweils unter Verwendung der Literalschreibweise erzeugst du in deinem ersten Beispiel ein Objekt und in deinem zweiten Beispiel ein Array, aber wie du im Titel deines Beitrags schon bewusst (oder unbewusst) festgestellt hast, sind Arrays in JavaScript ebenfalls Objekte.

    Das heißt, mit var obj = { } erzeugst du eine Instanz von Object.prototype und mit var arr = [ ] eine Instanz von Array.prototype, wobei Array wiederum von Object erbt.

    oder

    var irgendwas = new Array();
    irgendwas[0]  = "irgendwer";
    irgendwas[1]  = "...";
    

    Hier rufst du die Funktion Array( ) als Konstruktor auf ohne Argumente zu übergeben, so dass in var irgendwas zunächst einmal ein leeres Array gespeichert wird, dessen Indizes 0 und 1 du dann jeweils einen String als Wert zuweist.

    Abgesehen davon, dass die Verwendung des Schlüsselwortes new hier überflüssig ist, da die Funktion Array( ) ohnehin ein neues Array als Rückgabewert hat (du hier also auch var irgendwas = Array( ) hättest schreiben können), dürfte auf den ersten Blick ersichtlich sein, dass diese Variante gegenüber der Literalnotation deutlich weniger „elegant“ ist.

    Das heißt, in der Regel sollte ein Array so erzeugt werden: var arr = [ ], beziehungsweise direkt mit Wertübergabe: var arr = ['eins', 'zwei', 'drei']

    Ich denke man kann alles in allen variationen lösen aber wie ist es eleganter?

    Die Frage ist hier nicht in erster Linie, was eleganter ist, sondern was im konkreten Einzelfall benötigt wird, ein Array oder ein Object.

    Zwar ist wie gesehen ein Array ebenfalls ein Objekt, aber Arrays verfügen über Eigenschaften und Methoden, über die plain objects nicht verfügen. Beispiel:

    var arr = [1, 2, 3];
    console.log(arr.length); // 3
    
    var obj = { '1' : 1, '2' : 2, '3' : 3 };
    console.log(obj.length); // undefined
    

    Bedenke: Es gibt in JavaScript keine assoziativen Arrays. Das heißt, du kannst, weil Arrays ja auch Objekte sind, zwar schreiben…

    var arr = [ ];
    arr['Hallo'] = 'Welt';
    

    …aber arr.length bliebe bei 0 und arr[0] bei undefined, dass heißt, letztlich würdest du hier nur ein Array wie ein Objekt verwenden, weshalb du in diesem Fall auch gleich ein Objekt verwenden könntest und solltest.

    Grundsätzlich gilt also: Hast du eine Anzahl verschiedener Werte, auf welche du über einen numerischen Index zugreifen kannst, verwende ein Array, ansonsten ein Objekt.

    Gruß,

    Orlok

    1. Hallo

      dazu noch eine Ergänzung:

      var arr = [ ];
      arr['Hallo'] = 'Welt';
      

      …aber arr.length bliebe bei 0 und arr[0] bei undefined, dass heißt, letztlich würdest du hier nur ein Array wie ein Objekt verwenden, weshalb du in diesem Fall auch gleich ein Objekt verwenden könntest und solltest.

      wenn der Bezeichner eines Elements in einem Objekt (z.B.) ein Leerzeichen enthält, geht

      o.ein Element
      

      nicht,

      o["ein Element"]
      

      aber schon.

      Gruß Jürgen

      1. Tach!

        dazu noch eine Ergänzung: wenn der Bezeichner eines Elements in einem Objekt (z.B.) ein Leerzeichen enthält, geht

        o.ein Element
        

        nicht,

        o["ein Element"]
        

        aber schon.

        Ja, aber das zweite ist auch kein Array-Zugriff, sondern eine Alternative, wie man auf eine Eigenschaft eines Objekts zugreift, deren Namen man als Wert oder Literal vorliegen hat. Mit anderen Worten (um das nochmal zu verdeutlichen), man braucht kein Array für das was man auch assoziativen Zugriff nennt.

        dedlfix.

    2. Hallo Orlok,

      Grundsätzlich gilt also: Hast du eine Anzahl verschiedener Werte, auf welche du über einen numerischen Index zugreifen kannst, verwende ein Array, ansonsten ein Objekt.

      Verstanden, Danke Dir. Wie gesagt komme aus der Java Ecke.

      Herzlichsen Gruß, MB

    3. Das heißt, mit var obj = { } erzeugst du eine Instanz von Object.prototype und mit var arr = [ ] eine Instanz von Array.prototype, wobei Array wiederum von Object erbt.

      Das hast du unglücklich formuliert, denn Instanzen werden in JavaScript von Konstruktor-Funktionen gebildet, nicht Objekt-Prototypen:

      [1,2,3] instanceof Array.prototype // Uncaught TypeError: Expecting a function in instanceof check, but got #<Object>
      [1,2,3] instanceof Array // true
      

      Dennoch gilt natürlich, dass sowohl Array.prototype als auch Object.prototype in der Prototypkette von [1,2,3] auftauchen:

      Array.prototype.isPrototypeOf([1,2,3]) // true
      Object.prototype.isPrototypeOf([1,2,3]) // true
      
      1. Hallo

        Das heißt, mit var obj = { } erzeugst du eine Instanz von Object.prototype […]

        Das hast du unglücklich formuliert […]

        Das habe ich unpräzise formuliert. Eigentlich sogar falsch. Aber nicht „unglücklich“! ;-)

        Instanzen werden in JavaScript von Konstruktor-Funktionen gebildet.

        So ist es. Aber bloß auf den Konstruktor zu verweisen würde der Sache auch nicht wirklich gerecht, oder?

        Nehmen wir also einmal an, wir würden eines Morgens aus unruhigen Träumen erwachen und die folgende Funktion notieren…

        var Kaefer = function (name) {
          this.name = name;
          this.typ = 'Ungeziefer';
          this.farbe = 'braun';
        };
        

        …und diese sogleich als Konstruktor aufrufen:

        var gregor = new Kaefer('Gregor');
        

        Dann wäre es absolut nachvollziehbar zu sagen, dass gregor ein Kaefer ist und es sich hierbei also um eine Instanz des Konstruktors handelt:

        var constructor = gregor.constructor; // function Kaefer()
        

        oder

        var check = gregor instanceof Kaefer; // true
        

        Indem wir Kaefer als Konstruktor aufrufen, erzeugen wir ein neues leeres Object, welches wir innerhalb der Konstruktorfunktion über this referenzieren können. Das heißt, wenn wir direkt im Konstruktor Eigenschaften und Methoden an die Variable this knüpfen, dann sind dies, sofern wir die Funktion auch tatsächlich als Konstruktor aufrufen, Eigenschaften und Methoden des erzeugten Objektes selbst:

        var ungeziefer = gregor.hasOwnProperty('typ'); // true
        

        Würden wir hingegen das Schlüsselwort new bei unserem Funktionsaufruf vergessen, dann hätten wir unter Anderem ein paar neue globale Variablen produziert…

        Davon aber einmal abgesehen, hätten wir hier in diesem Fall auch gleich folgendes schreiben können:

        var kaefer = function (name) {
          return {
            name : name,
            typ : 'Ungeziefer',
            farbe : 'braun'
          };
        };
        
        var gregor = kaefer('Gregor');
        
        var ungeziefer = gregor.hasOwnPrototype('typ'); // true
        

        Wie dem auch sei, bezogen auf unseren Kaefer-Konstruktor besteht soweit jedenfalls kein Zweifel an dessen Rolle als "Identitätsstifter" hinsichtlich der von ihm erzeugten Objekte.

        Aber als Funktionsobjekt besitzt unser Konstruktor ja auch noch eine Eigenschaft namens prototype, bei der es sich ebenso um ein (beinahe) leeres Object handelt, welchem wir ebenfalls Eigenschaften und Methoden zuweisen können:

        var Kaefer = function (name) {
          this.name = name;
          this.typ = 'Ungeziefer';
          this.farbe = 'braun';
        };
        
        Kaefer.prototype.beruf = false;
        
        Kaefer.prototype.gesundheit = 0;
        
        Kaefer.prototype.bewerfen = function ( ) {
          this.gesundheit -= Math.ceil(Math.random( ) * 10);
        };
        

        Wenn wir nun also Kaefer als Konstruktor aufrufen, dann verfügt gregor nicht nur über die direkt in der Funktion deklarierten Eigenschaften, sondern ebenso über die Eigenschaften und Methoden, welche von uns in Kaefer.prototype hinterlegt wurden:

        var gregor = new Kaefer('Gregor');
        
        gregor.bewerfen( );
        gregor.bewerfen( );
        
        var status = gregor.gesundheit; // z.B. -7  :-(
        

        Zwischen den direkt im Konstruktor angelegten Objekteigenschaften und denjenigen, welche wir im Prototypobjekt des Konstruktors spezifiziert haben, besteht allerdings ein Unterschied:

        var farbe = gregor.hasOwnProperty('farbe'); // true
        
        var beruf = gregor.hasOwnProperty('beruf'); // false
        

        Bei den Eigenschaften, welche wir für das Prototypobjekt der Konstruktorfunktion angelegt hatten, handelt es sich also nicht um eigene Eigenschaften unseres Instanzobjektes, sondern um geerbte.

        Jedes Objekt in JavaScript hat eine interne Eigenschaft [[Prototype]], welche standardmäßig auf das prototype-Objekt seines Konstruktors verweist, und dem jeweiligen Objekt so dessen Eigenschaften und Methoden zur Verfügung stellt:

        var prototyp = Object.getPrototypeOf(gregor);
        

        Oder auch…

        var prototyp = gregor.__proto__;
        

        …wobei ich mir nicht sicher bin, ob letztere Schreibweise zum Zugriff auf die interne [[Prototype]] Eigenschaft nun überholt ist oder nicht, aber die Einordnung in der Spezifikation unter legacy features spricht dafür, am besten grundsätzlich die erste Variante zu verwenden. ;-)

        Jedenfalls hat auch der Prototyp eines Objektes selbst wiederum einen Prototyp, so dass eine Prototypenkette entsteht, die bei Object.prototype und schließlich bei null endet.

        Als plain object ist der Prototyp von Kaefer.prototype, in dem wir zusätzliche Eigenschaften hinterlegt haben, Object.prototype, aber das kann man natürlich auch ändern, weshalb wir zunächst ein neues Objekt erstellen…

        var properties = {
          nachname : 'Samsa',
        
          "familie" : ['Vater', 'Mutter', 'Grete'],
        
          beruf : 'Handelsreisender',
        
          hobbys : ['Zeitunglesen', 'Laubsägearbeiten', 'aus dem Fenster gucken'],
        
          typ : 'Mensch'
        };
        

        …und welches wir dann als Prototypen von Kaefer.prototype festlegen:

        Object.setPrototypeOf(Kaefer.prototype, properties);
        
        // Oder, wenn auch weniger empfehlenswert:
        
        Kaefer.prototype.__proto__ = properties;
        

        Nachdem wir dies also vollbracht haben, verfügt gregor nun über eine ganze Reihe an Eigenschaften, von denen lediglich name, typ und farbe eigene Eigenschaften sind, welche in der Konstruktorfunktion spezifiziert wurden, während der gesamte Rest über die Prototypenkette geerbt wurde.


        Dabei soll allerdings nicht unerwähnt bleiben, dass "nachträgliche" Eingriffe in die prototype chain teure Operationen sind, die hier nur aus dramaturgischen Gründen vorgeführt wurden: In der Regel sollte die Kette unter Verwendung von Object.create( ) von unten aufgebaut werden, indem man der genannten Methode ein geeignetes Prototypobjekt als Argument übergibt, welches dann dem zurückgegebenen neuen Objekt als Wert der [[Prototype]] Eigenschaft dient. Aber das nur nebenbei bemerkt.


        Jedenfalls sollten wir einen Blick auf ein paar Eigenschaften von gregor werfen:

        var beruf = gregor.beruf; // false
        

        Warum false und nicht 'Handelsreisender', wie wir es gerade bestimmt haben?

        Weil – ähnlich dem Scope von Variablen – nur dann in einem assoziierten Prototypobjekt nach einer entsprechenden Eigenschaft gesucht wird, wenn unter dem selben Bezeichner keine eigene Eigenschaft vorhanden ist.

        Das heißt, da die Eigenschaft beruf sowohl in Kaefer.prototype als auch in dessen Prototypobjekt properties hinterlegt ist, wird aus Sicht von gregor auf die nächste Eigenschaft in der Kette, also die in Kaefer.prototype bestimmte Eigenschaft beruf mit dem Wert false zugegriffen.

        Wenn wir diese Eigenschaft löschen…

        delete Kaefer.prototype.beruf;
        

        …wird beim nächsten Prototypen in der Kette nachgefragt und voilà…

        var beruf = gregor.beruf; // Handelsreisender
        

        Und selbstverständlich handelt es sich bei gregor nicht um 'Ungeziefer':

        delete gregor.typ;
        
        var check = gregor.typ; // 'Mensch' ;-)
        

        Tja, und wenn wir uns nun anschauen, was gregor an eigenen Eigenschaften geblieben ist, dann ist das nicht besonders viel, im Vergleich zu dem, was ihn sonst noch so ausmacht…

        var names = Object.getOwnPropertyNames(gregor); // ['name', 'farbe']
        

        …aber dennoch gilt nach wie vor:

        console.log(gregor instanceof Kaefer); // true :-/
        

        Also, was ist die Moral von der Geschichte?

        Es war kein Traum! Obwohl fast alle Eigenschaften von gregor aus der Prototypenkette geerbt wurden, ist er nach wie vor ein Kaefer, also eine Instanz seines Konstruktors

        Aber beschreibt das die Herkunft und Identität unseres Objektes wirklich zufriedenstellend?

        Wenn ich ein Array erzeuge, var arr = [ ], ist dann wirklich der Konstruktor Array( ) von Interesse oder nicht doch eher Array.prototype, die Quelle der Methoden, die ich auf meinem Array anweden will?

        Vielleicht ist der Begriff „Instanz“ im Zusammenhang mit der prototypischen Vererbung in JavaScript ganz einfach keine all zu treffende Bezeichnung. ;-)

        Wie auch immer…

        Gruß,

        Orlok

        1. Würden wir hingegen das Schlüsselwort new bei unserem Funktionsaufruf vergessen, dann hätten wir unter Anderem ein paar neue globale Variablen produziert…

          Dagegen beschützt dich der strict-Mode.

          Aber als Funktionsobjekt besitzt unser Konstruktor ja auch noch eine Eigenschaft namens prototype, bei der es sich ebenso um ein (beinahe) leeres Object handelt, welchem wir ebenfalls Eigenschaften und Methoden zuweisen können:

          var Kaefer = function (name) {
            this.name = name;
            this.typ = 'Ungeziefer';
            this.farbe = 'braun';
          };
          
          Kaefer.prototype.beruf = false;
          
          Kaefer.prototype.gesundheit = 0;
          
          Kaefer.prototype.bewerfen = function ( ) {
            this.gesundheit -= Math.ceil(Math.random( ) * 10);
          };
          

          Wenn wir nun also Kaefer als Konstruktor aufrufen, dann verfügt gregor nicht nur über die direkt in der Funktion deklarierten Eigenschaften, sondern ebenso über die Eigenschaften und Methoden, welche von uns in Kaefer.prototype hinterlegt wurden:

          var gregor = new Kaefer('Gregor');
          
          gregor.bewerfen( );
          gregor.bewerfen( );
          
          var status = gregor.gesundheit; // z.B. -7  :-(
          

          Zwischen den direkt im Konstruktor angelegten Objekteigenschaften und denjenigen, welche wir im Prototypobjekt des Konstruktors spezifiziert haben, besteht allerdings ein Unterschied:

          var farbe = gregor.hasOwnProperty('farbe'); // true
          
          var beruf = gregor.hasOwnProperty('beruf'); // false
          

          Bei den Eigenschaften, welche wir für das Prototypobjekt der Konstruktorfunktion angelegt hatten, handelt es sich also nicht um eigene Eigenschaften unseres Instanzobjektes, sondern um geerbte.

          Interessant in dem Zusammenhang ist, dass die Methode bewerfen bei ihrem ersten Aufruf eine neue Eigenschaft direkt auf der Instanz erzeugt, und nicht die Eigenschaft des Prototyps verändert:

          var gregor = new Kaefer('gregor');
          console.assert(gregor.hasOwnProperty('gesundheit') === false, 'Gesundheit ist eine direkte Eigenschaft von gregor');
          gregor.bewerfen();
          console.assert(gregor.hasOwnProperty('gesundheit') === true, 'Gesundheit ist keine direkte Eigenschaft von gregor');
          

          Die Meldungen sind etwas konfus, weil sie an der Stelle Fehlermeldungen darstellen und keine Erläuterungen. Sie beschreiben also den negativen Fall, dass die Assertions fehlschlagen, nicht den positiven Fall (hier fallen beide Assertions positiv aus)

          Eine unmittelbare Auswirkung ist, dass die bewerfen-Methode die Zustände anderer Kaefer-Instanzen unverändert lässt.

          Nachdem wir dies also vollbracht haben, verfügt gregor nun über eine ganze Reihe an Eigenschaften, von denen lediglich name, typ und farbe eigene Eigenschaften sind, welche in der Konstruktorfunktion spezifiziert wurden, während der gesamte Rest über die Prototypenkette geerbt wurde.

          Bis auf solche Eigenschaften, die durch Duck typing eben noch zur Laufzeit hinzukommen können, wie im oberen Fall geschildert. gesundheit ist zunächst keine eigene Eigeschaft von Kaefer-Instanzen, wird aber dazu, sobald die bewerfen-Methode aufgerufen wird.

          1. Hallo

            Würden wir hingegen das Schlüsselwort new bei unserem Funktionsaufruf vergessen, dann hätten wir unter Anderem ein paar neue globale Variablen produziert…

            Dagegen beschützt dich der strict-Mode.

            Ja, mich schon, aber nicht denjenigen, der vielleicht ohne Kenntnis dieses Features den Code aus meinen Beispielen übernimmt; Deshalb wollte ich es nicht unerwähnt lassen… ;-)

            Obwohl ein entsprechender Hinweis auf den strict-Mode hier sicher nicht geschadet hätte – da hast du vollkommen recht! Eine kurze Erklärung für den interessierten Mitleser sei also nachgereicht:

            Nehmen wir also einmal an, wir haben die folgende als Konstruktor bestimmte Funktion…

            var Constructor = function ( ) {
              this.property = 'value';
            };
            

            …und würden sie ohne das Schlüsselwort new aufrufen…

            Constructor( );
            

            …dann würde this auf das globale Objekt, also window verweisen und property wäre eine globale Variable:

            console.log(property); // value
            
            console.log(!!window.property); // true
            

            Würden wir aber statt dessen zum Beispiel schreiben…

            var Constructor = function ( ) {
              'use strict';
              this.property = 'value';
            };
            

            …dann würde this beim Funktionsaufruf ohne new nicht mehr auf window zeigen, sondern mit undefined initialisiert werden, wobei die Eigenschaftszuweisung dann einen type error produziert, wodurch das Programm abgebrochen und der Fehler beim Aufruf der Funktion sofort offensichtlich wird:

            Constructor( ); // type error: this is undefined
            

            Use strict!

            Das also dazu. ;-)


            Im Folgenden mein etwas gekürztes Ursprungsbeispiel:

            
            var Kaefer = function (name) {
              this.name = name;
              this.typ = 'Ungeziefer';
              this.farbe = 'braun';
            };
            
            Kaefer.prototype.gesundheit = 0;
            
            Kaefer.prototype.bewerfen = function ( ) {
              this.gesundheit -= Math.ceil(Math.random( ) * 10);
            };
            
            var gregor = new Kaefer('Gregor');
            
            gregor.bewerfen( );
            
            var status = gregor.gesundheit; // z.B. -7  :-( 
            

            Bei den Eigenschaften, welche wir für das Prototypobjekt der Konstruktorfunktion angelegt hatten, handelt es sich also nicht um eigene Eigenschaften unseres Instanzobjektes, sondern um geerbte.

            Interessant in dem Zusammenhang ist, dass die Methode bewerfen bei ihrem ersten Aufruf eine neue Eigenschaft direkt auf der Instanz erzeugt, und nicht die Eigenschaft des Prototyps verändert […]

            Vielen Dank für den Hinweis! Das ist mir im Eifer des Gefechts gar nicht aufgefallen, aber das wäre definitiv einer Erwähnung wert gewesen, zumal das nicht nur „interessant“ ist, sondern eine fundamentale Voraussetzung dafür, dass dieses Prinzip der Vererbung überhaupt zuverlässig und vor allem praktikabel funktioniert, denn wie du treffend bemerkt hast:

            Eine unmittelbare Auswirkung ist, dass die bewerfen-Methode die Zustände anderer Kaefer-Instanzen unverändert lässt.

            Dadurch, dass bei der Zuweisung eines Wertes zu einer geerbten Eigenschaft – in diesem Fall also der Eigenschaft gesundheit von Kaefer.prototype – automatisch eine neue eigene Eigenschaft der Instanz – hier also gregor – erzeugt wird, wird sichergestellt, das sich Veränderungen nur lokal auswirken, sprich, wäre dem nicht so, müsste man immer die Herkunft einer Eigenschaft berücksichtigen und im Zweifel explizit eine neue eigene Eigenschaft definieren.

            Dazu gleich noch etwas mehr.


            Aber zunächst noch eine Randbemerkung zu deinem Code-Beispiel:

            var gregor = new Kaefer('gregor');
            console.assert(gregor.hasOwnProperty('gesundheit') === false, 'Gesundheit ist eine direkte Eigenschaft von gregor');
            gregor.bewerfen();
            console.assert(gregor.hasOwnProperty('gesundheit') === true, 'Gesundheit ist keine direkte Eigenschaft von gregor');
            

            Die Meldungen sind etwas konfus, weil sie an der Stelle Fehlermeldungen darstellen und keine Erläuterungen. Sie beschreiben also den negativen Fall, dass die Assertions fehlschlagen, nicht den positiven Fall (hier fallen beide Assertions positiv aus)

            Zur asynchronen Überwachung von Objekten gibt es eine bessere Methode als console.assert, die aber leider noch nicht offizieller ECMAScript-Standard ist und die auch noch nicht browserübergreifend funktioniert, nämlich Object.observe; Sowie zur Beendigung der Überwachung, die Schwestermethode Object.unobserve; Siehe hierzu den entsprechenden Vorschlag.

            Die Syntax: Object.observe( object, callback [, array] );

            Das erste Argument ist das Objekt, welches überwacht werden soll, das zweite Argument ist eine Callback-Funktion, die bei eingetretener Veränderung aufgerufen wird, und das optionale dritte Argument ist ein Array, welches die Bezeichner der zu berücksichtigenden Veränderungen als Strings enthält.

            Der Callback-Funktion wird bei ihrem Aufruf als Parameter ein Array übergeben, welches für jede erfolgte Veränderung ein Objekt enthält, dem wiederum sachdienliche Informationen entnommen werden können.

            Bis dato beschränkt auf Chrome und Opera, könnte man für unseren Fall also auch schreiben:

            var gregor = new Kaefer('Gregor');
            
            Object.observe(gregor, function (changes) {
              console.log('Folgende Eigenschaft wurde zum Objekt hinzugefügt: ' + changes[0].name);
            }, ['add']);
            
            gregor.bewerfen( );
            

            Hier übergeben wir also zunächst das Instanzobjekt gregor an die Methode und bestimmen dann, dass der Name der Veränderten Eigenschaft in die Konsole geschrieben wird, indem wir auf die name-Eigenschaft des ersten Objektes im Array zugreifen, in welcher der Bezeichner der betroffenen Eigenschaft hinterlegt ist.

            Schließlich bestimmen wir mit der Übergabe des Arrays ['add'], dass unsere Callback-Funktion nur aufgerufen werden soll, wenn eine Eigenschaft hinzugefügt wurde. Was dann mit dem Aufruf der Methode bewerfen auch passiert…

            // Folgende Eigenschaft wurde hinzugefügt: gesundheit
            

            Ich halte das jedenfalls für ein außerordentlich nützliches Feature und hoffe, dass es bald auch von Mozilla und Microsoft implementiert, sowie in die nächste Version des ECMAScript-Standards aufgenommen wird.


            Aber gut, zurück zum eigentlichen Thema! ;-)

            Wir hatten festgestellt, dass um die Invarianz vererbter Eigenschaften sicherzustellen, bei der Zuweisung (assignment) auf der Instanz automatisch eine neue eigene Eigenschaft angelegt wird.

            Aber dieser Grundsatz gilt nicht immer! – Beispiel:

            Object.freeze(Kaefer.prototype);
            
            gregor.bewerfen( );
            
            console.log(gregor.gesundheit); // 0
            
            console.log(gregor.hasOwnProperty('gesundheit')); // false
            

            Hier haben wir auf Kaefer.prototype die Methode Object.freeze angewendet, und der Aufruf der vererbten Methode bewerfen bewirkt überhaupt nichts:

            Weder verändert sich der Wert der ebenfalls geerbten Eigenschaft gesundheit, noch wird auf der Instanz eine neue eigene Eigenschaft mit dieser Bezeichnung angelegt!

            Um nun zu verstehen, was hier passiert, müssen wir erst einmal wissen, dass Objekteigenschaften in JavaScript selbst wiederum über mehrere interne Eigenschaften verfügen (um Verwechslungen zu vermeiden im Folgenden Attribute genannt), welche in einem assoziierten Objekt hinterlegt sind, welches als property descriptor bezeichnet wird:

            var farbe = Object.getOwnPropertyDescriptor(gregor, 'farbe');
            

            Mit der Methode Object.getOwnPropertyDescriptor können wir für jede eigene Objekteigenschaft die dazugehörigen Attribute auslesen, indem wir das Objekt sowie den Bezeichner der Eigenschaft als Argumente übergeben.

            Der Rückgabewert der Methode ist dann der property descriptor, welcher in unserem Beispiel für gregor.farbe so aussehen würde:

            { value : 'braun', writable : true, enumerable : true, configurable : true } // farbe
            

            Die Eigenschaften des property descriptors repräsentieren hier die dazugehörigen internen Eigenschaften [[Value]], [[Writable]], [[Enumerable]] und [[Configurable]], wobei in diesem Zusammenhang nur das Attribut writable von Interesse ist, welches darüber bestimmt, ob der Wert der Eigenschaft, welcher in value hinterlegt ist, verändert werden darf.

            Schauen wir uns einmal die Attribute einer Eigenschaft von Kaefer.prototype an, auf welche wir die Methode Object.freeze angewendet haben…

            var gesundheit = Object.getOwnPropertyDescriptor(Kaefer.prototype, 'gesundheit');
            
            { value: 0, writable: false, enumerable: true, configurable: false } // gesundheit
            

            …und wir sehen, dass die Attribute writable und configurable jeweils auf false gesetzt wurden.

            Dadurch, dass wir mit (der Holzhammermethode) Object.freeze den Wert der Eigenschaft gesundheit auf read-only gesetzt haben, haben wir nicht nur bewirkt, dass die Originaleigenschaft auf Kaefer.prototype nicht mehr verändert werden kann, sondern wir haben außerdem dafür gesorgt, dass diese Eigenschaft auch nicht durch assignment, also durch bloße Zuweisung auf einer Instanz überschrieben wird.

            Nebenbei sei übrigens noch erwähnt, dass im strict-Mode, bei dem Versuch eine Eigenschaft welche auf read-only gesetzt ist zu verändern, eine Fehlermeldung geworfen wird, sonst nicht!

            Jedenfalls sieht es anders aus, wenn wir die entsprechende Eigenschaft nicht durch Zuweisung, sondern durch Definition hinzufügen, also vor dem Aufruf unserer Methode bewerfen zum Beispiel folgendes schreiben:

            Object.defineProperty(gregor, 'gesundheit', {
              value : 0,
              writable : true
            });
            
            gregor.bewerfen( );
            
            var status = gregor.gesundheit; // z.B. -5 :-(
            
            console.log(gregor.hasOwnProperty('gesundheit')); // true
            

            Der Wert des Attributes writable einer vererbten Eigenschaft bestimmt also nicht nur, ob der Wert der Eigenschaft auf dem Objekt, dessen eigene Eigenschaft sie ist, überschrieben werden darf, sondern ebenso, ob bei einem Instanzobjekt durch assignment eine neue eigene Eigenschaft angelegt wird oder nicht.

            Wird eine Eigenschaft als read-only initialisiert, dann bleibt sie also über die gesamte Prototypenkette hinweg konstant, und kann auch auf einer Instanz nur durch explizite Definition überschrieben werden.

            Gruß,

            Orlok

            1. var gregor = new Kaefer('gregor');
              console.assert(gregor.hasOwnProperty('gesundheit') === false, 'Gesundheit ist eine direkte Eigenschaft von gregor');
              gregor.bewerfen();
              console.assert(gregor.hasOwnProperty('gesundheit') === true, 'Gesundheit ist keine direkte Eigenschaft von gregor');
              

              Zur asynchronen Überwachung von Objekten gibt es eine bessere Methode als console.assert […]

              Meine Zielsetzung bei diesem Beispiel war es, mit JavaScripts Hausmitteln den Sachverhalt zu reflektieren, den ich zuvor in natürlicher Sprache geschildert habe. console.assert() ist dazu da, um Fakten über das Programm zur Laufzeit zu examinieren, es ist hier ein expressives Mittel, um die Vorher- und Nachherzustände des Programms zu illustrieren. Das Object.observe()-Beispiel zeigt das auf eine andere Art, die ich persönlich weniger suggestiv finde, anderen mag es anders ergehen, deswegen ist es trotzdem gut, dass du es gebracht hast.

              1. Hallo

                Meine Zielsetzung bei diesem Beispiel war es, mit JavaScripts Hausmitteln den Sachverhalt zu reflektieren, den ich zuvor in natürlicher Sprache geschildert habe.

                Tut mir ehrlich leid. Das war idiotisch formuliert von mir! Ich wollte eigentlich nur eine zusätzliche Möglichkeit aufzeigen, wie man das auch anders hätte darstellen können.

                console.assert() ist dazu da, um Fakten über das Programm zur Laufzeit zu examinieren, es ist hier ein expressives Mittel, um die Vorher- und Nachherzustände des Programms zu illustrieren.

                Im Großen und Ganzen ACK, allerdings stört mich an dieser Methode, dass in dem Fall, dass die assertion fehlschlägt, pauschal eine Fehlermeldung in die Konsole geschrieben wird, sprich, die zweifellos nützliche Funktionalität hier meiner Meinung nach unnötig mit einer bestimmten Semantik verknüpft wird.

                Ich fände es besser, wenn man es hier so eingerichtet hätte, dass die Art der Ausgabe über einen zusätzlichen Parameter gesteuert werden kann. Als default value hätte man ja durchaus error festlegen können.

                Davon abgesehen hoffe ich, du hast mir meine ausschweifenden Erklärungen nicht übelgenommen. ;-)

                Der Threadersteller hatte ja erst kurz zuvor Fragen zu dem Thema gestellt, ohne dass wir aber dessen eigentlichen Kern in irgendeiner Form näher erläutert hätten, und da wollte ich einfach die Gelegenheit nutzen und ein paar Erklärungen nachschieben, in der Hoffnung, dass er die Beiträge vielleicht noch liest und einen Nutzen daraus ziehen kann, zumal das Thema hier ja auch nicht jeden Tag diskutiert wird.

                Außerdem liegt die Zeit ja noch nicht so lange zurück, in der ich als interessierter Anfänger hier nur still mitgelesen habe, und ich erinnere mich noch gut, dass ich mich immer sehr darüber gefreut habe, wenn jemand einen bestimmten Sachverhalt einmal etwas ausführlicher beschrieben hat, so dass es mir trotz meiner begrenzten Kenntnisse der Materie dennoch möglich war, den Ausführungen zu folgen.

                Ich hatte gehofft, du würdest diese Erklärungen also nicht als unangemessene Belehrungen auffassen, sondern meine eigentliche Motivation erahnen, aber da ich mir dessen nicht mehr so sicher bin, wollte ich es lieber nochmal klarstellen. ;-)

                Also, sollte das falsch rüber gekommen sein, bitte ich es zu entschuldigen!

                Gruß,

                Orlok

                1. Davon abgesehen hoffe ich, du hast mir meine ausschweifenden Erklärungen nicht übelgenommen. ;-)

                  Quatsch, diesem Forum mangelt es leider an JavaScript-KnowHow, deshalb begrüße ich deine Beiträge in jüngster Zeit umso mehr, weiter so!

                  1. @@1unitedpower

                    Quatsch, diesem Forum mangelt es leider an JavaScript-KnowHow.

                    @molily ist abhandengekommen.

                    LLAP 🖖

                    --
                    Ist diese Antwort anstößig? Dann könnte sie nützlich sein.
                    1. Hallo Gunnar Bittersmann,

                      @molily ist abhandengekommen.

                      Das bedauere ich sehr. Die Notification wird ihn auch nicht erreichen, denn sein letzter Beitrag stammt vom 31.7.2014 noch im alten Forum. Die alten Zugangsdaten konnten nicht übernommen werden.

                      Sein geänderter Twittername spricht Bände.

                      Bis demnächst
                      Matthias

                      --
                      Signaturen sind bloed (Steel) und Markdown ist mächtig.