Warum kollidieren meine JavaScript-Funktionsnamen?

97

Ich habe das folgende Skript geschrieben, um zu sehen, was passiert, wenn einer Variablen und einer Funktion, der eine Funktion zugewiesen ist, ihre Namen kollidieren:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

Die Ausgabe, die ich bekomme, ist "Me original". Warum wurde die andere Funktion nicht aufgerufen?

Wenn ich meine ursprüngliche Zuordnung in ändere var f = new function() {, erhalte ich "Me original", gefolgt von einem TypeError-Spruch object is not a function. Kann mir bitte jemand erklären?

ankush981
quelle
26
@ Dean.DePue - Es gibt keine Verwirrung seitens JavaScript. Die Regeln für den Umgang mit ihnen sind ziemlich klar (und von Benjamin in seiner Antwort erklärt).
Quentin
4
Neugier, immer noch der beste Weg, um etwas über eine Sprache zu lernen. :-D
Cerbrus
2
Ich stelle mir auch vor, dass es für etwas so Immaterielles wie "JavaScript" ziemlich unmöglich ist, sich verwirrt (oder überhaupt emotional) zu fühlen
;-)
2
Warum sollte das Heben die Reihenfolge im zweiten Beispiel umkehren?
Cerbrus
5
Schritte, um das Wissen über Javascript zu erweitern: 1) Verwenden Sie 'use strict'. 2) Verwenden Sie immer entweder jslint oder jshint. 3) Suchen Sie nach den Dingen, über die sich jslint oder jshint beschwert. 4) Spülen und wiederholen
steve-er-rino

Antworten:

170

Funktionsdeklarationen werden in JavaScript gehisst (nach oben verschoben). Obwohl in Bezug auf die Analysereihenfolge falsch, ist der Code, den Sie haben, semantisch derselbe wie der folgende, da Funktionsdeklarationen hochgezogen werden:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Was wiederum mit Ausnahme des Funktionsnamens derselbe ist wie:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Was wiederum aufgrund des variablen Hebens dasselbe ist wie:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Was erklärt, was Sie bekommen, überschreiben Sie die Funktion. Im Allgemeinen varsind in JavaScript mehrere Deklarationen zulässig - dies var x = 3; var x = 5ist völlig legal. Im neuen ECMAScript 6-Standard letverbieten Aussagen dies.

Dieser Artikel von @kangax leistet hervorragende Arbeit bei der Entmystifizierung von Funktionen in Javascript

Benjamin Gruenbaum
quelle
2
Können Sie wirklich vereinfachen function f()zu var f = function()so viel? Sind Hebe- und Funktionsnamen wirklich der einzige Unterschied?
Djechlin
6
@djechlin im Kontext dieser Frage - ja. Im Allgemeinen ist es subtiler - siehe stackoverflow.com/questions/336859/… . Aus der Sicht des Compilers unterscheiden sie sich - aber aus der Sicht des Programmierers sind wir nah genug dran, um dies zu behaupten. Aus diesem Grund habe ich so lange hinzugefügt: "Obwohl der Code, den Sie haben, in Bezug auf die Analysereihenfolge falsch ist, ist er semantisch derselbe wie" anstatt zu sagen "ist der gleiche wie". Guter Punkt.
Benjamin Gruenbaum
1
@dotslash Bitte bearbeiten Sie Ihre ursprüngliche Frage nicht und ändern Sie sie nicht. Dies wird hier als schlecht eingestuft. Auch das Mischen mehrerer Fragen in einer Frage wird hier als schlecht eingestuft. Sie können stattdessen eine neue Frage stellen oder, wenn Sie der Meinung sind, dass sie zu geringfügig ist, in den Kommentaren um Klarstellung bitten (dafür sind sie sowieso da). Im obigen Code werden beide Versionen von fangehoben, und die "Me Original"Version wird erst später angehoben . Sie werden jeweils nach oben verschoben, jedoch in derselben Reihenfolge. Ich möchte nur hinzufügen, dass Sie im Allgemeinen nicht mehrere Funktionen auf die gleiche Weise benennen sollten :)
Benjamin Gruenbaum
5
Im strengen Modus können Sie nicht vardenselben Namen zweimal im selben Bereich verwenden.
Hoffmann
4
"Das sollte offensichtlich sein" - für Sie vielleicht, aber es war mir zu einem bestimmten Zeitpunkt nicht klar, und es war für OP nicht offensichtlich, als er danach fragte, und die Benennung und allgemeiner, wie die lexikalische Umgebung in JavaScript verwaltet wird, war eine davon von den schwierigsten Dingen zu verstehen, wenn ich zum ersten Mal JavaScript für mich lerne. Ich würde nicht so schnell Leute beleidigen, die es nicht verstehen.
Benjamin Gruenbaum
10

Wenn es nicht so aussieht, als hätte jemand Ihre Folgefrage beantwortet, werde ich sie hier beantworten, obwohl Sie Folgefragen im Allgemeinen als separate Fragen stellen sollten.

Sie haben gefragt, warum das so ist:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

druckt "Me original" aus. und dann ein Fehler.

Was hier passiert, ist, newdass die Funktion als Konstruktor verwendet wird. Das entspricht also dem Folgenden:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

Und dank der von Benjamin erläuterten Funktion zum Heben ist das oben Gesagte im Wesentlichen gleichbedeutend mit:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

Dieser Ausdruck:

var f = new function() {
    console.log("Me original.");
}

bewirkt, dass ein neues Objekt erstellt und zugewiesen wird f, wobei eine anonyme Funktion als Konstruktor verwendet wird. "Ich original." wird ausgedruckt, während der Konstruktor ausgeführt wird. Aber das Objekt, das konstruiert wird, ist selbst keine Funktion. Wenn dies schließlich ausgeführt wird:

f();

Sie erhalten eine Fehlermeldung, da dies fkeine Funktion ist.

JLRishe
quelle
Oh wunderbar! Vielen Dank, dass Sie sich die Mühe gemacht haben, darauf zu antworten! :) :)
ankush981
2

Verzeihen Sie mir, wenn dies der falsche Weg ist, einen Punkt hinzuzufügen. Ich war nicht so oft hier und würde konstruktive Anweisungen und / oder Kritik begrüßen.

Benjamins Antwort spricht die Frage des OP hervorragend an, aber ich möchte eine Optimierung hinzufügen, die uns einen vollständigen Überblick über das Heben und seine Kuriositäten gibt.

Wenn wir den ursprünglichen Code mit einem Aufruf von beginnen f, wie folgt:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

Die Ausgabe lautet dann:

Me duplicate.
Me original.

Der Grund dafür ist, dass varund functionAussagen auf leicht unterschiedliche Weise gehisst werden.

Denn vardie Deklaration wird an den Anfang des aktuellen Bereichs * verschoben, aber eine Zuordnung wird nicht gehisst. Der Wert der deklarierten Variable ist undefiniert, bis die ursprüngliche Zuweisungszeile erreicht ist.

Bei functionAnweisungen werden sowohl die Deklaration als auch die Definition angehoben. Funktion Ausdrücke , wie sie in dem verwendeten var f = function() {...Konstrukt, nicht gehisst.

Nach dem Heben ist die Ausführung so, als wäre der Code:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

* Der gesamte JavaScript-Bereich ist ein lexikalischer oder Funktionsbereich, aber es schien, als würde es die Dinge nur verwirren, das f-Wort an diesem Punkt zu verwenden.

Codelahoma
quelle