Tach!
Zudem muss p
nicht "global" in myDialog rumliegen sondern kann als const p = ...
innerhalb der For-Schleife im dortigen lokalen Scope existieren. Der Wert wird nie verändert, weswegen sie als const deklariert werden kann. Ebenso geht es der Variable element
und elements
, die jeweils nur innerhalb eines eines Blocks existiert. Alle anderen var
können auch zu const
umgeschrieben werden, weil sich ihr Inhalt nicht ändert.
Dieses const
kenne ich noch nicht so lange wie var
.
const/let gibt es ja auch noch nicht so lange. Aber laut CanIUse kann das sogar der IE11 in Grundzügen.
Beide sind für mich nur dazu da, eine Variable nicht als Eigenschaft von window
, also als globale Variable zu definieren, sondern als lokale.
Es gibt noch den Unterschied im Scope-Verhalten, const/let gilt nur innerhalb der nächstgelegenen {}-Klammern, var innerhalb der gesamten Funktion.
Mir ist es völlig egal, ob man den Inhalt von const
später mit einem anderen Datentyp befüllen kann, oder nicht - wegen mir bräuchte es das überhaupt nicht.
Vermutlich ist const für die Laufzeitumgebung interessant, dass es da besser optimieren kann als bei veränderbaren Variablen.
Die von querySelectorAll() zurückgegebenen NodeList ist non-live und braucht keine slice()-Kopie, um damit gefahrlos arbeiten zu können.
Deswegen tue ich es auch nicht.
Und obwohl sie kein Array ist, forEach() hat sie jedenfalls.
In wirklich allen Browsern? Zuletzt meine ich mich zu erinnern, wollte es im IE nicht so recht. Ist das jetzt nicht mehr so?
Naja, keiner der IEs hat diese Methode an der NodeList.
Das prop
ist auch wieder nur lokal verwendet und kann als for (let prop of data)
notiert werden, so dass man es ebenfalsl nicht "global" in myDialog braucht.
Ich empfand das immer als "unsauber", weil eine so definierte Variable nicht am Anfang meines Programms steht. Aber der Scope innerhalb einer Schleife geht ja nicht nach außen, daher würde prop
tatsächlich so nur innerhalb der Schleife existieren. Also besser.
Ich empfinde es eher unübersichtlich, alles am Anfang aufzuführen, selbst wenn es nur lokal verwendet wird. Das ist ja seit let/const schon besser geworden.
Um nicht nur beiden Vorlieben gerecht zu werden, sondern auch der Lesbarkeit zu dienen, könnte man den Code weiter in logische und kürzere Einheiten (lies Funktionen) aufbrechen. Damit werden die Teile einfacher zu verstehen, weil sie kürzere in sich abgeschlossene Einheiten bilden. Und auch der Hauptcode ist dann nicht mehr so lang.
Wenn man es schafft, die Funktionen mit beschreibenden Namen zu versehen, hat man als Leser gleich noch einen Hinweis auf das, was die Funktion tun soll, ohne erst den Code verstehen zu müssen und auch ohne dass da ein Kommentar zur Erläuterung steht.
Zudem meckert meine IDE an, man solle bei diesen Stellen den typsicheren Vergleich nehmen.
Du meinst ein Boolean true
würde sonst auch akzeptiert? Kann das als Bezeichner einer Eigenschaft überhaupt sein?
Kann nicht, aber der Linter in meiner IDE ist nicht clever genug, um das zu erkennen. Der kennt nur die Crockford-Regel, alles was ein Vergleich sein soll, habe === zu nehmen, selbst wenn es überhaupt nicht nötig ist.
Es gibt da eine Comparison Table für Javascript. Alle Strings, die nicht mit Zahlen beginnen oder Leerstrings sind, sind nur zu Strings mit gleichem Inhalt gleich. In der Tabelle sind "true"
und "false"
(die mit den Anführungszeichen) Vertreter dieser Sorte. Es ist also komplett egal, was da an Wert reinkommt, denn nichts davon ergibt true außer dem Wert "info" in diesem Fall. Deswegen ist das Gemecker vom Linter hier sinnlos. Man kann aber trotzdem === statt == notieren, wenn man mag.
if (data[prop].type && data[prop].type == "info")
Aber das ist ziemlich egal. Ob der nun Vergleich fehlschlägt, weil irgendein x-beliebiger Wert auch nach Typumwandlung nicht gleich dem gegebenen String ist oder die Typen der Werte nicht stimmen, kommt aufs gleiche raus.
So betrachtet schon. Ich habe mir immer angewöhnt, auf das prinzipielle Vorhandensein zu prüfen. Daher die erste Bedingung.
Diesen Teil hatte ich gar nicht beachtet, nur den Vergleich nach dem &&
. Aber jetzt wo du es sagst, fällt mir auf, dass ich doch was daran auszusetzen habe. Prinzipiell ist so ein Testen auf Vorhandensein richtig und wichtig, aber man muss es richtig tun. Ein einfacher Zugriff auf eine nicht vorhandene Eigenschaft erzeugt noch keinen Fehler, sondern ergibt stillschweigend undefined
zurück. Aber das Verketten und Durchgreifen misslingt mit Fehlermeldung, wenn zwischendrin eine Eigenschaft auf undefined läuft, weil undefined ein primitiver Wert ist, der keine Eigenschaften hat.
let obj = {};
let x = obj.nicht_da;
let x = obj.nicht_da.irgendwas;
Man muss also nur "vorn" alles testen, aber nicht die letzte Eigenschaft selbst.
if (obj && obj.nicht_da && obj.nicht_da.irgendwas == 0)
if (obj && obj.nicht_da && obj.nicht_da.irgendwas && obj.nicht_da.irgendwas == 0)
Letzteres würde auch bereits false liefern, wenn der Wert in irgendwas zu false kompatibel ist. Also wenn er in dem Beispiel 0 wäre, würde die erste Zeile true ergeben, die zweite nicht.
Um zu deinem Code zurückzukommen
if (data[prop].type && data[prop].type == "info")
Die erste Prüfung ist so nicht sinnvoll. Man müsste data[prop]
prüfen. Wenn das auf undefined/null läuft, erzeugt der Zugriff auf .type
eine Fehlermeldung und Script-Abbruch. Um undefined oder null zu ergeben, reicht es, wenn der Anwender die Eigenschaft mit dem Wert null oder undefined statt einem Objekt erstellt. Besser also wie folgt, aber es geht hier noch besser.
if (data[prop] && data[prop].type == "info")
Man muss das nun nicht bei jeder Eigenschaft einzeln notieren, sondern kann diese Prüfung einmal am Schleifenanfang vornehmen.
for (const prop in data) {
if (!data[prop])
continue;
if (data[prop].type == "info") {
Außerdem muss ich mich in einem Punkt revidieren. for...of
geht nur mit iterierbaren Dingen wie Arrays. Objekte gehören ohne Spezialbehandlung nicht dazu. Das muss also weiterhin eine for...in
-Schleife bleiben, damit das über die Objekteigenschaften laufen kann. Falls man die prototypische Vererbung verhindern möchte, muss man das über hasOwnProperty() prüfen und entsprechend verzweigen. Das kann man hier aber wohl weglassen.
Wenn man das Ganze noch ordentlich weitertreibt, dann würde man die Datenobjekte nicht von Hand als Literal notieren, sondern mittels einer Klasse bauen lassen, die dafür Sorge trägt, dass alle notwendigen Eigenschaften auch vorhanden sind - und als default-Typ eben info
verwendet.
Jein, wenn es "ordentlich" sei sollte, müsste man dieses XY-Problem durch Verwendung von <template> vermeiden.
Als Bonus kann man das HTML des Templates mit allen Finessen des HTML-Editors erstellen.
Der Phantasie sind eben keine Grenzen gesetzt. Man kann mit seiner Programmlogik alles Denkbare umsetzen.
Da hast du mich wohl missverstanden. Ich meinte, dass dein Ansatz über das Konfigurationsobjekt nicht nur bei der Gestaltung des Dialoginhalts einschränkt auf das was der dieses Objekt auswertende Code bietet, auch im Editor beim Erstellen des Objekts hat man keinerlei Hilfe durch Autovervollständigung und ähnliche Hilfsmittel. Man erstellt ein Objekt mit für die IDE unbekannte Struktur. Wenn man die Eigenschaften nicht richtig tippt, gibt es zur Laufzeit kein Ergebnis. Wenn man stattdessen den Dialoginhalt direkt als HTML notieren könnte, wäre die IDE in der Lage, Syntaxprüfungen und Vervollständigung anzubieten.
dedlfix.