Comment écrire une fonction de flèche nommée dans ES2015?

204

J'ai une fonction que j'essaye de convertir à la nouvelle syntaxe de flèche dans ES6 . C'est une fonction nommée:

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

Existe-t-il un moyen de lui donner un nom sans instruction var:

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

Évidemment, je ne peux utiliser cette fonction qu'après l'avoir définie. Quelque chose comme suivant:

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

Existe-t-il une nouvelle façon de procéder dans ES6 ?

jhamm
la source
3
La syntaxe du point de flèche n'est-elle pas pour ne pas donner un nom à la fonction?
AstroCB
64
Pas nécessairement, les fonctions fléchées conservent la portée lexicale, donc avoir un raccourci pour produire des fonctions nommées (utiles pour une trace de pile) avec la portée lexicale serait assez utile
Matt Styles
6
Quel est le problème avec le deuxième extrait?
Bergi
Vous pouvez très certainement référencer un nom attribué dans le corps de la fonction: var sayHello = (name) => {console.log (sayHello); }; Et dans le cas des fonctions récursives, vous ferez souvent exactement cela. Pas un exemple super utile, mais voici une fonction qui se retourne si elle n'obtient pas son argument: var sayHello = (nom) => nom? Console.log (nom + 'dit bonjour'): sayHello; sayHello () ('franc'); // -> "
Frank
4
Le titre de la question est extrêmement trompeur par rapport à son contenu, car vous avez exclu la façon dont vous nommez les fonctions fléchées (qui est le deuxième extrait).
TJ Crowder

Réponses:

211

Comment écrire une fonction de flèche nommée dans ES2015?

Vous le faites comme vous l'avez exclu dans votre question: vous le placez à droite d'une affectation ou d'un initialiseur de propriété où le nom de variable ou de propriété peut raisonnablement être utilisé comme nom par le moteur JavaScript. Il n'y a pas d' autre moyen de le faire, mais cela est correct et entièrement couvert par la spécification.

Par spécification, cette fonction a un vrai nom sayHello:

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

Ceci est défini dans Assignment Operators> Runtime Semantics: Evaluation où il appelle l' SetFunctionNameopération abstraite (cet appel est actuellement à l'étape 1.e.iii).

De même, Runtime Semantics: PropertyDefinitionEvaluation appelle SetFunctionNameet donne donc à cette fonction un vrai nom:

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

Les moteurs modernes définissent déjà le nom interne de la fonction pour des instructions comme celle-ci; Edge a toujours le bit le rendant disponible comme namesur l'instance de fonction derrière un indicateur d'exécution.

Par exemple, dans Chrome ou Firefox, ouvrez la console Web, puis exécutez cet extrait:

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

Sur Chrome 51 et supérieur et Firefox 53 et supérieur (et Edge 13 et supérieur avec un indicateur expérimental), lorsque vous exécutez cela, vous verrez:

foo.name est: foo
Erreur
    sur foo (http://stacksnippets.net/js:14:23)
    à http://stacksnippets.net/js:17:3

Notez le foo.name is: fooet Error...at foo.

Sur Chrome 50 et versions antérieures, Firefox 52 et versions antérieures, et Edge sans l'indicateur expérimental, vous verrez cela à la place, car ils n'ont pas Function#name(encore) la propriété:

foo.name est: 
Erreur
    sur foo (http://stacksnippets.net/js:14:23)
    à http://stacksnippets.net/js:17:3

Notez que le nom est absent de foo.name is:, mais il est affiché dans la trace de la pile. C'est juste que l'implémentation de la name propriété sur la fonction était une priorité inférieure à certaines autres fonctionnalités ES2015; Chrome et Firefox l'ont maintenant; Edge l'a derrière un drapeau, sans doute ne sera-t-il pas longtemps derrière le drapeau.

Évidemment, je ne peux utiliser cette fonction qu'après l'avoir définie

Correct. Il n'y a pas de syntaxe de déclaration de fonction pour les fonctions fléchées, uniquement la syntaxe d' expression de fonction , et il n'y a pas de flèche équivalente au nom dans une expression de fonction nommée à l'ancienne ( var f = function foo() { };). Il n'y a donc pas d'équivalent à:

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

Vous devez le diviser en deux expressions (je dirais que vous devriez le faire de toute façon ) :

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

Bien sûr, si vous devez mettre ceci là où une seule expression est requise, vous pouvez toujours ... utiliser une fonction flèche:

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

Je ne dis pas que c'est joli, mais cela fonctionne si vous avez absolument besoin d'un wrapper d'expression unique.

TJ Crowder
la source
Comment empêcher la définition du nom (comme dans les moteurs plus anciens)?
trusktr
1
@trusktr: Je ne vois pas pourquoi vous voudriez, mais si vous voulez l'empêcher: ne faites pas les choses ci-dessus. Par exemple, alors qu'il let f = () => { /* do something */ };attribue un nom à la fonction, ce let g = (() => () => { /* do something */ })();n'est pas le cas.
TJ Crowder
C'est ce que j'ai fini par faire. Je préférerais que les classes anonymes générées par ma bibliothèque d'héritage de classe soient anonymes plutôt que d'avoir des noms de variables internes auxquelles l'utilisateur de ma bibliothèque d'héritage de classe n'a pas besoin de penser, et j'aimerais que l'utilisateur final voie un nom seulement s'ils ont fourni un nom pour la classe. ( github.com/trusktr/lowclass )
trusktr
4
@trusktr: La seule exception qu'ils ont faite peut convenir à vos besoins, alors: Si vous affectez une fonction à une propriété sur un objet existant , le nom n'est pas défini: o.foo = () => {};ne donne pas de nom à la fonction. Ceci était intentionnel pour empêcher la fuite d'informations.
TJ Crowder
On dirait qu'avec react-native sur certains appareils ne renvoie toujours pas le nom, ou
react
86

Non. La syntaxe des flèches est un raccourci pour les fonctions anonymes. Les fonctions anonymes sont, eh bien, anonymes.

Les fonctions nommées sont définies avec le functionmot - clé.

Jörg W Mittag
la source
16
Comme l'a déclaré @ DenysSéguret, ce n'est pas un raccourci pour les fonctions anonymes . Vous obtiendrez une fonction liée dur . Vous pouvez voir le résultat transpilé ici bit.do/es2015-arrow pour mieux comprendre ce que cela implique ...
sminutoli
16
Le premier mot de cette réponse est correct pour la question posée car l'OP a exclu la façon dont vous nommez une fonction de flèche. Mais le reste de la réponse est tout simplement incorrect. Cherchez dans la spécification où l' opération abstraiteSetFunctionName est utilisée. let a = () => {};définit une fonction nommée (le nom est a) à la fois selon les spécifications et dans les moteurs modernes (lancez une erreur dedans pour voir); ils ne prennent pas encore en charge la namepropriété (mais c'est dans les spécifications et ils y arriveront finalement).
TJ Crowder
4
@ DenysSéguret: Vous pouvez simplement les nommer, c'est dans les spécifications. Vous le faites comme l'OP l'a exclu dans la question, ce qui donne à la fonction (pas seulement la variable) un vrai nom.
TJ Crowder du
5
@TJCrowder Merci pour ces informations et pour votre réponse utile . Je ne connaissais pas cette fonctionnalité (très très bizarre).
Denys Séguret
4
Cette réponse n'est pas correcte. La question a été répondu correctement @TJCrowder ci - dessous
Drenai
44

Si par «nommé», vous voulez dire que vous voulez .name propriété de votre fonction flèche soit définie, vous avez de la chance.

Si une fonction de flèche est définie à droite d'une expression d'affectation, le moteur prendra le nom à gauche et l'utilisera pour définir les fonctions de flèche .name, par exemple

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

sayHello.name //=== 'sayHello'

Cela dit, votre question semble être plus «puis-je obtenir une fonction de flèche à hisser?». La réponse à cette question est un grand «non», je le crains.

Hughfdjackson
la source
1
Dans la version actuelle de Chrome, au moins, sayHello.name est toujours ""; Notez que comme toutes les fonctions, sayHello est un objet, vous pouvez donc lui définir des propriétés arbitraires si vous le souhaitez. Vous ne pouvez pas écrire sur "nom" car il est en lecture seule, mais vous pouvez dire bonjour.mon_nom = "dire bonjour"; Je ne sais pas ce que cela vous apporte, car vous ne pouvez pas faire / référencer cela dans le corps de la fonction.
Dtipson
2
J'avais tort: ​​bien que le nom ne soit pas directement modifiable, vous pouvez le configurer comme ceci: Object.defineProperty (sayHello, 'name', {valeur: 'sayHello'})
Dtipson
1
"Si une fonction fléchée est définie sur le côté droit [..]" quelle est la source de cela? Je ne le trouve pas dans la spécification. Juste besoin d'une référence pour citer.
Gajus
@Dtipson: C'est simplement parce que les moteurs modernes n'ont pas encore implémenté la définition de la namepropriété partout où la spécification ES2015 les oblige; ils y arriveront. C'est l'une des dernières choses sur la liste de conformité pour Chrome et même Chrome 50 ne l'a pas (Chrome 51 le fera enfin). Détails . Mais l'OP a spécifiquement exclu de faire cela (bizarrement).
TJ Crowder
Puis-je obtenir ce nom dans toString? J'ai essayé de le remplacer, mais je ne sais pas comment accéder au corps de la fonction.
Qwerty
0

Il semble que cela sera possible avec ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

L'exemple donné est:

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

Le corps des fonctions fléchées ES6 partage le même lexical que le code qui les entoure, ce qui nous donne le résultat souhaité en raison de la portée des initialiseurs de propriété ES7.

Notez que pour que cela fonctionne avec babel, j'avais besoin d'activer la syntaxe ES7 stade 0 la plus expérimentale. Dans mon fichier webpack.config.js, j'ai mis à jour le chargeur babel comme suit:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},
Hamish Currie
la source
6
Ce n'est pas exactement une fonction de flèche nommée. Il s'agit d'un raccourci pour créer des méthodes d'instance dans le constructeur, et je me demande en quoi il est pertinent ici?
Bergi
Salut @Bergi, je ne sais pas si c'était l'intention de l'auteur original, mais j'essayais de créer une fonction nommée avec la même portée que l'objet. Si nous n'utilisons pas cette technique et que nous voulons avoir l'étendue appropriée, nous devons l'intégrer dans le constructeur de classe. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie
8
Euh, vous pouvez tout aussi facilement utiliser constructor() { this.handleOptionsButtonClick = (e) => {…}; }ce qui est totalement équivalent au code de votre réponse mais qui fonctionne déjà dans ES6. Pas besoin d'utiliser .bind.
Bergi
1
Btw, je pense que vous confondez "fonction nommée" avec "méthode (instance)" et "portée" avec " thiscontexte"
Bergi
1
@tdecs: Oui, vous le pouvez; Jörg se trompe. let a = () => {};définit une fonction de flèche nommée appelée a. Détails .
TJ Crowder
0

En fait, cela ressemble à une façon de nommer les fonctions fléchées (au moins à partir de Chrome 77 ...) est de faire ceci:

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

entrez la description de l'image ici

Devin G Rhode
la source
pourrait théoriquement créer une macro babel fn('yourName', ()=>{}), ce qui pourrait maintenir 100% de la sémantique fonction correcte flèche, ou mieux encore un plugin pour babel « nommée syntaxe de la fonction flèche »: fn yourName() => {}. L'un ou l'autre compilerait le code ci-dessus. Je pense que les flèches nommées seraient si BADA55
Devin G Rhode
-1

afin d'écrire une fonction de flèche nommée, vous pouvez suivre l'exemple ci-dessous, où j'ai une classe nommée LoginClass et à l'intérieur de cette classe, j'ai écrit une fonction de flèche nommée , nommée successAuth classe LoginClass {

    constructor() {

    }

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

    }

}
BERGUIGA Mohamed Amine
la source
1
Il est toujours préférable d'ajouter une explication au code que vous publiez comme réponse, afin qu'il aide les visiteurs à comprendre pourquoi c'est une bonne réponse.
abarisone
Cela fait ce que le PO a dit qu'il ne voulait pas faire, et s'appuie sur cette proposition qui n'est qu'à l'étape 2 et ne va probablement même pas faire ES2017 (mais je pense que c'est probablement ES2018, et les transpilateurs l'ont soutenue) Pendant des mois). Il duplique également efficacement plusieurs réponses précédentes, ce qui n'est pas utile.
TJ Crowder
-2

CECI EST ES6

Oui, je pense que ce que vous recherchez est quelque chose comme ceci:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"
Venkatesh Namburi
la source
J'ai essayé cet exemple, ça marche bien. Une bonne raison de donner des votes négatifs? Cette utilisation crée-t-elle des effets secondaires indésirables ou manque-t-il un point important? Votre aide pour clarifier mon doute sera appréciée.
FullMoon
@GaneshAdapa: Je m'attends à ce que les downvotes soient parce que cela suggère de faire exactement ce que le PO a dit qu'il ne voulait pas faire, sans donner plus d'informations.
TJ Crowder
-2

Vous pouvez ignorer la partie fonction et la partie flèche pour créer des fonctions. Exemple:

 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
la source