Orlok: Object Initializer

Beitrag lesen

Hallo Rolf

Auch in JS Objekten sind Stringbegrenzer für die Properties nicht erforderlich. Es sei denn, da will jemand reservierte Worte verwenden.

Das war früher mal richtig, trifft aber schon lange nicht mehr zu. (Edit. Danke @Rolf B für die Erinnerung) Heutzutage gilt: Auch wenn es sich bei dem Namen einer Objekteigenschaft oder -methode um ein reserviertes Wort handelt, muss der Name nicht als String angegeben werden. Nicht bei der Definition der Eigenschaft oder Methode, und auch nicht bei ihrer Referenzierung.

Sehen wir uns mal ein Beispiel an:

const object = {

  extends (source = {}) {
    Object.assign(this, source);
  }

};


object.extends({

  default: 42,

  function (value = this.default) {
    console.log(value);
  }

});


object.function(); // 42

Hier werden zwei Objekte in Literalsyntax definiert. Das erste Objekt, welches der Konstante mit dem Bezeichner object zugewiesen wird, besitzt eine Methode namens extends. Bei diesem Namen handelt es sich um ein reserviertes Wort, genauer gesagt, um ein Schlüsselwort der Sprache. Das Keyword extends wird verwendet, um eine abgeleitete Klasse zu erzeugen:

class Stack extends Array {

  peek () {
    return this[this.length - 1];
  }

}


console.log(new Stack(1, 2, 3).peek()); // 3

Obwohl es sich bei extends um ein reserviertes Wort handelt, muss es bei der Verwendung als Name einer Eigenschaft oder einer Methode in einem Objektliteral nicht als String notiert werden. Gleiches gilt für die Schlüsselworte default und function, die in dem Objekt verwendet werden, das der Methode extends bei ihrem Aufruf übergeben wird:

object.extends({

  default: 42,

  function (value = this.default) {
    console.log(value);
  }

});

Wie hier beim Aufruf der Methode extends und der Definition des Standardwertes für den Parameter der Methode function zu sehen ist, muss in einem Elementausdruck der Name der referenzierten Eigenschaft oder Methode ebenfalls nicht als String angegeben werden, auch wenn es sich bei dem Namen um ein reserviertes Wort handelt.

Die Definition für einen Identifier, also einen Bezeichner sieht in Backus-Naur-Form ausgedrückt etwa wiefolgt aus. Wobei zu beachten ist, dass die BNF keine Negation kennt, aber das ist an dieser Stelle auch nicht wichtig, schließlich wollen wir kein JS parsen, sondern nur die Syntax nachvollziehen.

<identifier> ::= <identifier-name> not <reserved-word>

Ein Bezeichner und sein Name stellen also zwei verschiedene Nichtterminalsymbole dar. Ein Bezeichner ist demnach einfach ein <identifier-name>, der kein reserviertes Wort ist. Die Syntax für ein Objektliteral würde dann vereinfacht und sehr unvollständig etwa so aussehen:

<object-literal> ::=

    { <optional-whitespace> } |

    { <property-definition-list> }


<property-definition-list> ::=

    <property-definition> |

    <property-definition-list> , <property-definition>


<property-definition> ::=

    <property-name> : <assignment-expression> |

    <method-definition>


<method-definition> ::=

    <property-name> ( <formal-parameters> ) { <function-body> }


<property-name> ::=

    <literal-property-name> |

    <computed-property-name>


<literal-property-name> ::=

    <identifier-name> |

    <numeric-literal> |

    <string-literal>

Hier fehlen einige Ableitungsregeln und einige Produktionen sind unvollständig, nichtsdestotrotz kann man erkennen, dass bei der Notierung eines Eigenschafts- oder Methodennamens in Form eines Literals ein beliebiger <identifier-name> verwendet werden kann. Eine Einschränkung, wonach es sich bei diesem Namen nicht um ein reserviertes Wort handeln darf, gibt es hier grundsätzlich nicht.

const object = {

  typeof: 'object',

  new () {
    return Object.create(this);
  }

};


console.log(object.new().typeof); // object

Eine Konstruktion wie in dem Beispiel oben ist also ohne weiteres möglich. Daraus ist allerdings nicht abzuleiten, dass die Verwendung von reservierten Wörtern für Eigenschafts- und Methodennamen auch empfehlenswert ist. Hier ist immer abzuwägen, ob durch den Gebrauch des reservierten Wortes die Lesbarkeit des Codes eingeschränkt wird.

Eine Objekteigenschaft default oder eine Methode do zu nennen muss je nach Kontext nicht zwingend schlecht sein. Bei den Keywords const oder function andererseits ist es schwer vorstellbar, dass bei deren Verwendung als Eigenschafts- oder Methodenname die Lesbarkeit nicht leidet. Hier ist also definitiv Vorsicht geboten und im Zweifel sollte auf diese Praxis verzichtet werden.

wenn ich als Key in einem Objektliteral einen Variablennamen hinschreibe, löst es ihn nicht zum Wert auf

Das kommt darauf an. Die von mir oben angegebene Produktion für Eigenschaftsdefinitionen war wie bereits angedeutet unvollständig. Tatsächlich müsste man das eher wiefolgt schreiben:

<property-definition> ::=

    <identifier-reference> |

    <property-name> : <assignment-expression> |

    <method-definition>


<identifier-reference> ::= <identifier>

Wenn in einem Objektliteral nur ein Bezeichner als Eigenschaft notiert wird, dann wird versucht, diese Referenz aufzulösen. Gelingt das, wird der Bezeichner als Eigenschaftsname und der mit dem Bezeichner assoziierte Wert als Eigenschaftswert verwendet. Kann die Referenz hingegen nicht aufgelöst werden, gibt das einen Reference Error.

const number = 42;


const object = {

  number,

  print () {
    console.log(this.number);
  }

};


object.print(); // 42

In diesem Beispiel wird in dem Objektliteral als Eigenschaft der Bezeichner number notiert, ohne dabei einen Wert zuzuweisen. Da es hier in der lexikalischen Umgebung des Objektliterals eine Bindung für diesen Bezeichner gibt, wird wie beschrieben eine Eigenschaft mit dessen Namen angelegt, und dem Wert, mit dem hier die Konstante initialisiert wurde.

Natürlich muss die Bindung für den Bezeichner nicht zwingend im selben Kontext existieren wie der Ausdruck mit dem das Objekt erzeugt wird. Es kann auch eine Konstante, Variable oder Funktion aus einem umgebenden Scope referenziert werden. Wichtig ist nur, dass die Referenz überhaupt aufgelöst werden kann. Und selbstverständlich folgendes:


<property-definition> ::= <identifier-reference> ...


<identifier-reference> ::= <identifier>


<identifier> ::= <identifier-name> not <reserved-word>

Da ein Bezeichner kein reserviertes Wort sein darf, kann eine Referenz auf einen Bezeichner zum Zwecke der Eigenschaftsdefinition natürlich auch kein reserviertes Wort enthalten.

Viele Grüße,

Orlok