JavaScript-Funktionsreihenfolge: Warum ist das wichtig?

106

Ursprüngliche Frage:

JSHint beschwert sich, wenn mein JavaScript eine Funktion aufruft, die weiter unten auf der Seite definiert ist als der Aufruf. Meine Seite ist jedoch für ein Spiel und es werden keine Funktionen aufgerufen, bis das Ganze heruntergeladen wurde. Warum erscheinen die Bestellfunktionen in meinem Code?

EDIT: Ich glaube, ich habe die Antwort gefunden.

http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

Ich stöhne innerlich. Sieht so aus, als müsste ich einen weiteren Tag damit verbringen, sechstausend Codezeilen neu zu bestellen. Die Lernkurve mit Javascript ist überhaupt nicht steil, aber sehr lang.

Chris Tolworthy
quelle
+1 für die hervorragende Referenz im Update. Und ich hoffe, das überzeugt Sie davon, dass Sie Ihren Code nicht wirklich nachbestellen müssen. :)
awm

Antworten:

293

tl; dr Wenn Sie nichts anrufen, bis alles geladen ist, sollte es Ihnen gut gehen.


Bearbeiten: Für eine Übersicht, die auch einige ES6-Deklarationen ( let, const) abdeckt : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

Dieses seltsame Verhalten hängt davon ab

  1. Wie definieren Sie die Funktionen und
  2. Wenn Sie sie anrufen.

Hier sind einige Beispiele.

bar(); //This won't throw an error
function bar() {}

foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
    foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
    foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
    foo(); //no error
}
var foo = function() {}
bar();

Dies liegt an etwas, das als Heben bezeichnet wird !

Es gibt zwei Möglichkeiten , um Funktionen zu definieren: Funktionsdeklaration und Funktion Ausdruck . Der Unterschied ist ärgerlich und winzig, also sagen wir einfach etwas Falsches: Wenn Sie es so schreiben function name() {}, ist es eine Deklaration , und wenn Sie es so schreiben var name = function() {}(oder eine anonyme Funktion, die einer Rückgabe zugewiesen ist, solche Dinge), ist es das eine Funktion Ausdruck .

Schauen wir uns zunächst an, wie mit Variablen umgegangen wird:

var foo = 42;

//the interpreter turns it into this:
var foo;
foo = 42;

Nun, wie Funktionsdeklarationen werden behandelt:

var foo = 42;
function bar() {}

//turns into
var foo; //Insanity! It's now at the top
function bar() {}
foo = 42;

Die varAnweisungen "werfen" die Erstellung von fooganz nach oben, weisen ihr jedoch noch keinen Wert zu. Als nächstes folgt die Funktionsdeklaration, und schließlich wird ein Wert zugewiesen foo.

Und was ist damit?

bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;

Nur die Deklaration von foowird nach oben verschoben. Die Zuordnung erfolgt erst nach dem Anruf bei bar, wo es war, bevor das gesamte Heben stattgefunden hat.

Und schließlich der Kürze halber:

bar();
function bar() {}
//turns to
function bar() {}
bar();

Nun, was Funktion Ausdrücke ?

var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();

Genau wie reguläre Variablen, zuerst foowird erklärt am höchsten Punkt des Bereichs, dann wird ein Wert zugewiesen.

Mal sehen, warum das zweite Beispiel einen Fehler auslöst.

bar();
function bar() {
    foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
    foo();
}
bar();
foo = function() {}

Wie wir zuvor gesehen haben, wird nur das Erstellen von fooangehoben, die Zuweisung erfolgt dort, wo sie im "ursprünglichen" (nicht angehobenen) Code enthalten ist. Wenn baraufgerufen wird, wird ihm vorher fooein Wert zugewiesen, also foo === undefined. Jetzt im Funktionskörper von barist es so, als ob Sie es tun undefined(), was einen Fehler auslöst.

Zirak
quelle
Tut mir leid, das herauszufinden, aber werden Überladungen wie Array.prototype.someMethod = function () {} hochgezogen? Ich bekomme anscheinend Fehler, wenn diese Art von Dingen am Ende meines Skripts stehen.
Edge
Wie stellen Sie sicher, dass alles geladen wird ? Gibt es eine gängige Praxis?
Zwcloud
6

Der Hauptgrund ist wahrscheinlich, dass JSLint die Datei nur einmal durchläuft, sodass es nicht weiß, dass Sie eine solche Funktion definieren werden.

Wenn Sie die Funktionsanweisungssyntax verwendet haben

function foo(){ ... }

Es gibt eigentlich überhaupt keinen Unterschied, wo Sie die Funktion deklarieren (sie verhält sich immer so, als ob die Deklaration am Anfang steht).

Auf der anderen Seite, wenn Ihre Funktion wie eine reguläre Variable festgelegt wurde

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

Sie müssen sicherstellen, dass Sie es nicht vor der Initialisierung aufrufen (dies kann tatsächlich eine Fehlerquelle sein).


Da das Neuanordnen von Tonnen von Code kompliziert ist und selbst eine Fehlerquelle sein kann, würde ich Ihnen empfehlen, nach einer Problemumgehung zu suchen. Ich bin mir ziemlich sicher, dass Sie JSLint den Namen globaler Variablen im Voraus mitteilen können, damit es sich nicht über nicht deklarierte Inhalte beschwert.

Geben Sie einen Kommentar zum Anfang der Datei ein

/*globals foo1 foo2 foo3*/

Oder Sie können dort ein Textfeld verwenden. (Ich denke auch, dass Sie dies in den Argumenten an die innere jslint-Funktion übergeben können, wenn Sie sich damit einmischen können.)

Hugomg
quelle
Vielen Dank. Also wird die Zeile / * globals * / funktionieren? Gut - alles, um JsHint dazu zu bringen, mich zu mögen. Ich bin noch neu in JavaScript und bekomme unerklärliche Pausen, wenn ich eine Seite aktualisiere, aber keine gemeldeten Fehler. Also dachte ich mir, dass die Lösung darin besteht, alle Regeln einzuhalten und dann zu sehen, ob es immer noch passiert.
Chris Tolworthy
4

Es gibt viel zu viele Leute, die willkürliche Regeln festlegen, wie JavaScript geschrieben werden soll. Die meisten Regeln sind völliger Müll.

Das Heben von Funktionen ist eine Funktion in JavaScript, da dies eine gute Idee ist.

Wenn Sie eine interne Funktion haben, die häufig von inneren Funktionen genutzt wird, ist das Hinzufügen am Anfang der äußeren Funktion eine akzeptable Art, Code zu schreiben. Sie hat jedoch den Nachteil, dass Sie die Details durchlesen müssen, um zu dem zu gelangen, was erreicht werden soll die äußere Funktion tut.

Sie sollten sich in Ihrer gesamten Codebasis an ein Prinzip halten, indem Sie entweder private Funktionen an die erste oder letzte Stelle in Ihrem Modul oder Ihrer Funktion setzen. JSHint ist gut, um die Konsistenz zu erzwingen, aber Sie sollten die .jshintrc ABSOLUT an Ihre Bedürfnisse anpassen und Ihren Quellcode NICHT an die verrückten Codierungskonzepte anderer Leute anpassen.

Ein Codierungsstil, den Sie möglicherweise in freier Wildbahn sehen, sollten Sie vermeiden, da er Ihnen keine Vorteile und nur mögliche Refactoring-Schmerzen bietet:

function bigProcess() {
    var step1,step2;
    step1();
    step2();

    step1 = function() {...};
    step2 = function() {...};
}

Genau diese Funktion soll das Heben von Funktionen vermeiden. Lernen Sie einfach die Sprache und nutzen Sie ihre Stärken.

Henrik Vendelbo
quelle
2

Es wird nur die Funktionsdeklaration gehisst, nicht der Funktionsausdruck (Zuweisung).

Abhay Srivastav
quelle