Come faccio a scrivere una funzione freccia denominata in ES2015?


204

Ho una funzione che sto cercando di convertire nella nuova sintassi della freccia in ES6 . È una funzione denominata:

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

C'è un modo per dargli un nome senza un'istruzione var:

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

Ovviamente, posso usare questa funzione solo dopo averla definita. Qualcosa di simile al seguente:

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

Esiste un nuovo modo per farlo in ES6 ?


3
La sintassi del punto di freccia non dà un nome alla funzione?
AstroCB,

64
Non necessariamente, le funzioni freccia mantengono l'ambito lessicale, quindi avere una scorciatoia per produrre funzioni denominate (utile per una traccia dello stack) con ambito lessicale sarebbe piuttosto utile
Matt Styles

6
Cosa c'è di sbagliato nel secondo frammento?
Bergi,

Sicuramente puoi fare riferimento a un nome assegnato all'interno del corpo della funzione: var sayHello = (name) => {console.log (sayHello); }; E nel caso di funzioni ricorsive, spesso lo farai esattamente. Non è un esempio super utile, ma ecco una funzione che restituisce se stessa se non ottiene il suo argomento: var sayHello = (nome) => nome? Console.log (nome + 'dice ciao'): sayHello; sayHello () ( 'Frank'); // -> "Frank saluta"
Dtipson

4
Il titolo della domanda è enormemente fuorviante rispetto al suo contenuto, perché hai escluso il modo in cui assegni un nome alle funzioni della freccia (che è il secondo frammento).
TJ Crowder,

Risposte:


211

Come faccio a scrivere una funzione freccia denominata in ES2015?

Lo fai come hai escluso nella tua domanda: lo metti sul lato destro di un assegnazione o inizializzatore di proprietà in cui la variabile o il nome della proprietà può ragionevolmente essere usato come nome dal motore JavaScript. Non c'è altro modo per farlo, ma farlo è corretto e completamente coperto dalle specifiche.

Per spec, questa funzione ha un vero nome, sayHello:

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

Questo è definito in Operatori di assegnazione> Semantica di runtime: valutazione in cui chiama l' SetFunctionNameoperazione astratta (quella chiamata è attualmente al passaggio 1.e.iii).

Analogamente, Runtime Semantics: PropertyDefinitionEvaluation chiama SetFunctionNamee quindi dà a questa funzione un nome vero:

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

I motori moderni impostano il nome interno della funzione per affermazioni del genere già; Edge ha ancora il bit che lo rende disponibile come namenell'istanza della funzione dietro un flag di runtime.

Ad esempio, in Chrome o Firefox, apri la console Web ed esegui questo frammento:

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

Su Chrome 51 e versioni successive e Firefox 53 e versioni successive (e Edge 13 e versioni successive con un flag sperimentale), quando lo esegui, vedrai:

foo.name è: foo
Errore
    su foo (http://stacksnippets.net/js:14:23)
    su http://stacksnippets.net/js:17:3

Nota il foo.name is: fooe Error...at foo.

Su Chrome 50 e precedenti, Firefox 52 e precedenti e Edge senza il flag sperimentale, lo vedrai invece perché non hanno Function#name(ancora) la proprietà:

foo.name è: 
Errore
    su foo (http://stacksnippets.net/js:14:23)
    su http://stacksnippets.net/js:17:3

Si noti che manca il nome foo.name is:, ma viene mostrato nella traccia dello stack. È solo che implementare effettivamente la name proprietà sulla funzione ha avuto una priorità inferiore rispetto ad alcune altre funzionalità ES2015; Chrome e Firefox lo hanno ora; Edge ce l'ha dietro una bandiera, presumibilmente non sarà molto più a lungo dietro la bandiera.

Ovviamente, posso usare questa funzione solo dopo averla definita

Corretta. Non esiste una sintassi di dichiarazione di funzione per le funzioni freccia, solo la sintassi dell'espressione di funzione e non esiste una freccia equivalente al nome in un'espressione di funzione denominata vecchio stile ( var f = function foo() { };). Quindi non c'è equivalente a:

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

Devi dividerlo in due espressioni (direi che dovresti farlo comunque ) :

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));

Naturalmente, se si dispone di mettere questo in cui è richiesta una sola espressione, è sempre possibile ... utilizzare una funzione freccia:

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

Non sto dicendo che sia carino, ma funziona se hai assolutamente, positivamente bisogno di un unico wrapper di espressioni.


Come posso impedire l'impostazione del nome (come nei motori più vecchi)?
trusktr,

1
@trusktr: non riesco a capire perché vorresti farlo, ma se vuoi prevenirlo: non fare le cose sopra. Ad esempio, mentre let f = () => { /* do something */ };assegna un nome alla funzione, let g = (() => () => { /* do something */ })();non lo fa.
TJ Crowder,

Questo è quello che ho finito per fare. Preferirei che le classi anonime generate dalla mia biblioteca di eredità di classe fossero anonime piuttosto che avere nomi di variabili interne a cui l'utente della mia biblioteca di eredità di classe non ha bisogno di pensare, e vorrei che l'utente finale vedesse un nome solo se hanno fornito un nome per la classe. ( github.com/trusktr/lowclass )
trusktr

4
@trusktr: L'unica eccezione che hanno fatto potrebbe soddisfare i tuoi scopi, quindi: Se assegni una funzione a una proprietà su un oggetto esistente , il nome non viene impostato: o.foo = () => {};non dà un nome alla funzione. Questo era intenzionale per prevenire la perdita di informazioni.
TJ Crowder,

A quanto pare, con reattivo-nativo su alcuni dispositivi non restituisce ancora il nome, oppure reagire e reagire-nativo / reagire-nativo-debugger non prende il nome
Zorgatone

86

No. La sintassi della freccia è una scorciatoia per funzioni anonime. Le funzioni anonime sono, anzi, anonime.

Le funzioni nominate sono definite con la functionparola chiave.


16
Come affermato da @ DenysSéguret, non è una scorciatoia per funzioni anonime . Avrai una funzione fortemente legata . Puoi vedere il risultato traspilato qui bit.do/es2015-arrow per capire meglio cosa questo implica ...
sminutoli,

16
La prima parola di questa risposta è corretta per la domanda posta perché l'OP ha escluso il modo in cui si nomina una funzione freccia. Ma il resto della risposta è semplicemente errato. Cerca nelle specifiche dove viene utilizzata l' operazione astrattaSetFunctionName . let a = () => {};definisce una funzione con nome (il nome è a) sia in base alle specifiche che nei motori moderni (gettare un errore in essa per vedere); semplicemente non supportano nameancora la proprietà (ma è nelle specifiche e alla fine ci arriveranno).
TJ Crowder,

4
@ DenysSéguret: puoi semplicemente nominarli, è nelle specifiche. Lo fai nel modo in cui l'OP ha escluso la domanda, il che dà alla funzione (non solo alla variabile) un vero nome.
TJ Crowder,

5
@TJCrowder Grazie per quel po 'di informazioni e per la tua risposta utile . Non conoscevo questa caratteristica (molto molto strana).
Denys Séguret,

4
Questa risposta non è corretta @TJCrowder ha risposto correttamente alla domanda di seguito
Drenai il

44

Se per "nome" intendi che vuoi impostare la .nameproprietà della tua funzione freccia, sei fortunato.

Se una funzione freccia è definita sul lato destro di un'espressione di assegnazione, il motore prenderà il nome sul lato sinistro e lo utilizzerà per impostare la funzione freccia .name, ad es.

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

sayHello.name //=== 'sayHello'

Detto questo, la tua domanda sembra essere più "posso ottenere una funzione freccia per sollevare?". La risposta a questo è un grande "no", temo.


1
Nell'attuale versione di Chrome, almeno, sayHello.name è ancora ""; Nota che come tutte le funzioni, sayHello è un oggetto, quindi puoi definire proprietà arbitrarie se lo desideri. Non puoi scrivere a "name" in quanto è di sola lettura, ma potresti direHello.my_name = "sayHello"; Non sono sicuro di ciò che ti prende, dal momento che non puoi fare / fare riferimento a quello all'interno del corpo della funzione.
Dtipson,

2
Ho sbagliato: mentre il nome non è direttamente modificabile, puoi configurarlo in questo modo: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson

1
"Se una funzione freccia è definita sul lato destro [..]" qual è la fonte per questo? Non riesco a trovarlo nelle specifiche. Ho solo bisogno di un riferimento per citare.
Gajus

@Dtipson: è solo perché i motori moderni non hanno ancora implementato l'impostazione della nameproprietà ovunque le specifiche ES2015 lo richiedano; ci riusciranno. È una delle ultime cose nell'elenco di conformità per Chrome e anche Chrome 50 non ce l'ha (Chrome 51 finalmente lo farà). Dettagli . Ma l'OP ha espressamente escluso di farlo (in modo bizzarro).
TJ Crowder,

Posso ottenere questo nome in toString? Ho provato a ignorarlo, ma poi non so come accedere al corpo della funzione.
Qwerty,

0

Sembra che ciò sarà possibile con ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

L'esempio fornito è:

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

Il corpo delle funzioni freccia ES6 condivide lo stesso lessico del codice che le circonda, il che ci dà il risultato desiderato a causa del modo in cui gli inizializzatori della proprietà ES7 sono definiti.

Nota che per far funzionare tutto questo con Babel avevo bisogno di abilitare la sintassi ES7 stage 0 più sperimentale. Nel mio file webpack.config.js ho aggiornato il caricatore di babele in questo modo:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},

6
Non è esattamente una funzione freccia denominata. Questa è una scorciatoia per la creazione di metodi di istanza nel costruttore e mi chiedo come sia rilevante qui?
Bergi,

Ciao @Bergi, non sono sicuro che questa fosse l'intenzione dell'autore originale, ma stavo provando a creare una funzione denominata con lo stesso ambito dell'oggetto. Se non utilizziamo questa tecnica e vogliamo avere l'ambito appropriato, dobbiamo inserirla nel costruttore della classe. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie,

8
Uh, puoi usare altrettanto facilmente il constructor() { this.handleOptionsButtonClick = (e) => {…}; }che è totalmente equivalente al codice nella tua risposta ma funziona già in ES6. Non c'è bisogno di usare .bind.
Bergi,

1
A proposito, penso che tu stia confondendo la "funzione denominata" con "il metodo (istanza)" e "ambito" con " thiscontesto"
Bergi,

1
@tdecs: Sì, puoi; Jörg si sbaglia. let a = () => {};definisce un nome funzione freccia chiamato a. Dettagli .
TJ Crowder,

0

In realtà sembra che un modo per nominare le funzioni freccia (almeno a partire da Chrome 77 ...) sia fare questo:

"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);
}

inserisci qui la descrizione dell'immagine


potrebbe teoricamente creare una macro babele fn('yourName', ()=>{}), che potrebbe mantenere il 100% semantica corretto funzionamento freccia, o meglio ancora un plugin babel per "sintassi della funzione freccia denominata": fn yourName() => {}. Ognuno di questi sarebbe compilare fino al codice sopra. Penso che le frecce nominate sarebbero così BADA55
Devin G Rhode,

-1

per scrivere la funzione freccia denominata puoi aggiungere l'esempio seguente, dove ho una classe denominata LoginClass e all'interno di questa classe ho scritto una funzione denominata freccia , denominata successAuth classe LoginClass {

    constructor() {

    }

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

    }

}

1
È sempre meglio aggiungere una spiegazione al codice che pubblichi come risposta, in modo che possa aiutare i visitatori a capire perché questa è una buona risposta.
Abarisone,

Questo fa ciò che l'OP ha affermato di non voler fare e si affida a questa proposta che è solo nella fase 2 e probabilmente non produrrà nemmeno ES2017 (ma penso che probabilmente lo farà ES2018, e transpilers lo hanno supportato per mesi). Duplica efficacemente anche diverse risposte precedenti, il che non è utile.
TJ Crowder,

-2

QUESTO È ES6

Sì, penso che ciò che cerchi sia qualcosa del genere:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"

Ho provato questo esempio, funziona benissimo. Qualche buona ragione per dare voti negativi? Questo utilizzo crea effetti collaterali indesiderati o mi manca qualche punto importante? Il tuo aiuto per chiarire il mio dubbio sarà apprezzato.
FullMoon il

@GaneshAdapa: mi aspetto che i downvotes siano dovuti al fatto che ciò suggerisce di fare esattamente ciò che l'OP ha affermato di non voler fare, senza fornire ulteriori informazioni.
TJ Crowder,

-2

È possibile saltare la parte della funzione e la parte della freccia per creare funzioni. Esempio:

 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();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.