Selbstreferenzierende anonyme Schließungen: Ist JavaScript unvollständig?

18

Zeigt die Tatsache, dass anonyme selbstreferenzierende Funktionsabschlüsse in JavaScript so häufig vorkommen, dass JavaScript eine unvollständige Spezifikation ist? Wir sehen so viel davon:

(function () { /* do cool stuff */ })();

und ich nehme an, alles ist Geschmackssache, aber sieht das nicht wie ein Kludge aus, wenn Sie nur einen privaten Namespace wollen? Konnte JavaScript keine Pakete und richtigen Klassen implementieren?

Vergleichen Sie mit ActionScript 3, das ebenfalls auf ECMAScript basiert

package com.tomauger {
  import bar;
  class Foo {
     public function Foo(){
       // etc...
     }

     public function show(){
       // show stuff
     }

     public function hide(){
       // hide stuff
     }
     // etc...
  }
}

Im Gegensatz zu den Konvolutionen, die wir in JavaScript ausführen (aus der jQuery-Plugin-Authoring-Dokumentation ):

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

Ich bin mir bewusst, dass diese Frage leicht zu einem Rant über Vorlieben und Programmierstile ausarten kann, aber ich bin tatsächlich sehr gespannt, wie Sie als erfahrene Programmierer diesbezüglich denken und ob es sich natürlich anfühlt, verschiedene Eigenheiten einer neuen Sprache zu lernen, oder kludgy , wie eine Problemumgehung für einige grundlegende Programmiersprachenkomponenten, die einfach nicht implementiert sind?

Tom Auger
quelle
22
"Konnte JavaScript nicht ... richtige Klassen implementieren?" Nein, es gibt bereits richtige Prototypen. Prototypen sind Klassen nicht unterlegen. Sie sind anders. Die Leute haben zu verschiedenen Zeiten versucht, Klassen zu JavaScript hinzuzufügen und waren ziemlich erfolglos.
Rein Henrichs
5
@Rein: Und trotzdem hat es ActionScript irgendwie geschafft ...
Mason Wheeler
8
@ Tom Es gibt keine "eingebauten Klassen". Es gibt keine Klasse in einer prototypischen Sprache . Sie verschmelzen immer wieder die beiden Paradigmen.
Rein Henrichs
1
Ich persönlich finde die anonyme Funktionssprache flexibler als Klassen. Ich mag jedoch funktionale Programmierung, in der diese Redewendung üblich ist.
Dietbuddha
1
Für andere ist hier: brianodell.net/?page_id=516 eine großartige Einführung in JavaScript als Prototypsprache.
Tom Auger

Antworten:

9

Ich nehme an, alles ist Geschmackssache, aber sieht das nicht wie ein Kludge aus, wenn Sie nur einen privaten Namespace wollen? Konnte JavaScript keine Pakete und richtigen Klassen implementieren?

Die meisten Kommentare sprechen gegen den Mythos, dass "Prototypen Klassen für arme Männer sind", und ich wiederhole nur, dass prototypbasierte OO in keiner Weise klassenbasierten OO unterlegen sind.

Der andere Punkt "ein Kludge, wenn alles, was Sie wollen, einen privaten Namespace". Es könnte Sie überraschen, dass Scheme genau dasselbe Kludge verwendet, um Bereiche zu definieren. Das hat nicht aufgehört, das archetypische Beispiel für lexikalisches Scoping zu werden.

Natürlich ist der 'Kludge' im Schema hinter Makros versteckt ....

Javier
quelle
1
Sie legen keine Beweise vor, die Ihre Behauptung stützen, dass Schema das wichtigste Beispiel für eine gut gemachte lexikalische Festlegung ist oder dass dies in irgendeiner Weise damit zu tun hat, wie Schema Funktionen zum Definieren von Gültigkeitsbereichen verwendet.
DeadMG
Ich kann nicht davon sprechen, dass Schema ein Vorbild ist, aber ein Beispiel dafür, dass JS-Mitgestalter Brendan Eich das Schema diskutiert, das eine Rolle in JSs Design spielt: readwrite.com/2011/07/22/javascript-was-no-accident
Erik Reppen
7

Zunächst ein paar Dinge:

  1. Eine andere Sichtweise auf JavaScript sind die 1 Million und 1 Dinge, die Sie mit der Funktion als Konstrukt tun können. Es ist alles da, wenn Sie danach suchen. Es ist einfach nie weit von einer Funktion entfernt.

  2. Das jQuery-Plug-in-Authoring-Ding ist schrecklich. Ich habe keine Ahnung, warum sie das befürworten. $ -Erweiterungen sollten allgemein verwendbare Dinge sein, die $ bereits ziemlich gut behandelt hat und keine Build-me-a-complete-Widget-Methoden. Es ist ein DOM-API-Normalisierungstool. Die Verwendung ist am besten in Ihren eigenen Objekten begraben. Ich sehe den Reiz, es als vollwertiges UI-Bibliotheks-Repository zu verwenden, nicht.

Pakete im clientseitigen Web sind sinnlos

Was ich persönlich an Paketen im clientseitigen Web nicht mag, ist, dass wir im Grunde so tun, als würden wir etwas tun, was wir wirklich nicht tun. In einem Post-.NET-Webformular und einem schrecklichen Zeug, das niemals aus unserer Java-Freundewelt herausgekommen ist, stelle ich mir lieber ein Stück HTML mit verknüpften Ressourcen als das vor, was es wirklich ist und versuchen Sie nicht, lernresistente OS-App-Entwickler zu beschwichtigen, indem Sie so tun, als wäre es etwas anderes. In JS auf der Client-Seite wird nichts "importiert", es sei denn, Ajax macht etwas Schreckliches, das Browser-Caching nicht kennt, was ja viele versucht haben. Für den Browser ist nur wichtig, dass er entweder geladen und interpretiert wurde oder nicht. Auf dem Client ist kein Code mehr gespeichert, der "nur für den Fall" verwendet werden kann. aus guten gründen. Das erste Problem ist, dass ich gerade die Plug-In- und Browser-Plug-In-Abhängigkeiten für Web-Apps als Phänomen beschrieben habe, das im Allgemeinen nicht gut funktioniert hat. Wir wollen jetzt Web. Nicht nach dem dritten Update von Adobe oder Sun in dieser Woche.

Die Sprache hat, was sie für die Struktur braucht

JS-Objekte sind in hohem Maße veränderbar. Wir können verzweigte Bäume von Namespaces haben, zu jedem Grad, den wir für nützlich halten und es ist sehr einfach zu tun. Aber ja, für alles, was wiederverwendbar ist, muss man die Wurzel einer Bibliothek im globalen Raum stecken. Alle Abhängigkeiten werden ohnehin gleichzeitig verknüpft und geladen. Wozu also sonst noch etwas tun? Der Punkt, den globalen Namespace zu vermeiden, ist nicht, dass irgendetwas dort schlecht ist. Es ist zu viel Zeug, weil Sie das Risiko von Namespace-Kollisionen oder versehentlichem Überschreiben von Kernsprachen-Features eingehen.

Nur weil es beliebt ist, heißt das noch lange nicht, dass wir es richtig machen

Wenn Sie dies nun in einer clientseitigen Web-App sehen:

(function(){
//lots of functions defined and fired and statement code here
})()

Das Problem ist nicht, dass uns Tools fehlen, um eine App zu strukturieren. Das Problem ist, dass die Leute die Struktur nicht schätzen. Für 2-3-seitige einmalige temporäre Wegwerfstellen bei einer Designagentur habe ich damit kein wirkliches Problem. Hässlich wird es, wenn Sie etwas Wartbares und Lesbares bauen müssen, das sich leicht modifizieren lässt.

Wenn Sie jedoch an den Ort gelangen, an dem es Zeit ist, alle wiederverwendbaren Objekte und Fabriken zu implementieren, und sich möglicherweise ein oder zwei neue temporäre Vars in diesen Prozess einschleichen, ist dies eine Annehmlichkeit.

Es gibt jedoch Implementierungen von JS mit Paketen / Modulen

Denken Sie daran, dass in Node.js, wo solche Dinge viel sinnvoller sind, Module vorhanden sind. JS ist das einzige, was in der Gleichung steht, vorausgesetzt, wir können die Uber-Config-Hölle vermeiden, die andere Sprachen plagt, und jede ausgeführte Datei hat ihren eigenen isolierten Gültigkeitsbereich. Auf einer Webseite ist das Verknüpfen einer js-Datei selbst die import-Anweisung. Schnelleres Importieren ist nur eine Verschwendung von Zeit und Ressourcen, da das Abrufen der Ressourcen viel mehr Aufwand erfordert als das Hinzufügen von Links zu Dateien, wenn Sie wissen, dass sie in einem Browser zwischengespeichert werden, wenn eine andere Seite sie erneut benötigt. Versuchen Sie also, den globalen Raum aufzuteilen, indem Sie etwas anderes tun, als Adapterobjektfactorys wie jQuery oder traditionellere Objekte zu erstellen, die eine große Teilmenge von Aufgaben in einer bestimmten Domäne abdecken und gleichzeitig einen Platz in der globalen Datenbank einnehmen. Dort'http://wiki.ecmascript.org/doku.php?id=harmony:modules

Nein, es ist nichts falsch an Auto-Invokern, die verwendet werden, um globale Namespace-Verschmutzung zu vermeiden, wenn es einen guten Grund gibt, solche Dinge zu verwenden (meistens gibt es keinen). Und wir haben persistente privat äquivalente Eigenschaften in unseren Objekten (definieren Sie einfach eine Variable im Konstruktor und machen Sie sie nicht als Eigenschaft verfügbar).

Die Tatsache, dass wir solche Dinge tun KÖNNEN, ist jedoch fantastisch. Starke Auslastung ist ein Zeichen dafür, dass JS-Entwickler vielleicht noch reifen, aber es ist kein Loch in der Sprache für jeden, der nicht versucht, ein Paradigma in das clientseitige Web zu zwingen, das hier einfach keinen Sinn ergibt.

Erik Reppen
quelle
Könnten Sie dem Down-Voter bitte erklären, warum? Wenn jemand so viel schreibt, hat er meiner Meinung nach eine Erklärung verdient!
Songo
+1 gute Antwort, nicht sicher, warum die Abstimmung vor.
Pllee
Großartiges Schreiben und großartige Perspektive. Ich mag auch das "Nur weil es ein Trope ist, heißt das nicht, dass es richtig ist." Ich denke, mein Problem ist, dass ich mich mit strengeren Sprachen wohler fühle, was die Entwicklungseffizienz in vielerlei Hinsicht fördert. JavaScript scheint wirklich verwaschen zu sein, da nicht viele Checks und Balances eingebaut sind: Sie können tun, was Sie wollen, daher ist die Landschaft da draußen ein Durcheinander von Redewendungen und Praktiken. Es ist schwer, den "richtigen" Weg zu finden, um sich Ihrer Codierungsstruktur zu nähern. Ich stimme zwar zu, dass dies für schnelle Einzeljobs kein großes Problem ist.
Tom Auger
1
IMO, es gibt eine Menge Dinge, die Sie mit viel Seil tun können, außer sich selbst aufzuhängen, und Sie lernen, robusten Code schneller zu schreiben, wenn Sie gelegentlich selbst aufhängen, was seltener vorkommt, wenn Sie bessere Gewohnheiten entwickeln jeder oder ein idealer Kandidat für jeden Job. Ich vermute, je mehr Sie darüber erfahren, desto erträglicher werden Sie es finden. Ich habe das Gefühl, dass mir die Hälfte meines Gehirns fehlt, wenn ich versuche, Dinge in Sprachen ohne erstklassige Funktionen oder Objekte zu tun, die so flexibel / wandelbar sind wie die von JS.
Erik Reppen
4

Das andere, was Sie vermissen, ist, dass javscript abwärtskompatibel sein muss. Wenn Sie versuchen, die Paketsyntax einzuführen, kann dies das Web auf verrückte Weise zerstören. Das wäre schlimm! Doug Crockford hat an verschiedenen Stellen darüber gesprochen und warum die Versuche, es hinzuzufügen, fehlgeschlagen sind.

Zachary K
quelle
Das ist ein guter Punkt. ActionScript schaffte es dennoch, indem es einfach eine neue Version herausbrachte. Bei der Definition Ihres Skript-Tags konnten Sie immer die JavaScript-Version angeben, sodass das "Brechen" vorhandener Websites kein Problem sein sollte.
Tom Auger
1
In der Praxis haben die meisten Skript-Tags im Internet keine Versionsnummer. Um ehrlich zu sein, bin ich mir nicht sicher, was dieses Problem angeht, aber ich weiß, dass die Leute, die über dieses Zeug nachdenken, entschieden haben, dass es nicht machbar ist.
Zachary K
1
@Tom: Adobe hat auch die volle Kontrolle über die Flash-Plattform. Niemand hat die volle Kontrolle über alle JS-Plattformen. Wenn Sie lediglich eine Versionsnummer für ein JS-Skript in einem Browser eingeben, werden ältere Browser entweder nicht unterstützt oder Sie müssen zwei Skripts schreiben. Es ist also ein Problem.
Jeremy Heiler
2

Ja, es ist ein Kludge.

Viele Leute sagen, dass "Prototypen den Klassen nicht unterlegen sind". Ich bin anderer Meinung, aber das ist eine Frage der Präferenz. Aber das ist noch nicht einmal das eigentliche Problem mit JavaScript - das Problem ist, dass es ursprünglich als schnelle und schmutzige Skriptsprache für animierte Schaltflächen entwickelt wurde. Mitte der 90er hätte niemand gedacht, dass JavaScript dazu aufgefordert werden würde, einige der verrückten Dinge zu tun, die es derzeit macht.

Mike Baranczak
quelle
6
Ich bin nicht einverstanden, JavaScript die Sprache ist eigentlich wirklich super. Daß es zusammen mit einem unter spezifizierten und gegenseitig inkompatiblen DOM zusammengefaßt wurde, ist der Ausgangspunkt für alle Probleme.
Dean Harding
2
Was hat das mit Prototypen zu tun?
Erik Reppen
2

Anonyme selbstaufrufende Funktionen ähneln eher Modulen als Klassen. Es ist ärgerlich, dass JavaScript standardmäßig im globalen Bereich ausgeführt wird. Das Komitee, das an JS.next arbeitet, erwägt ernsthaft, Module hinzuzufügen, damit Sie Ihre lokalen Variablen nicht in den globalen Bereich verschieben. Zum Glück haben die Funktionen von Javascript eine so praktische Semantik, dass wir eine anonyme Funktion relativ einfach als privaten Bereich verwenden können.

Ich verstehe überhaupt nicht, wie Klassen wirklich in die Diskussion eintreten, außer dass sie in vielen Sprachen das Top-Level-Scoping-Konstrukt sind. Ein besseres Modul / Paket / bitte-gib-mir-einen-lokalen-Gültigkeitsbereich-damit-ich-meine-Variablen-in-der-globalen-Umgebung-nicht-lasse-Konstrukt wäre sehr schön zu haben.

Sean McMillan
quelle
1

Vielleicht möchten Sie einen Blick auf ExtJS 3 und 4 werfen, wo es ihnen gelungen ist, Namespaces recht gut zu implementieren.

- hinzugefügt nach -1

Mein Punkt hier war, es ist möglich, all diese 'Windungen' zu verbergen und trotzdem einen ziemlich benutzerfreundlichen Code wie diesen zu haben:

Ext.ns('com.tomauger');
Ext.Loader.load('bar.js'); //unfortunately filname needs to be used
MyNameSpace.Foo = {
   //...
}
Mchl
quelle