Felix Riesterer: Neues Anfänger-Tutorial für JavaScript: Tic-Tac-Toe

Beitrag lesen

problematische Seite

Lieber dedlfix,

vielen Dank für Deine vielen wertvollen Denkanstöße!

Ich würde es gern sehen, wenn die Instantiierungen anonymer Funktionen durch die Variante 2, also die IIFE-Schreibweise ausgetauscht würden.

sagte nicht Crockford, er möge es nicht, wenn die Argumente-Klammer außerhalb der umspannenden Klammern läge? So á la "da hängen die Eier 'raus"?

(function (param) { ... } (value));
// anstatt
(function (param) { ... }) (value);

Aber das ist meines Erachtens ziemlich egal. Gerade für Anfänger ist es das. Inwiefern ich damit Anfängern den Einstieg in die Instanziierung von Objekten mit dem Schlüsselwort new verbaue... da bin ich als Pädagoge eher gelassen, denn meine Erfahrung zeigt, dass man bereits Bekanntes durchaus in neue Kontexte einzuordnen lernt.

Ich hab da mal eben einen Erläuterungsartikel dazu geschrieben, siehe obigen Link.

Und schon wieder hat das Wiki inhaltlich gewonnen. Danke dafür!

Das MDN sagt, dass querySelectorAll() eine non-live NodeList liefert und keine lebendige. Dieses Detail ist aber für den Artikel nicht weiter relevant, weil die Anzahl der Spiele auf der Seite sich nicht im laufenden Betrieb ändern wird.

Ungeachtet dessen muss ich das dann korrigieren. Leider bleibt es eine NodeList und wird kein Array, sodass die schönen Iterationsmethoden wie forEach leider nicht zur Verfügung stehen.

Es ist sicher auch wenig sinnvoll, mehr als ein Spiel auf die Seite zu bringen, solange man eh nur zwei Personen abwechselnd agieren lässt.

Um aber die Unabhängigkeit der Objekte bzw. den Scope von Funktionen und Variablen zu zeigen, wäre es durchaus sinnvoll - immerhin ist es ein Tutorial, bei dem Anfänger grundsätzlich etwas lernen sollen, nicht nur zielgerichtet für das eine Ergebnis.

Jedenfalls könnte man dem Spiel auch eine ID statt einer Klasse vergeben. Es sei denn, du möchtest nur einen Grund haben, warum das eigentliche Spiel in einer Funktion (TicTacToe) ausgelagert ist. Aber das könnte man auch mit guter Strukturierung und Bilden von Zuständigkeitseinheiten begründen.

Wenn mehr als ein Spiel auf der Seite möglich sein soll, dann braucht es eine Klasse anstelle einer ID. Und das Fass "ID versus Klasse" wollte ich nicht aufmachen. Das soll gerne in einem anderen Tutorial besprochen werden. Warum soll man z.B. nicht verschiedene Spielstände zum Weiterspielen anbieten können? Das geht nur mit einer Klasse anstelle einer ID!

Statt for mit der Zählvariable i fände ich ja forEach() ausdrucksstärker, vor allem, weil man dann nicht games[i] sondern game zur Repräsentation eines einzelnen Spiels hat. Dummerweise liefert querySelectorAll() kein Array sondern eine NodeList, und die hat keine Array-Methoden. Was aber geht, wäre for...of, was aber auf Microsoft-Seite erst im Edge-Browser verfügbar ist. Jedenfalls kommt man damit an die Ausdrucksstärke von forEach heran.

Das mit Iterationsmethoden und Callback-Funktionen empfinde ich als ein kleines bisschen fortgeschrittener, als es ein reines Anfänger-Tutorial im Kern verkraftet. Bei der "Was fehlt noch?"-Rubrik könnte man solche Gedanken anbringen. Oder man schreibt gleich ein ganz anders konstruiertes Beispiel und macht ein Anfänger-II-Tutorial daraus. Magst Du (<I>) ...?

Es gibt einen entscheidenden Unterschied zwischen

var foo = function() {...};

und

function foo() {...};

Ja. Den habe ich bewusst vorenthalten. Mir war im Kontext des Tutorials zunächst wichtig anzudeuten (nicht: genau darzulegen!), dass sich ein Funktionsbezeichner wie eine gewöhnliche Variable verhält. Wann die Funktion definiert wird, und wann ihr Code ausführbar zur Verfügung steht, ist selbstverständlich bei den Schreibweisen höchst unterschiedlich.

Variablen-/Parameternamen bitte ausschreiben. Bei i als allgemein übliche Laufvariable ist das noch kein Problem (besser: forEach/for...of verwenden, wenn möglich), aber el und e sind gerade für Anfänger, die nicht wissen, was die Funktionen für Parameter erwarten, erklärungsbedürftig. Also lieber element und event nehmen. Das gilt im Prinzip auch für Code, der nicht nur Anfängern gewidmet ist. Für absichtlich unleserlichen Code gibt es Uglifier ;)

Guter Einwand! Das werde ich verbessern. Auch an den Beispieldateien.

In check() ist es besser, statt !finished nur auf finished zu prüfen und in dem Fall die Funktion zu verlassen. Das reduziert eine Einrückungsebene, besonders weil es keinen Else-Zweig oder generellen Code nach diesem if kommt.

Hmm. Ja. Im Prinzip schon. Aber dann müsste ich return einführen. Das habe ich zwar implizit schon getan, indem ich Rückgabewerte von Methoden in Variablen ablege, wie ich aber mitten aus einer Funktion aussteige, egal ob mit oder ohne Rückgabewert, wollte ich nicht ohne konkreten Anlass einführen.

"Es hat einen Sinn, die Variablendeklaration an den Anfang der Funktion zu setzen und nicht erst im Anweisungsblock des if-Statements. Das ist gute Programmierertradition, da damit der Code für andere übersichtlicher wird." - Njein, kann man so sehen, kann man aber auch anders sehen.

Stimmt, kann man. Aber wenn man die Sache mit dem Scope üben will, dann ist es einleuchtend, wenn man die Deklaration an oberster Stelle des Scopes vornimmt. Findest Du nicht?

Besser sind kleinere lokale übersichtliche Scopes, statt dass man im Großen versucht den globalen Scope zu meiden, im kleinen aber Variablen im gesamten Scope rumliegen hat. Im Falle von i ist es sogar unübersichtlicher, weil i nur ganz lokal benötigt wird und nicht in der gesamten Funktion.

Bedeutet das, dass Du gerne noch mehr Funktionen verschachteln möchtest? Hier in diesem konkreten Anfänger-Tutorial? Oder war das eher eine allgemeine Marschrichtung für erfahrenere JavaScriptler?

Dafür hat man sich auch in der kommenden JavaScript-Version das Schlüsselwort let ausgedacht, das den Scope von Variablen nochmal deutlich einschränken kann. Ansonsten muss man sagen: Kommt drauf an. Manchmal benötigt man eine Variable wirklich im gesamten Scope und manchmal eben nicht.

Aha! Dann wäre das offensichtlich etwas für forgeschrittenere Tutorials. Aber gut, dass Du das angesprochen hast, denn diese neuesten ECMA-Script-Standards habe ich noch nicht so auf dem Schirm. Insbesondere let kenne ich aus BASIC auf dem C64...

Zur Ermittlung ob full oder nicht, kann man nach dem ersten false die Schleife abbrechen, "fuller" wird es nämlich mit den anderen Werten auch nicht.

Tja, dann müsste ich break einführen. Wollte aber sparsam darin sein, was ich alles auf einen echten Anfänger los lasse.

Noch besser wäre es, every() zu nehmen, was im vorliegenden Ansatz wieder daran krankt, dass eine HTMLCollection kein Array ist.

Eben. Warum nur um Himmels Willen hat man das so gemacht?? Diese ganzen NodeLists haben diese bequemen Iterationsmethoden nicht. Was haben die sich dabei nur gedacht? Ist es das live, dass eine Iterationsmethode scheitern ließe? Das will mir nicht einleuchten.

"Wenn wir prüfen wollen, ob der String leer ist, genügt es nicht mit dem ==-Operator zu prüfen, da JavaScript intern Wertetypen umrechnen kann, um verschiedene Typen miteinander zu vergleichen." - Oh doch, das genügt in dem Fall vollkommen, weil beim Vergleich von zwei Strings (hier className mit einem Stringliteral) kein anderer Wertetyp beteiligt ist. Anders sähe der Fall aus, wenn ein Integer oder Boolean beteiligt wäre - ist aber nicht.

Es könnte eine Klasse "0" oder "000000" benutzt werden - extrem unwahrscheinlich, aber irgendwie hielt ich es für nötig, die Typsicherheit bei Vergleichen von beiden Seiten aus vorzuführen. Es sollte einmal ein typsicherer Vergleich nötig sein und einmal ein lose typisierter. Hättest Du bessere Beispiele gewusst, um das Problem zu veranschaulichen? Ich habe hier eher pädagogisch denn maximal effizient programmierend entschieden.

className ist definiert als string, und selbst wenn man da 0 (Integer-Literal) reinschreibt, kommt "0" (String) beim Lesen raus.

Genau, und "0" ist falsy, ebenso wie "00000".

Ist das nicht etwas inkonsequent? Einerseits die automatische Typkonvertierung mithilfe von === vermeiden (abgesehen von obigem Sinn-Argument für den dortigen Fall) und andererseits darauf zu bauen?

Es war eine pädagogische Entscheidung, um die Problematik der Typ(un)sicherheit in JavaScript schon einem Anfänger zu vermitteln.

Abgesehen von dieser Stichelei, pfeif ich persönlich auf solche Konsistenzen aus niederen Beweggründen.

Hehe, und ich mache sie mir pädagogisch zunutze.

Ich brauche die Erfahrungswerte, die ich bekomme, wenn ich in solche potentiellen Fehlerquellen reintappe.

Eben.

Gut, dass wir darüber diskutiert haben.

Die Verwendung eines //<![CDATA[...//]]>-Blocks ist laut Standard nicht notwendig. Vielleicht gab/gibt es einige Situationen, für die die Browser das brauchen, aber da ist mir auch noch keine über den Weg gelaufen. In HTML5 ist das Regelwerk von script gehörig aufgebohrt worden (und ich hab keine Lust das alles zu lesen), aber dass nun darin Markup interpretiert wird, kann ich mir nicht vorstellen. Der CDATA-Rahmen kann also weg. Und type="text/javascript" ist der Standard-Wert und kann damit auch weggelassen werden.

Das ist alles schön und gut, wenn kein String in der Form "</script>" enthalten ist. Bastele ich einen CDATA-Block darum, habe ich kein Problem. Ohne diesen Block könnte mir das um die Ohren fliegen (und ist es in der Vergangenheit auch). Daher mache ich das immer dran. Es stört nicht und fördert ungefragt und unbenötigt die XHTML-Kompatibilität.

Wenn man es "richtig" macht, stochert man lieber nicht in der Ausgabe herum, um Werte für die Geschäftslogik zu bekommen. Wenn man an der Ausgabe Änderungen vornehmen möchte (zum Beispiel statt Tabelle was anderes), muss man durch die Geschäftslogik durchlaufen und dort alle Element- und/oder Klassennamen ändern. Besser ist, man hat eine separate Datenhaltung.

Darüber hatte ich mir im Vorfeld einige Gedanken gemacht. Ich hatte ein Array mit den 3x3 Feldern, wobei jedes Array-Element, welches für ein Feld stand, ein Objekt war, welches unter anderem das betroffene Elementobjekt enthielt. Etwas in dieser Richtung:

var board = [
    [
        {
            element: <HTMLTableCellElement>,
            value: "x"
        },
        {
            element: <HTMLTableCellElement>,
            value: ""
        },
        {
            element: <HTMLTableCellElement>,
            value: "o"
        }
    ], // weitere Arrays für Zeilen 2 & 3
];

Ich habe dann festgestellt, dass das Prüfen und Ermitteln von Gewinner oder Unentschieden für einen Anfänger unnötig kompliziert wird. Deshalb habe ich mich für eine Anfänger-Lösung mit der className-Eigenschaft entschieden.

Liebe Grüße,

Felix Riesterer.

3 205

Neues Anfänger-Tutorial für JavaScript: Tic-Tac-Toe

Felix Riesterer
  • meinung
  • seitenbewertung
  • selfhtml-wiki
  1. 0
    Matthias Scharwies
    1. 0
      Felix Riesterer
      1. 0
        Matthias Apsel
        1. 0
          Felix Riesterer
          1. 0
            Der Martin
            1. 0
              Gunnar Bittersmann
              1. 0
                Der Martin
                1. 0
                  Gunnar Bittersmann
                  1. 0
                    Matthias Apsel
                    1. 0
                      Gunnar Bittersmann
                      1. 0
                        Der Martin
                        1. 0
                          Gunnar Bittersmann
                2. 0
                  Felix Riesterer
                  1. 0
                    Camping_RIDER
                    1. 0
                      Gunnar Bittersmann
                      1. 0
                        Camping_RIDER
          2. 0
            Gunnar Bittersmann
            1. 3
              Felix Riesterer
              1. 1
                Gunnar Bittersmann
                1. 0
                  Felix Riesterer
                  1. 0
                    Gunnar Bittersmann
                    1. 1
                      Matthias Apsel
                      1. 0
                        Gunnar Bittersmann
                        1. 0
                          Matthias Apsel
                          1. 0
                            Gunnar Bittersmann
                            1. 0
                              Camping_RIDER
                              1. 0
                                Gunnar Bittersmann
                                1. 0
                                  JürgenB
                                  1. 0
                                    Gunnar Bittersmann
                                    1. 0
                                      JürgenB
                                      1. 0
                                        Gunnar Bittersmann
                                2. 4
                                  Camping_RIDER
                                  1. 0
                                    Gunnar Bittersmann
                                    1. 0
                                      Camping_RIDER
                                      1. 0
                                        Gunnar Bittersmann
                                        1. 0
                                          Felix Riesterer
                                          1. 0
                                            Gunnar Bittersmann
                                            1. 0
                                              Camping_RIDER
                                              1. 0
                                                Gunnar Bittersmann
                                                1. 1

                                                  Kommunikation

                                                  Camping_RIDER
                                                  • meinung
                                                  • menschelei
              2. 0
                Richard Rüfenacht
                1. 0
                  Gunnar Bittersmann
                  1. 0
                    Richard Rüfenacht
                  2. 0
                    Auge
                    • meinung
                    • selfhtml-wiki
                    1. 0
                      Gunnar Bittersmann
                2. 0
                  Auge
                  • meinung
                  • selfhtml-wiki
    2. 0
      Gunnar Bittersmann
      1. 0
        Felix Riesterer
        1. 0

          Großes ẞ

          Gunnar Bittersmann
          • typografie
          1. 0
            Camping_RIDER
            1. 0
              Gunnar Bittersmann
              1. 0
                Camping_RIDER
                1. 0
                  Gunnar Bittersmann
                  1. 0
                    Camping_RIDER
                    1. 0
                      Der Martin
                      1. 0
                        Christian Kruse
                        1. 0
                          Der Martin
                        2. 0
                          Gunnar Bittersmann
                          1. 0
                            Christian Kruse
                            1. 0
                              Camping_RIDER
                            2. 0
                              Christian Kruse
                          2. 0
                            Der Martin
                            1. 0
                              Gunnar Bittersmann
                              • menschelei
                    2. 0
                      Matthias Apsel
            2. 0
              MudGuard
              1. 0
                Christian Kruse
              2. 0
                Gunnar Bittersmann
                1. 0
                  Felix Riesterer
              3. 0
                Gunnar Bittersmann
                1. 0
                  Matthias Apsel
          2. 2

            Großes ẞ - der Vollständigkeit halber

            Bai Se Wey
            1. 0
              Gunnar Bittersmann
              1. 1

                Großes ẞ - der Kleinlichkeit halber

                Bai Se Wey
      2. 0
        Klawischnigg
        1. 0
          Camping_RIDER
          • menschelei
  2. 0
    Gunnar Bittersmann
    1. 0
      JürgenB
      1. 0
        Matthias Apsel
        1. 0
          Christian Kruse
          1. 0
            JürgenB
            1. 0
              Tabellenkalk
          2. 0
            Camping_RIDER
      2. 0
        Gunnar Bittersmann
        1. 0
          Felix Riesterer
          1. 0
            Gunnar Bittersmann
            • selfhtml-wiki
            • svg
            1. 0
              Felix Riesterer
              1. 0
                Camping_RIDER
                1. 0
                  Gunnar Bittersmann
                  • selfhtml-wiki
                  • typografie
                  1. 0
                    Auge
                    1. 0
                      Gunnar Bittersmann
                      1. 0
                        Christian Kruse
                        1. 0
                          Gunnar Bittersmann
                          1. 3
                            Christian Kruse
              2. 0
                Gunnar Bittersmann
                1. 1
                  Camping_RIDER
                  1. 0
                    Gunnar Bittersmann
    2. 0
      Felix Riesterer
  3. 0
    JürgenB
    1. 0
      Felix Riesterer
      1. 0
        Gunnar Bittersmann
        1. 0
          Matthias Apsel
        2. 0
          Felix Riesterer
        3. 0
          Camping_RIDER
          1. 0
            Gunnar Bittersmann
            1. 0
              JürgenB
              1. 0
                Auge
              2. 0
                Camping_RIDER
              3. 0
                Gunnar Bittersmann
                1. 0
                  Gunnar Bittersmann
      2. 0
        JürgenB
  4. 4
    dedlfix
    1. 0
      Matthias Scharwies
      1. 0
        dedlfix
        1. 0
          Matthias Scharwies
          1. 0
            dedlfix
            1. 0
              Matthias Scharwies
              1. 0
                dedlfix
          2. 0
            Matthias Apsel
            1. 0
              Matthias Scharwies
            2. 0
              Camping_RIDER
    2. 0
      Felix Riesterer
      1. 0
        Gunnar Bittersmann
      2. 0
        dedlfix
  5. 0
    pl
    1. 0
      Felix Riesterer
      1. 0
        pl
        1. 0
          Gunnar Bittersmann
  6. 0
    Msass
  7. 5
    Gunnar Bittersmann
    1. 1
      dedlfix
      1. 0
        Gunnar Bittersmann
        1. 2
          Felix Riesterer
          1. 1
            Gunnar Bittersmann
            1. 1
              Felix Riesterer
              1. 0
                Gunnar Bittersmann
                1. 0
                  Gunnar Bittersmann
                  1. 0
                    Felix Riesterer
                    • meinung
                    • selfhtml-wiki
                2. 0
                  Felix Riesterer
                  • meinung
                  • selfhtml-wiki
            2. 3
              Matthias Apsel
    2. 0
      Matthias Apsel
      1. 0
        Matthias Apsel
        1. 2
          dedlfix
      2. 0
        Gunnar Bittersmann
        1. 0
          Felix Riesterer
          1. 0
            Gunnar Bittersmann
            1. 0
              Camping_RIDER
              1. 0
                Gunnar Bittersmann
                1. 1
                  Felix Riesterer
                  1. 0
                    Gunnar Bittersmann
                    1. 0
                      Felix Riesterer
            2. 0
              Felix Riesterer
              1. 0
                Gunnar Bittersmann
    3. 0
      Felix Riesterer
      1. 1
        Gunnar Bittersmann
        1. 0
          Gunnar Bittersmann
    4. 0

      Wer hat gewonnen?

      Matthias Apsel
      • javascript
      1. 0
        Gunnar Bittersmann
    5. 0
      Matthias Apsel
    6. 0
      Matthias Apsel
      1. 0
        Matthias Apsel
    7. 6
      Felix Riesterer
      1. 1
        Der Martin
        1. 1
          Felix Riesterer
          1. 0
            Matthias Scharwies
            1. 0
              Felix Riesterer
              1. 0
                Gunnar Bittersmann
          2. 1
            Gunnar Bittersmann
            1. 0
              Felix Riesterer
              1. 0
                Gunnar Bittersmann
                1. 0
                  Felix Riesterer
                  1. 0
                    Gunnar Bittersmann
                    1. 0
                      Matthias Apsel
                2. 1

                  Roter-Faden-Artikel in Wikis

                  Camping_RIDER
              2. 0
                Gunnar Bittersmann
      2. 1
        Gunnar Bittersmann
  8. 0
    Gunnar Bittersmann
    1. 0
      Felix Riesterer
      • meinung
      • selfhtml
      • selfhtml-wiki
    2. 0
      Camping_RIDER
  9. 1
    Gunnar Bittersmann
    1. 0
      Felix Riesterer
      1. 0
        Gunnar Bittersmann
        1. 0
          Felix Riesterer
          1. 0
            Gunnar Bittersmann
            1. 2
              Mitleser
              1. 0
                Gunnar Bittersmann
              2. 0
                Matthias Apsel
                1. 0
                  Gunnar Bittersmann
                  1. 1
                    Camping_RIDER
                    1. 1
                      Gunnar Bittersmann
                      1. 1
                        Felix Riesterer
                        • meinung
                        • selfhtml-wiki
                        1. 0
                          Gunnar Bittersmann
                          1. 1
                            Camping_RIDER
                            1. 0
                              Gunnar Bittersmann
                              1. 1
                                Matthias Apsel
                        2. 0
                          Gunnar Bittersmann
                          1. 2
                            Tabellenkalk
                    2. 1
                      Gunnar Bittersmann
                      1. 0
                        Camping_RIDER
                        1. 1
                          Der Martin
                      2. 0
                        Matthias Apsel
          2. 1
            Gunnar Bittersmann
    2. 0
      Camping_RIDER
      1. 0
        Gunnar Bittersmann
        1. 1
          Camping_RIDER