ES6 a immédiatement appelé la fonction de flèche

149

Pourquoi cela fonctionne-t-il dans une Node.jsconsole (testé en 4.1.1 et 5.3.0) mais ne fonctionne pas dans le navigateur (testé dans Chrome)? Ce bloc de code doit créer et appeler une fonction anonyme qui enregistre Ok.

() => {
  console.log('Ok');
}()

De plus, bien que ce qui précède fonctionne dans Node, cela ne fonctionne pas:

n => {
  console.log('Ok');
}()

Ni ceci:

(n) => {
  console.log('Ok');
}()

Ce qui est étrange, c'est que lorsque le paramètre est ajouté, il lance en fait un SyntaxErrorà la partie qui appelle immédiatement.

XCS
la source
8
Bonne question. Les deux versions paramétrées fonctionnent avec Babel
CodingIntrigue
2
Par intérêt, ça (n => { console.log("Ok"); })();marche?
CodingIntrigue
Oui (n => { console.log("Ok"); })()fonctionne même dans la console de développement Chrome
XCS
et donc, 3 ans plus tard, la réponse est? sûrement l'une des 3 réponses ci-dessous devrait être acceptée?!
joedotnot
@joedotnot Je n'ai pas obtenu de réponse claire, c'était surtout une implémentation étrange dans Node.js. Il semble que la dernière version de Node.jsla première version ne fonctionne plus.
XCS

Réponses:

194

Vous devez en faire une expression de fonction au lieu d' une définition de fonction qui n'a pas besoin d'un nom et en fait un JavaScript valide.

(() => {
  console.log('Ok');
})()

Est l'équivalent de IIFE

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

Et la raison possible pour laquelle cela fonctionne dans Node.js mais pas dans chrome est parce que son analyseur l'interprète comme une fonction auto-exécutable, comme ceci

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

fonctionne très bien dans Node.jsCeci est une expression de fonction et Chrome et Firefox et la plupart des navigateurs l'interprètent de cette façon. Vous devez l'invoquer manuellement.

La façon la plus largement acceptée de dire à l'analyseur d'attendre une expression de fonction est simplement de l'encapsuler dans des parens, car en JavaScript, les parens ne peuvent pas contenir d'instructions. À ce stade, lorsque l'analyseur rencontre le mot-clé de fonction, il sait l'analyser comme une expression de fonction et non comme une déclaration de fonction.

Concernant la version paramétrée , cela fonctionnera.

((n) => {
  console.log('Ok');
})()
néant
la source
4
Le premier exemple fonctionne Node.jset enregistre réellement la valeur. Ma question est pourquoi ça marche? Et pourquoi ce n'est pas le cas lorsque j'ajoute le paramètre?
XCS
1
Je connais assez bien les IIFEs et je sais comment corriger mon code. J'étais juste curieux de savoir pourquoi, par exemple, mon IIFEne fonctionne pas lorsque le nparamètre est ajouté, même si cela fonctionnait sans le paramètre.
XCS
3
Je n'ai pas voté contre, mais la question est de savoir pourquoi la version paramétrée ne fonctionne pas dans Node alors que la même définition exacte sans paramètre le fait - elle ne pose pas la différence entre les implémentations Node / Chrome de fonctions anonymes
CodingIntrigue
1
C'est bon à savoir mais cela ne répond pas à la question, comme mentionné précédemment, - pourquoi la version paramétrée ne fonctionne-t-elle pas dans Node alors que la même définition exacte sans paramètre le fait
jkris
Mais quel est l'équivalent des function(){}()fonctions fléchées? Si je veux que les expressions de fonction exposées de l'objet fonction se protègent contre cela.
dabadaba
18

Aucun de ceux-ci ne devrait fonctionner sans parenthèses.

Pourquoi?

Parce que selon les spécifications:

  1. ArrowFunction est répertorié sous AssignmentExpression
  2. La LHS d'une CallExpression doit être une MemberExpression , SuperCall ou CallExpression

Ainsi, une ArrowFunction ne peut pas être sur la LHS d'une CallExpression .


Ce que cela signifie effectivement dans la façon dont =>doit être interprété, c'est que cela fonctionne sur le même type de niveau que les opérateurs d'affectation =, +=etc.

Sens

  • x => {foo}() ne devient pas(x => {foo})()
  • L'interprète essaie de l'interpréter comme x => ({foo}())
  • C'est donc toujours une SyntaxError
  • Ainsi l'interpréteur décide que le (doit avoir été faux et lance une SyntaxError

Il y avait un bug sur Babel à ce sujet ici aussi.

Paul S.
la source
Ce sont des points valables, mais si j'essaie de remplacer la première version fonctionnelle par: () => ({console.log('Ok')}())cela ne fonctionne plus. Donc, il ne l'interprète pas vraiment de cette façon.
XCS
@Cristy Ce n'est pas une production de fonction de flèche valide. Il pense que vous essayez de créer un objet avec un littéral Object (entre parenthèses) et console.log(...)n'est pas un nom de clé valide.
thefourtheye
@Cristy: Oui, je pense que la partie interprétation de ce qui précède (le bit "Signification") n'est peut-être pas tout à fait correcte, mais les parties de spécification le sont pour autant que je sache. Cela correspond également à l'erreur que j'obtiens du V8: SyntaxError: Unexpected token ((pointant vers (le ()à la fin, pas vers l' (entrée console.log(...)).
TJ Crowder le
@TJCrowder vous avez raison, je vais biffer cela car cela change le message d'erreur et ce que j'essaie de dire n'est pas transmis (c'est-à-dire (que l'interprète a abandonné après des tentatives épuisantes pour trouver une interprétation valide et "ça doit être faux alors"), ce qui peut être faux de toute façon parce que je ne sais pas comment l'interprète est vraiment écrit
Paul S.
Je me demande si ce n'est pas un jeton valide à cette position, n'essaierait-il pas d'insérer un point-virgule?
thefourtheye
2

La raison pour laquelle vous rencontrez des problèmes comme celui-ci est que la console elle-même essaie d'émuler la portée globale du contexte que vous ciblez actuellement. Il essaie également de capturer les valeurs de retour des instructions et des expressions que vous écrivez dans la console, afin que les résultats apparaissent. Prenons par exemple:

> 3 + 2
< 5

Ici, il s'exécute comme s'il s'agissait d'une expression, mais vous l'avez écrit comme s'il s'agissait d'une instruction. Dans les scripts normaux, la valeur serait ignorée, mais ici, le code doit être mutilé en interne (comme envelopper l'instruction entière avec un contexte de fonction et une returninstruction), ce qui provoque toutes sortes d'effets étranges, y compris les problèmes que vous rencontrez.

C'est également l'une des raisons pour lesquelles certains codes ES6 nus dans les scripts fonctionnent correctement, mais pas dans la console Chrome Dev Tools.

Essayez de l'exécuter dans la console Node et Chrome:

{ let a = 3 }

Dans Node ou une <script>balise ça marche très bien, mais dans la console, ça donne Uncaught SyntaxError: Unexpected identifier. Il vous donne également un lien vers la source sous la forme VMxxx:1duquel vous pouvez cliquer pour inspecter la source évaluée, qui apparaît comme:

({ let a = 3 })

Alors pourquoi a-t-il fait ça?

La réponse est qu'il doit convertir votre code en une expression afin que le résultat puisse être renvoyé à l'appelant et affiché dans la console. Vous pouvez le faire en entourant l'instruction entre parenthèses, ce qui en fait une expression, mais cela rend également le bloc ci-dessus syntaxiquement incorrect (une expression ne peut pas avoir de déclaration de bloc).

La console essaie de résoudre ces cas extrêmes en étant intelligente sur le code, mais cela dépasse le cadre de cette réponse, je pense. Vous pouvez déposer un bogue pour voir si c'est quelque chose qu'ils envisageraient de corriger.

Voici un bon exemple de quelque chose de très similaire:

https://stackoverflow.com/a/28431346/46588

Le moyen le plus sûr de faire fonctionner votre code est de vous assurer qu'il peut être exécuté en tant qu'expression et d'inspecter le SyntaxErrorlien source pour voir quel est le code d'exécution réel et de procéder à l' ingénierie inverse d'une solution à partir de cela. Habituellement, cela signifie une paire de parenthèses stratégiquement placées.

En bref: la console essaie d'émuler le contexte d'exécution global aussi précisément que possible, mais en raison des limitations d'interaction avec le moteur v8 et la sémantique JavaScript, cela est parfois difficile voire impossible à résoudre.

Klemen Slavič
la source
1
C'est le point entier, je me soucie du paramètre, mais cela ne fonctionne pas avec le jeu de paramètres.
XCS
OK, je vois votre point. La différence réside dans la manière dont la console Chrome Dev Tools exécute réellement votre code. Je vais modifier la réponse pour refléter cela.
Klemen Slavič
0

J'ai posé une question comme celle-ci:

@getify J'ai cette question: pour produire un modèle #IIFE, nous utilisons des parans autour d'une déclaration de fonction pour la transformer en une expression de fonction, puis l'appelons. Maintenant dans les IIFE à fonction fléchée, pourquoi avons-nous besoin de parans?! La fonction flèche n'est-elle pas déjà une expression par défaut?!

et voici la réponse de Kyle Simpson:

une fonction de flèche est une expression, mais nous avons besoin de parenthèses entourant b / c de "priorité d'opérateur" (sorta), de sorte que les parenthèses finales pour invoquer la flèche-IIFE s'appliquent à la fonction entière et pas seulement au dernier jeton de son corps .

x => console.log(x)(4)

contre

(x => console.log(x))(4)

- getify (@getify) 12 juin 2020

Ershad Qaderi
la source
Ma question était de savoir pourquoi cela fonctionnait sur certains compilateurs et pas sur d'autres.
XCS
C'est parce que différents compilateurs se comportent différemment dans certains détails, tout comme différents navigateurs, qui ont bien sûr différents compilateurs
Ershad Qaderi
Vous avez raison, ils se comportent différemment, mais les spécifications JavaScript sont les mêmes pour tous. J'étais curieux de savoir lequel avait raison, que dit la spécification JS à propos de ce cas et surtout comment cela pourrait-il fonctionner sans argument mais pas avec argument. Je cherchais une réponse plus technique.
XCS
Votre exemple est assez évident, dans le premier cas, il devrait en effet appeler console.log(x)(4).
XCS
Je suppose juste ici mais je pense qu'il est très raisonnable de l'expliquer comme ceci: dans les expressions de fonction de flèche, lorsque nous n'utilisons pas de paramètre, nous devons utiliser les parenthèses et cela indique très clairement pour le moteur qu'il s'agit d'une flèche expression de fonction, mais lorsque nous avons un seul paramètre, les parenthèses sont arbitraires, ce qui pourrait ne pas être très clair qu'il s'agit d'une fonction et confond le moteur that, pour résoudre la confusion, nous devons mettre une paire de parenthèses autour de l'expression de la fonction entière
Ershad Qaderi