7585 Stimmen

var Funktionsname = function() {} vs function Funktionsname() {}

Ich habe vor kurzem damit begonnen, den JavaScript-Code von jemand anderem zu pflegen. Ich behebe Fehler, füge Funktionen hinzu und versuche, den Code aufzuräumen und konsistenter zu machen.

Der vorherige Entwickler verwendete zwei Arten, Funktionen zu deklarieren, und ich kann nicht herausfinden, ob es einen Grund dafür gibt oder nicht.

Es gibt zwei Möglichkeiten:

var functionOne = function() {
    // Some code
};

function functionTwo() {
    // Some code
}

Was sind die Gründe für die Verwendung dieser beiden unterschiedlichen Methoden und was sind die Vor- und Nachteile der jeweiligen Methode? Gibt es irgendetwas, das mit der einen Methode gemacht werden kann, was mit der anderen nicht möglich ist?

5564voto

Greg Punkte 306033

Der Unterschied besteht darin, dass functionOne ist ein Funktionsausdruck und daher nur definiert, wenn diese Zeile erreicht wird, während functionTwo ist eine Funktionsdeklaration und wird definiert, sobald die sie umgebende Funktion oder das Skript ausgeführt wird (aufgrund von Heben ).

Zum Beispiel ein Funktionsausdruck:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

Und, eine Funktionserklärung:

// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

In der Vergangenheit wurden Funktionsdeklarationen, die innerhalb von Blöcken definiert wurden, in den verschiedenen Browsern uneinheitlich gehandhabt. Der (mit ES5 eingeführte) strikte Modus löste dieses Problem, indem Funktionsdeklarationen auf den sie umschließenden Block beschränkt wurden.

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError

0 Stimmen

Funktionsdefinitionen werden ausgeführt, wenn der Code in den umgebenden Block eintritt, und nicht, wenn er in die einschließende Funktion eintritt. Ich weiß nicht, ob das immer so funktioniert hat, aber es wäre unvermeidlich, wenn ein Block let o const um eine Variable zu definieren, die durch eine Funktion in ihr geschlossen wurde, und die konsequente Anwendung dieser Regel ist wahrscheinlich besser, als sie nur anzuwenden, wenn es unvermeidbar ist.

44 Stimmen

Der Satz "aufgrund des Hebens" könnte den falschen Eindruck erwecken, dass nur die genannte Funktion gehoben wird. In Wirklichkeit werden beide var functionOne wie auch function functionTwo werden bis zu einem gewissen Grad hochgezogen - es ist nur so, dass functionOne auf undefiniert gesetzt ist (man könnte es als halbes Hochziehen bezeichnen, Variablen werden immer nur bis zu diesem Grad hochgezogen), wohingegen functionTwo vollständig hochgezogen ist, da sie definiert und deklariert ist. Wenn man etwas aufruft, das nicht definiert ist, wird natürlich ein typeError ausgelöst.

0 Stimmen

@rails_has_elegance Was bringt es also, es "halb hochgezogen" zu nennen, wenn es sich genauso verhält wie "gar nicht hochgezogen"?

2092voto

Eugene Lazutkin Punkte 43092

Zunächst möchte ich Greg korrigieren: function abc(){} ist auch skaliert - der Name abc in dem Bereich definiert ist, in dem diese Definition vorkommt. Beispiel:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

Zweitens ist es möglich, beide Stile zu kombinieren:

var xyz = function abc(){};

xyz wird wie üblich definiert, abc ist in allen Browsern außer dem Internet Explorer undefiniert - verlassen Sie sich nicht darauf, dass es definiert ist. Sie wird aber innerhalb des Textkörpers definiert sein:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

Wenn Sie Funktionen auf allen Browsern aliasieren wollen, verwenden Sie diese Art von Deklaration:

function abc(){};
var xyz = abc;

In diesem Fall sind beide xyz y abc sind Aliasnamen desselben Objekts:

console.log(xyz === abc); // prints "true"

Ein zwingender Grund für die Verwendung des kombinierten Stils ist das "name"-Attribut von Funktionsobjekten ( wird von Internet Explorer nicht unterstützt ). Wenn Sie eine Funktion definieren wie

function abc(){};
console.log(abc.name); // prints "abc"

wird sein Name automatisch zugewiesen. Wenn Sie ihn jedoch wie folgt definieren

var abc = function(){};
console.log(abc.name); // prints ""

sein Name ist leer - wir haben eine anonyme Funktion erstellt und sie einer Variablen zugewiesen.

Ein weiterer guter Grund für die Verwendung des kombinierten Stils ist die Verwendung eines kurzen internen Namens, um auf sich selbst zu verweisen, während für externe Benutzer ein langer, nicht widersprüchlicher Name verwendet wird:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

Im obigen Beispiel könnten wir das Gleiche mit einem externen Namen tun, aber das wäre zu unhandlich (und langsamer).

(Eine andere Möglichkeit, auf sich selbst zu verweisen, ist die Verwendung von arguments.callee was immer noch relativ lang ist und im strengen Modus nicht unterstützt wird).

In der Tiefe behandelt JavaScript beide Aussagen unterschiedlich. Dies ist eine Funktionsdeklaration:

function abc(){}

abc ist hier überall im aktuellen Geltungsbereich definiert:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

Außerdem hob es durch eine return Erklärung:

// We can call it here
abc(); // Works
return;
function abc(){}

Dies ist ein Funktionsausdruck:

var xyz = function(){};

xyz wird hier vom Zeitpunkt der Zuweisung an definiert:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

Funktionsdeklaration vs. Funktionsausdruck ist der eigentliche Grund, warum es einen Unterschied gibt, den Greg aufgezeigt hat.

Lustige Tatsache:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

Ich persönlich bevorzuge die Deklaration "Funktionsausdruck", weil ich auf diese Weise die Sichtbarkeit kontrollieren kann. Wenn ich die Funktion wie folgt definiere

var abc = function(){};

Ich weiß, dass ich die Funktion lokal definiert habe. Wenn ich die Funktion wie folgt definiere

abc = function(){};

Ich weiß, dass ich es global definiert habe, vorausgesetzt, ich habe nicht definiert abc an beliebiger Stelle in der Kette der Geltungsbereiche. Diese Art der Definition ist auch dann belastbar, wenn sie innerhalb von eval() . Während die Definition

function abc(){};

hängt vom Kontext ab und lässt möglicherweise erahnen, wo es tatsächlich definiert ist, insbesondere im Fall von eval() - Die Antwort lautet: Es kommt auf den Browser an.

0 Stimmen

Var abc = function(){}; console.log(abc.name); // "abc" // ab 2021

4 Stimmen

Offenbar ist die JS-Laufzeitumgebung intelligenter geworden. Doch packen Sie es ein und: var abc = (() => function(){})(); console.log(abc.name); // nichts

0 Stimmen

@EugeneLazutkin Sie führen die Funktion aus und versuchen, den Namen des Ergebnisses zu lesen. Entfernen Sie den '();' Teil und Ihr Beispiel wird e korrekt ;)

736voto

T.J. Crowder Punkte 948310

Im Folgenden finden Sie eine Übersicht über die Standardformulare zur Erstellung von Funktionen: (Ursprünglich für eine andere Frage geschrieben, aber angepasst, nachdem sie in die kanonische Frage verschoben wurde).

Bedingungen:

Die kurze Liste:

  • Funktionserklärung

  • "Anonym" function Ausdruck (die trotz der Bezeichnung manchmal Funktionen mit Namen erzeugen)

  • Benannt function Ausdruck

  • Initialisierung von Zugriffsfunktionen (ES5+)

  • Pfeil-Funktionsausdruck (ES2015+) (die, wie anonyme Funktionsausdrücke, keinen expliziten Namen beinhalten und dennoch Funktionen mit Namen erstellen können)

  • Methodendeklaration im Objektinitialisierer (ES2015+)

  • Konstruktor- und Methodendeklarationen in class (ES2015+)

Funktionserklärung

Die erste Form ist eine Funktionsdeklaration die wie folgt aussieht:

function x() {
    console.log('x');
}

Eine Funktionsdeklaration ist eine 宣言 Es ist keine Aussage oder ein Ausdruck. Als solcher folgen Sie ihm nicht mit einem ; (obwohl dies harmlos ist).

Eine Funktionsdeklaration wird verarbeitet, wenn die Ausführung in den Kontext eintritt, in dem sie erscheint, antes de ein schrittweiser Code ausgeführt wird. Die erstellte Funktion wird mit einem eigenen Namen versehen ( x im obigen Beispiel), und dieser Name wird in den Bereich gesetzt, in dem die Erklärung erscheint.

Da er vor dem Schritt-für-Schritt-Code im gleichen Kontext verarbeitet wird, können Sie Dinge wie diese tun:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

Bis ES2015 war in der Spezifikation nicht festgelegt, was eine JavaScript-Engine tun soll, wenn Sie eine Funktionsdeklaration innerhalb einer Kontrollstruktur wie try , if , switch , while usw., etwa so:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

Und da sie verarbeitet werden antes de Schritt-für-Schritt-Code ausgeführt wird, ist es knifflig zu wissen, was zu tun ist, wenn sie sich in einer Kontrollstruktur befinden.

Obwohl dies nicht der Fall war angegeben bis ES2015 war es ein zulässige Erweiterung um Funktionsdeklarationen in Blöcken zu unterstützen. Unglücklicherweise (und zwangsläufig) taten die verschiedenen Motoren unterschiedliche Dinge.

Ab ES2015 sagt die Spezifikation, was zu tun ist. In der Tat gibt es drei verschiedene Dinge zu tun:

  1. Wenn im losen Modus no in einem Webbrowser soll die JavaScript-Engine vor allem eines tun
  2. Im losen Modus eines Webbrowsers soll die JavaScript-Engine etwas anderes tun
  3. Wenn in Streng Modus (Browser oder nicht), soll die JavaScript-Engine noch etwas anderes tun

Die Regeln für die losen Modi sind kompliziert, aber in Streng Modus sind Funktionsdeklarationen in Blöcken einfach: Sie sind lokal für den Block (sie haben Blockumfang was ebenfalls neu in ES2015 ist), und sie werden an die Spitze des Blocks gehievt. So:

"use strict";
if (someCondition) {
    foo();               // Works just fine
    function foo() {
    }
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
                         // because it's not in the same block)

"Anonym" function Ausdruck

Die zweite gängige Form wird als anonymer Funktionsausdruck :

var y = function () {
    console.log('y');
};

Wie alle Ausdrücke wird er ausgewertet, wenn er bei der schrittweisen Ausführung des Codes erreicht wird.

In ES5 hat die so erstellte Funktion keinen Namen (sie ist anonym). In ES2015 wird der Funktion ein Name zugewiesen, wenn möglich, indem er aus dem Kontext abgeleitet wird. Im obigen Beispiel würde der Name lauten y . Etwas Ähnliches wird getan, wenn die Funktion der Wert eines Eigenschaftsinitialisierers ist. (Für Details darüber, wann dies geschieht und die Regeln, suchen Sie nach SetFunctionName im die Spezifikation  - es scheint überall den Ort.)

Benannt function Ausdruck

Die dritte Form ist eine benannter Funktionsausdruck ("NFE"):

var z = function w() {
    console.log('zw')
};

Die so erzeugte Funktion hat einen eigenen Namen ( w in diesem Fall). Wie alle Ausdrücke wird auch dieser ausgewertet, wenn er bei der schrittweisen Ausführung des Codes erreicht wird. Der Name der Funktion lautet no zu dem Bereich hinzugefügt, in dem der Ausdruck erscheint; der Name ist im Geltungsbereich der Funktion selbst:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

Beachten Sie, dass NFEs häufig eine Quelle von Fehlern für JavaScript-Implementierungen gewesen sind. IE8 und frühere Versionen behandeln NFEs zum Beispiel völlig falsch Damit wurden zwei verschiedene Funktionen zu zwei verschiedenen Zeitpunkten geschaffen. Frühe Versionen von Safari hatten ebenfalls Probleme. Die gute Nachricht ist, dass aktuelle Versionen von Browsern (IE9 und höher, aktueller Safari) diese Probleme nicht mehr haben. (Zum Zeitpunkt der Erstellung dieses Artikels ist der IE8 leider immer noch weit verbreitet, so dass die Verwendung von NFEs mit Code für das Web im Allgemeinen immer noch problematisch ist).

Initialisierung von Zugriffsfunktionen (ES5+)

Manchmal schleichen sich Funktionen weitgehend unbemerkt ein; das ist der Fall bei Accessorfunktionen . Hier ist ein Beispiel:

var obj = {
    value: 0,
    get f() {
        return this.value;
    },
    set f(v) {
        this.value = v;
    }
};
console.log(obj.f);         // 0
console.log(typeof obj.f);  // "number"

Beachten Sie, dass ich bei der Verwendung der Funktion nicht () ! Das liegt daran, dass es ein Accessorfunktion für eine Immobilie. Wir erhalten und setzen die Eigenschaft auf die normale Weise, aber hinter den Kulissen wird die Funktion aufgerufen.

Sie können auch Accessor-Funktionen erstellen mit Object.defineProperty , Object.defineProperties und das weniger bekannte zweite Argument zu Object.create .

Pfeil-Funktionsausdruck (ES2015+)

ES2015 bringt uns die Pfeilfunktion . Hier ist ein Beispiel:

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

Siehe das n => n * 2 Ding, das sich in der map() anrufen? Das ist eine Funktion.

Ein paar Dinge zu den Pfeilfunktionen:

  1. Sie haben keine eigene this . Stattdessen haben sie zu Ende gehen el this des Kontextes, in dem sie definiert sind. (Sie schließen auch über arguments und, sofern zutreffend, super .) Dies bedeutet, dass die this in ihnen ist die gleiche wie die this wo sie erstellt werden, und können nicht geändert werden.

  2. Wie Sie sicher schon bemerkt haben, verwenden Sie das Schlüsselwort function ; stattdessen verwenden Sie => .

Le site n => n * 2 Das obige Beispiel ist eine Form von ihnen. Wenn Sie mehrere Argumente haben, um die Funktion zu übergeben, verwenden Sie Parens:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

(Denken Sie daran, dass Array#map übergibt den Eintrag als erstes Argument und den Index als zweites).

In beiden Fällen ist der Körper der Funktion nur ein Ausdruck; der Rückgabewert der Funktion wird automatisch das Ergebnis dieses Ausdrucks sein (Sie verwenden keine explizite return ).

Wenn Sie mehr als nur einen einzigen Ausdruck verwenden, sollten Sie {} und eine ausdrückliche return (wenn Sie einen Wert zurückgeben müssen), wie üblich:

var a = [
  {first: "Joe", last: "Bloggs"},
  {first: "Albert", last: "Bloggs"},
  {first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
  var rv = a.last.localeCompare(b.last);
  if (rv === 0) {
    rv = a.first.localeCompare(b.first);
  }
  return rv;
});
console.log(JSON.stringify(a));

Die Version ohne { ... } wird als Pfeilfunktion mit einer Ausdrucksorgan o prägnanter Aufbau . (Auch: A kurz und bündig Pfeilfunktion). Die mit { ... } die den Körper definiert, ist eine Pfeilfunktion mit einer Funktionsrumpf . (Auch: A Ausführlich Pfeilfunktion).

Methodendeklaration im Objektinitialisierer (ES2015+)

ES2015 erlaubt eine kürzere Form der Deklaration einer Eigenschaft, die auf eine Funktion namens Methodendefinition Es sieht folgendermaßen aus:

var o = {
    foo() {
    }
};

das fast gleichwertige in ES5 und früher wäre:

var o = {
    foo: function foo() {
    }
};

Der Unterschied (abgesehen von der Ausführlichkeit) besteht darin, dass eine Methode Folgendes verwenden kann super aber eine Funktion nicht. Wenn Sie also zum Beispiel ein Objekt haben, das (sagen wir) definiert valueOf unter Verwendung der Methodensyntax, könnte es super.valueOf() um den Wert zu erhalten Object.prototype.valueOf zurückgegeben hätte (bevor es vermutlich etwas anderes damit gemacht hätte), während die ES5-Version Folgendes tun müsste Object.prototype.valueOf.call(this) stattdessen.

Das bedeutet auch, dass die Methode einen Verweis auf das Objekt hat, für das sie definiert wurde. Wenn dieses Objekt also temporär ist (zum Beispiel, wenn Sie es in Object.assign als eines der Quellobjekte), Methodensyntax könnte bedeutet, dass das Objekt im Speicher verbleibt, obwohl es andernfalls hätte entsorgt werden können (wenn die JavaScript-Engine diese Situation nicht erkennt und behandelt, wenn keine der Methoden mit super ).

Konstruktor- und Methodendeklarationen in class (ES2015+)

ES2015 bringt uns class Syntax, einschließlich deklarierter Konstruktoren und Methoden:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

Oben sind zwei Funktionen deklariert: Eine für den Konstruktor, der den Namen Person und eine für getFullName die eine Funktion ist, die der Person.prototype .

175voto

Christian C. Salvadó Punkte 763569

Was den globalen Kontext betrifft, so sind sowohl die var Anweisung und eine FunctionDeclaration am Ende wird eine nicht löschbar Eigenschaft des globalen Objekts, aber der Wert der beiden kann überschrieben werden .

Der feine Unterschied zwischen den beiden Möglichkeiten besteht darin, dass die Instanziierung von Variablen Prozess läuft (vor der eigentlichen Code-Ausführung), werden alle Bezeichner, die mit var wird initialisiert mit undefined und die, die von der FunctionDeclaration zum Beispiel ab diesem Zeitpunkt zur Verfügung stehen werden:

 alert(typeof foo); // 'function', it's already available
 alert(typeof bar); // 'undefined'
 function foo () {}
 var bar = function () {};
 alert(typeof bar); // 'function'

Die Zuordnung der bar FunctionExpression findet bis zur Laufzeit statt.

Eine globale Eigenschaft, die durch eine FunctionDeclaration kann wie ein Variablenwert problemlos überschrieben werden, z.B.:

 function test () {}
 test = null;

Ein weiterer offensichtlicher Unterschied zwischen Ihren beiden Beispielen ist, dass die erste Funktion keinen Namen hat, die zweite aber schon, was bei der Fehlersuche sehr nützlich sein kann (z. B. bei der Untersuchung eines Aufrufstapels).

Zu Ihrem bearbeiteten ersten Beispiel ( foo = function() { alert('hello!'); }; ), handelt es sich um eine nicht deklarierte Zuweisung, ich würde Ihnen dringend empfehlen, immer die var Stichwort.

Mit einem Auftrag, ohne die var Anweisung wird der referenzierte Bezeichner, wenn er nicht in der Bereichskette gefunden wird, zu einem löschbar Eigenschaft des globalen Objekts.

Außerdem führen nicht deklarierte Zuweisungen zu einer ReferenceError zu ECMAScript 5 unter Strenger Modus .

Unbedingt lesen:

Hinweis : Diese Antwort wurde zusammengelegt von weitere Frage in dem der größte Zweifel und das größte Missverständnis des Auftraggebers darin bestand, dass Bezeichner, die mit einem FunctionDeclaration nicht überschrieben werden konnte, was nicht der Fall ist.

146voto

thomasrutter Punkte 109036

Die beiden Codeschnipsel, die Sie dort gepostet haben, verhalten sich für fast alle Zwecke gleich.

Der Unterschied im Verhalten besteht jedoch darin, dass bei der ersten Variante ( var functionOne = function() {} ), kann diese Funktion erst nach diesem Punkt im Code aufgerufen werden.

Bei der zweiten Variante ( function functionTwo() ), steht die Funktion dem Code zur Verfügung, der oberhalb der Stelle läuft, an der die Funktion deklariert ist.

Denn bei der ersten Variante wird die Funktion der Variablen foo zur Laufzeit. Im zweiten Fall wird die Funktion diesem Bezeichner zugewiesen, foo zum Zeitpunkt des Parsens.

Weitere technische Informationen

JavaScript hat drei Möglichkeiten, Funktionen zu definieren.

  1. Ihr erstes Snippet zeigt eine Funktionsausdruck . Dies beinhaltet die Verwendung der Operator "Funktion" um eine Funktion zu erstellen - das Ergebnis dieses Operators kann in einer beliebigen Variablen oder Objekteigenschaft gespeichert werden. Der Funktionsausdruck ist auf diese Weise sehr mächtig. Der Funktionsausdruck wird oft als "anonyme Funktion" bezeichnet, weil er keinen Namen haben muss,
  2. Ihr zweites Beispiel ist ein Funktionsdeklaration . Dabei wird die Anweisung "Funktion" um eine Funktion zu erstellen. Die Funktion wird zum Zeitpunkt des Parsens verfügbar gemacht und kann überall in diesem Bereich aufgerufen werden. Sie können sie auch später noch in einer Variablen oder Objekteigenschaft speichern.
  3. Die dritte Möglichkeit, eine Funktion zu definieren, ist die "Funktion()"-Konstruktor die in Ihrem ursprünglichen Beitrag nicht enthalten ist. Es wird nicht empfohlen, dies zu verwenden, da es auf die gleiche Weise funktioniert wie eval() was seine Probleme hat.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X