Wie schreibe ich eine benannte Pfeilfunktion in ES2015?

204

Ich habe eine Funktion, die ich in ES6 in die neue Pfeilsyntax konvertieren möchte . Es ist eine benannte Funktion:

function sayHello(name) {
    console.log(name + ' says hello');
}

Gibt es eine Möglichkeit, ihm einen Namen ohne var-Anweisung zu geben:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

Natürlich kann ich diese Funktion erst verwenden, nachdem ich sie definiert habe. So etwas wie folgendes:

sayHello = (name) => {
        console.log(name + ' says hello');
    }

Gibt es eine neue Möglichkeit, dies in ES6 zu tun ?

jhamm
quelle
3
Ist das nicht der Punkt der Pfeil - Syntax nicht gibt der Funktion einen Namen?
AstroCB
64
Nicht unbedingt, Pfeilfunktionen behalten den lexikalischen Bereich bei, daher wäre es ziemlich nützlich, eine Abkürzung zu haben, um benannte Funktionen (nützlich für eine Stapelverfolgung) mit lexikalischem Bereich zu erzeugen
Matt Styles
6
Was ist los mit dem zweiten Snippet?
Bergi
Sie können mit Sicherheit auf einen zugewiesenen Namen im Hauptteil der Funktion verweisen: var sayHello = (name) => {console.log (sayHello); }; Und bei rekursiven Funktionen tun Sie oft genau das. Kein besonders nützliches Beispiel, aber hier ist eine Funktion, die sich selbst zurückgibt, wenn sie ihr Argument nicht erhält: var sayHello = (name) => name? Console.log (name + 'sagt hallo'): sayHello; sayHello () ('frank'); // -> "Frank sagt Hallo"
Dtipson
4
Der Titel der Frage ist im Vergleich zum Inhalt äußerst irreführend, da Sie ausgeschlossen haben, wie Sie Pfeilfunktionen benennen (dies ist das zweite Snippet).
TJ Crowder

Antworten:

211

Wie schreibe ich eine benannte Pfeilfunktion in ES2015?

Sie tun dies so, wie Sie es in Ihrer Frage ausgeschlossen haben: Sie platzieren es auf der rechten Seite einer Zuweisung oder eines Eigenschaftsinitialisierers, wobei die Variable oder der Eigenschaftsname von der JavaScript-Engine vernünftigerweise als Name verwendet werden kann. Es gibt keine andere Möglichkeit, dies zu tun, aber dies ist korrekt und wird vollständig von der Spezifikation abgedeckt.

Pro Spezifikation hat diese Funktion einen wahren Namen sayHello:

var sayHello = name => {
    console.log(name + ' says hello');
};

Dies wird unter Zuweisungsoperatoren> Laufzeitsemantik: Auswertung definiert, wo die abstrakte SetFunctionNameOperation aufgerufen wird (dieser Aufruf befindet sich derzeit in Schritt 1.e.iii).

In ähnlicher Weise ruft Runtime Semantics: PropertyDefinitionEvaluation aufSetFunctionName und gibt dieser Funktion somit einen wahren Namen:

let o = {
    sayHello: name => {
        console.log(`${name} says hello`);
    }
};

Moderne Engines setzen den internen Namen der Funktion bereits für solche Anweisungen; Edge hat immer noch das Bit, das es wie nameauf der Funktionsinstanz hinter einem Laufzeitflag verfügbar macht .

Öffnen Sie beispielsweise in Chrome oder Firefox die Webkonsole und führen Sie dieses Snippet aus:

"use strict";
let foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

Auf Chrome 51 und höher und Firefox 53 und höher (und Edge 13 und höher mit einem experimentellen Flag) sehen Sie, wenn Sie dies ausführen:

foo.name ist: foo
Error
    bei foo (http://stacksnippets.net/js:14:23)
    unter http://stacksnippets.net/js:17:3

Beachten Sie das foo.name is: foound Error...at foo.

In Chrome 50 und früheren Versionen, Firefox 52 und früheren Versionen und Edge ohne das experimentelle Flag wird dies stattdessen angezeigt, da sie Function#name(noch) nicht über die Eigenschaft verfügen :

foo.name ist: 
Error
    bei foo (http://stacksnippets.net/js:14:23)
    unter http://stacksnippets.net/js:17:3

Beachten Sie, dass der Name von fehlt foo.name is:, aber es ist in dem Stack - Trace angezeigt. Es ist nur so, dass die tatsächliche Implementierung der name Eigenschaft für die Funktion eine niedrigere Priorität hatte als einige andere ES2015-Funktionen. Chrome und Firefox haben es jetzt; Edge hat es hinter einer Flagge, vermutlich wird es nicht mehr lange hinter der Flagge sein.

Natürlich kann ich diese Funktion erst verwenden, nachdem ich sie definiert habe

Richtig. Es gibt keine Funktion Deklaration Syntax für Pfeil Funktionen, nur die Funktion Ausdruck Syntax, und es gibt keine Entsprechung in einem alten Stil benannte Funktion Ausdruck (auf den Namen Pfeil var f = function foo() { };). Es gibt also kein Äquivalent zu:

console.log(function fact(n) {
    if (n < 0) {
        throw new Error("Not defined for negative numbers");
    }
    return n == 0 ? 1 : n * fact(n - 1);
}(5)); // 120

Sie müssen es in zwei Ausdrücke aufteilen (ich würde argumentieren, dass Sie das trotzdem tun sollten ) :

let fact = n => {
    if (n < 0) {
      throw new Error("Not defined for negative numbers.");
    }
    return n == 0 ? 1 : n * fact(n - 1);
};
console.log(fact(5));

Wenn Sie dies dort platzieren müssen , wo ein einzelner Ausdruck erforderlich ist, können Sie natürlich immer ... eine Pfeilfunktion verwenden:

console.log((() => {
    let fact = n => {
        if (n < 0) {
            throw new Error("Not defined for negative numbers.");
        }
        return n == 0 ? 1 : n * fact(n - 1);
    };
    return fact(5);
})()); // 120

Ich sage nicht, dass das hübsch ist, aber es funktioniert, wenn Sie unbedingt einen Wrapper mit einem einzelnen Ausdruck benötigen.

TJ Crowder
quelle
Wie verhindere ich, dass der Name gesetzt wird (wie bei älteren Motoren)?
Trusktr
1
@trusktr: Ich kann nicht verstehen, warum Sie wollen, aber wenn Sie es verhindern wollen: Tun Sie nicht die oben genannten Dinge. Wenn Sie let f = () => { /* do something */ };der Funktion beispielsweise einen Namen zuweisen, ist let g = (() => () => { /* do something */ })();dies nicht der Fall.
TJ Crowder
Das habe ich letztendlich getan. Ich möchte lieber, dass anonyme Klassen, die von meiner Klassenvererbungsbibliothek generiert werden, anonym sind, als Namen interner Variablen, über die der Benutzer meiner Klassenvererbungsbibliothek nicht nachdenken muss, und ich möchte, dass der Endbenutzer einen Namen sieht nur wenn sie einen Namen für die Klasse angegeben haben. ( github.com/trusktr/lowclass )
trusktr
4
@trusktr: Die einzige Ausnahme, die sie gemacht haben, passt möglicherweise zu Ihren Zwecken: Wenn Sie einer Eigenschaft eines vorhandenen Objekts eine Funktion zuweisen , wird der Name nicht festgelegt: Gibt o.foo = () => {};der Funktion keinen Namen. Dies war beabsichtigt , um Informationslecks zu verhindern.
TJ Crowder
86

Nein. Die Pfeilsyntax ist eine Kurzform für anonyme Funktionen. Anonyme Funktionen sind anonym.

Benannte Funktionen werden mit dem functionSchlüsselwort definiert .

Jörg W Mittag
quelle
16
Wie von @ DenysSéguret angegeben, ist dies keine Abkürzung für anonyme Funktionen . Sie erhalten eine fest gebundene Funktion. Sie können das transpilierte Ergebnis hier bit.do/es2015-arrow sehen , um besser zu verstehen, was dies impliziert ...
sminutoli
16
Das erste Wort dieser Antwort ist für die gestellte Frage richtig , da das OP die Art und Weise, wie Sie eine Pfeilfunktion benennen, ausgeschlossen hat. Aber der Rest der Antwort ist einfach falsch. Suchen Sie in der Spezifikation nach dem Verwendungszweck der abstrakten SetFunctionNameOperation . let a = () => {};definiert eine benannte Funktion (der Name ist a) sowohl gemäß der Spezifikation als auch in modernen Motoren (werfen Sie einen Fehler hinein, um zu sehen); Sie unterstützen das nameAnwesen nur noch nicht (aber es ist in der Spezifikation und sie werden es irgendwann erreichen).
TJ Crowder
4
@ DenysSéguret: Sie können sie einfach benennen, es ist in der Spezifikation. Sie tun dies so, wie es das OP in der Frage ausgeschlossen hat, wodurch die Funktion (nicht nur die Variable) einen wahren Namen erhält.
TJ Crowder
5
@TJCrowder Vielen Dank für diese Informationen und für Ihre nützliche Antwort . Ich kannte diese (sehr, sehr seltsame) Funktion nicht.
Denys Séguret
4
Diese Antwort ist nicht korrekt. Die Frage wurde von @TJCrowder unten richtig beantwortet
Drenai
44

Wenn mit "benannt" gemeint ist, dass die .nameEigenschaft Ihrer Pfeilfunktion festgelegt werden soll, haben Sie Glück.

Wenn eine Pfeilfunktion auf der rechten Seite eines Zuweisungsausdrucks definiert ist, nimmt die Engine den Namen auf der linken Seite und verwendet ihn, um die Pfeilfunktionen festzulegen .name, z

var sayHello = (name) => {
    console.log(name + ' says hello');
}

sayHello.name //=== 'sayHello'

Ihre Frage scheint jedoch eher zu lauten: "Kann ich eine Pfeilfunktion zum Heben bringen?". Die Antwort auf diese Frage ist leider ein großes altes "Nein".

Hughfdjackson
quelle
1
Zumindest in der aktuellen Version von Chrome ist sayHello.name immer noch ""; Beachten Sie, dass sayHello wie alle Funktionen ein Objekt ist, sodass Sie bei Bedarf beliebige Eigenschaften definieren können. Sie können nicht in "name" schreiben, da es schreibgeschützt ist, aber Sie könnten sagen: Hallo.my_name = "sagen, Hallo"; Ich bin mir jedoch nicht sicher, was Sie damit erreichen, da Sie dies im Funktionskörper nicht tun / referenzieren können.
Dtipson
2
Ich habe mich geirrt: Obwohl der Name nicht direkt veränderbar ist, können Sie ihn folgendermaßen konfigurieren: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson
1
"Wenn auf der rechten Seite eine Pfeilfunktion definiert ist [..]" Was ist die Quelle dafür? Ich kann es nicht in der Spezifikation finden. Benötigen Sie nur eine Referenz, um zu zitieren.
Gajus
@Dtipson: Das liegt nur daran, dass moderne Engines die Einstellung der nameEigenschaft noch nicht überall dort implementiert haben, wo es die ES2015-Spezifikation erfordert. sie werden es schaffen. Es ist eines der letzten Dinge auf der Konformitätsliste für Chrome, und selbst Chrome 50 hat es nicht (Chrome 51 wird es endlich tun). Details . Das OP schloss dies jedoch ausdrücklich aus (bizarr).
TJ Crowder
Kann ich diesen Namen in toString bekommen? Ich habe versucht, es zu überschreiben, aber dann weiß ich nicht, wie ich auf den Funktionskörper zugreifen soll.
Qwerty
0

Es scheint, dass dies mit ES7 möglich sein wird: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

Das gegebene Beispiel ist:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

Der Hauptteil der ES6-Pfeilfunktionen hat dieselbe lexikalische Form wie der sie umgebende Code, wodurch wir aufgrund der Art und Weise, in der die Initialisierer der ES7-Eigenschaften festgelegt sind, das gewünschte Ergebnis erzielen.

Beachten Sie, dass ich die experimentellste ES7-Syntax der Stufe 0 aktivieren musste, damit dies mit babel funktioniert. In meiner Datei webpack.config.js habe ich den Babel Loader folgendermaßen aktualisiert:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},
Hamish Currie
quelle
6
Das ist nicht gerade eine benannte Pfeilfunktion. Dies ist eine Verknüpfung zum Erstellen von Instanzmethoden im Konstruktor, und ich frage mich, wie relevant sie hier ist.
Bergi
Hallo @Bergi, ich bin nicht sicher, ob dies die Absicht des ursprünglichen Autors war, aber ich habe versucht, eine benannte Funktion mit demselben Gültigkeitsbereich wie das Objekt zu erstellen. Wenn wir diese Technik nicht verwenden und den entsprechenden Bereich haben möchten, müssen wir sie im Klassenkonstruktor einbinden. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie
8
Äh, Sie können genauso einfach verwenden, constructor() { this.handleOptionsButtonClick = (e) => {…}; }was dem Code in Ihrer Antwort völlig entspricht, aber bereits in ES6 funktioniert. Keine Notwendigkeit zu verwenden .bind.
Bergi
1
Übrigens, ich denke, Sie verwechseln "benannte Funktion" mit "( this
Instanz-
1
@tdecs: Ja, das kannst du; Jörg irrt sich. let a = () => {};definiert eine benannte Pfeilfunktion namens a. Details .
TJ Crowder
0

Eigentlich sieht es so aus, als ob eine Möglichkeit, Pfeilfunktionen zu benennen (zumindest ab Chrom 77 ...), darin besteht, Folgendes zu tun:

"use strict";
let fn_names = {};
fn_names.foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

Geben Sie hier die Bildbeschreibung ein

Devin G Rhode
quelle
könnte theoretisch ein Babel-Makro erstellen fn('yourName', ()=>{}), das eine 100% korrekte Pfeilfunktionssemantik beibehalten könnte, oder besser noch ein Babel-Plugin für die "benannte Pfeilfunktionssyntax" : fn yourName() => {}. Beides würde bis zum obigen Code kompiliert. Ich denke, benannte Pfeile wären so BADA55
Devin G Rhode
-1

Um eine benannte Pfeilfunktion zu schreiben, können Sie das folgende Beispiel verwenden, in dem ich eine Klasse mit dem Namen LoginClass habe. Innerhalb dieser Klasse habe ich einen Pfeil mit dem Namen function namens successAuth loginClass {geschrieben.

    constructor() {

    }

    successAuth = (dataArgs)=> { //named arow function

    }

}
BERGUIGA Mohamed Amine
quelle
1
Es ist immer besser, dem Code, den Sie als Antwort veröffentlichen, eine Erklärung hinzuzufügen, damit die Besucher verstehen, warum dies eine gute Antwort ist.
Abarisone
Dies tut, was der OP gesagt hat, dass er nicht tun möchte, und stützt sich auf diesen Vorschlag, der sich erst in Phase 2 befindet und wahrscheinlich nicht einmal ES2017 machen wird (aber ich denke, es wird wahrscheinlich ES2018 machen, und Transpiler haben ihn unterstützt für Monate). Es dupliziert auch effektiv mehrere vorherige Antworten, was nicht nützlich ist.
TJ Crowder
-2

DAS IST ES6

Ja, ich denke, was Sie suchen, ist ungefähr so:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"
Venkatesh Namburi
quelle
Ich habe dieses Beispiel ausprobiert, es funktioniert gut. Gibt es einen guten Grund für negative Stimmen? Verursacht diese Verwendung unerwünschte Nebenwirkungen oder fehlt mir ein wichtiger Punkt? Ihre Hilfe bei der Klärung meiner Zweifel wird geschätzt.
FullMoon
@ GaneshAdapa: Ich gehe davon aus, dass die Abstimmungen darauf zurückzuführen sind, dass dies darauf hindeutet, genau das zu tun, was der OP gesagt hat, dass er / sie nicht wollte, ohne weitere Informationen zu geben.
TJ Crowder
-2

Sie können den Funktionsteil und den Pfeilteil überspringen, um Funktionen zu erstellen. Beispiel:

 class YourClassNameHere{

   constructor(age) {
     this.age = age;
   }

   foo() {
     return "This is a function with name Foo";
   }

   bar() {
     return "This is a function with name bar";
   }

 }

let myVar = new YourClassNameHere(50);
myVar.foo();
scre_www
quelle