ES6 ha immediatamente richiamato la funzione freccia


149

Perché funziona in una Node.jsconsole (testato in 4.1.1 e 5.3.0) ma non funziona nel browser (testato in Chrome)? Questo blocco di codice dovrebbe creare e richiamare una funzione anonima che registra Ok.

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

Inoltre, mentre quanto sopra funziona in Node, questo non funziona:

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

Né questo:

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

La cosa strana è che quando viene aggiunto il parametro, viene effettivamente lanciato un SyntaxErrornella parte che invoca immediatamente.


8
Buona domanda. Entrambe le versioni con parametri funzionano con Babel
CodingIntrigue il

2
Per interesse, (n => { console.log("Ok"); })();funziona?
CodingIntrigue

Sì, (n => { console.log("Ok"); })()funziona anche nella console di sviluppo di Chrome
XCS

e così, 3 anni dopo, la risposta è? sicuramente una delle 3 risposte qui sotto dovrebbe essere accettata ?!
joedotnot non

@joedotnot Non ho ricevuto una risposta chiara, principalmente è stata una strana implementazione in Node.js. Sembra che nell'ultima versione della Node.jsprima versione non funzioni più.
XCS

Risposte:


194

Devi renderlo un'espressione della funzione invece della definizione della funzione che non ha bisogno di un nome e lo rende un JavaScript valido.

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

È l'equivalente di IIFE

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

E il possibile motivo per cui funziona in Node.js ma non in Chrome è perché il suo parser lo interpreta come una funzione auto-eseguibile, in quanto

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

funziona bene in Node.jsQuesta è un'espressione di funzione e chrome e firefox e la maggior parte del browser la interpreta in questo modo. Devi invocarlo manualmente.

Il modo più ampiamente accettato di dire al parser di aspettarsi che un'espressione di funzione sia semplicemente racchiuderla in parentesi, perché in JavaScript le parentesi non possono contenere istruzioni. A questo punto, quando il parser incontra la parola chiave della funzione, sa analizzarla come espressione di funzione e non come dichiarazione di funzione.

Per quanto riguarda la versione parametrizzata , funzionerà.

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

4
Il primo esempio funziona Node.jse registra effettivamente il valore. La mia domanda è: perché funziona? E perché non lo faccio quando aggiungo il parametro?
XCS

1
Conosco abbastanza bene i messaggi di posta elettronica IIFEe so come correggere il mio codice. Ero solo curioso di sapere perché, ad esempio, my IIFEnon funziona quando nviene aggiunto il parametro, anche se ha funzionato senza il parametro.
XCS,

3
Non ho effettuato il downgrade, ma la domanda è: perché la versione con parametri non funziona in Node quando la stessa identica definizione senza un parametro funziona - non sta facendo la differenza tra le implementazioni Node / Chrome di funzioni anonime
CodingIntrigue

1
È utile, ma non risponde alla domanda, come accennato in precedenza, perché la versione con parametri non funziona in Node quando la stessa identica definizione senza un parametro funziona
jkris

Ma qual è l'equivalente delle function(){}()funzioni freccia? Se voglio l'oggetto funzione, le espressioni di funzione esposte proteggono da ciò.
Dabadaba,

18

Nessuno di questi dovrebbe funzionare senza parentesi.

Perché?

Perché secondo le specifiche:

  1. ArrowFunction è elencato in AssignmentExpression
  2. L' LHS di una CallExpression deve essere MemberExpression , SuperCall o CallExpression

Quindi un ArrowFunction non può essere sull'LHS di un CallExpression .


Ciò che ciò significa effettivamente in come =>dovrebbe essere interpretato è che funziona allo stesso livello di operatori di assegnazione =, +=ecc.

Senso

  • x => {foo}() non diventa(x => {foo})()
  • L'interprete cerca di interpretarlo come x => ({foo}())
  • Quindi è ancora un SyntaxError
  • Quindi l'interprete decide che (deve essere stato sbagliato e lancia un SyntaxError

C'era un bug su Babel su di esso qui , anche.


Questi sono alcuni punti validi, ma se provo a sostituire la prima versione funzionante con: () => ({console.log('Ok')}())non funziona più. Quindi non lo interpreta davvero in quel modo.
XCS

@Cristy Non è una produzione Arrow Function valida. Pensa che stai cercando di creare un oggetto con oggetto letterale (racchiuso tra parentesi) e console.log(...)non è un nome chiave valido.
thefourtheye

@Cristy: Sì, penso che la parte dell'interpretazione di cui sopra (il bit "Significato") potrebbe non essere del tutto corretta, ma le parti delle specifiche sono per quanto posso dire. Si adatta anche all'errore che ottengo da V8: SyntaxError: Unexpected token ((indicando in (in ()alla fine, non (in in console.log(...)).
TJ Crowder,

@TJCrowder hai ragione, lo colpirò mentre cambia il messaggio di errore e ciò che sto cercando di dire non viene trasmesso (cioè il (fatto che l'interprete ha rinunciato dopo estenuanti tentativi di trovare un'interpretazione valida e va "questo deve essere sbagliato allora"), che può essere sbagliato comunque perché non so come sia veramente scritto l'interprete
Paul S.

Mi chiedo se non è un token valido in questa posizione, non cercherebbe di inserire un punto e virgola?
thefourtheye

2

Il motivo per cui stai riscontrando problemi come questo è che la console stessa tenta di emulare l'ambito globale del contesto al quale stai attualmente mirando. Prova anche a catturare i valori di ritorno dalle istruzioni e dalle espressioni che scrivi nella console, in modo che vengano visualizzati come risultati. Prendi ad esempio:

> 3 + 2
< 5

Qui, viene eseguito come se fosse un'espressione, ma l'hai scritto come se fosse un'affermazione. Negli script normali, il valore verrebbe scartato, ma qui il codice deve essere alterato internamente (come se si racchiudesse l'intera istruzione con un contesto di funzione e returnun'istruzione), che causa tutti i tipi di strani effetti, compresi i problemi che si verificano.

Questo è anche uno dei motivi per cui alcuni codici ES6 nudi negli script funzionano bene ma non nella console di Chrome Dev Tools.

Prova a eseguirlo nella console Node e Chrome:

{ let a = 3 }

In Node o in un <script>tag funziona bene, ma nella console dà Uncaught SyntaxError: Unexpected identifier. Ti dà anche un link alla fonte sotto forma di VMxxx:1cui puoi fare clic per ispezionare la fonte valutata, che si presenta come:

({ let a = 3 })

Quindi perché lo ha fatto?

La risposta è che deve convertire il codice in un'espressione in modo che il risultato possa essere restituito al chiamante e visualizzato nella console. Puoi farlo avvolgendo l'istruzione tra parentesi, il che la rende un'espressione, ma rende anche sintatticamente errato il blocco sopra (un'espressione non può avere una dichiarazione di blocco).

La console cerca di risolvere questi casi limite essendo intelligente riguardo al codice, ma questo va oltre lo scopo di questa risposta, credo. Puoi presentare un bug per vedere se è qualcosa che potrebbero prendere in considerazione.

Ecco un buon esempio di qualcosa di molto simile:

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

Il modo più sicuro per far funzionare il codice è assicurarsi che possa essere eseguito come espressione e ispezionare il SyntaxErrorcollegamento di origine per vedere qual è il codice di esecuzione effettivo e decodificare una soluzione da quello. Di solito significa una coppia di parentesi posizionate strategicamente.

In breve: la console tenta di emulare il contesto di esecuzione globale nel modo più accurato possibile, ma a causa dei limiti di interazione con il motore v8 e la semantica JavaScript, questo è talvolta difficile o impossibile da risolvere.


1
Questo è l'intero punto, mi interessa il parametro, ma non funziona con il set di parametri.
XCS,

OK, vedo il tuo punto. La differenza sta nel modo in cui la console Chrome Dev Tools esegue effettivamente il tuo codice. Modificherò la risposta per riflettere questo.
Klemen Slavič,

0

Ho fatto una domanda come questa:

@getify Ho questa domanda: per produrre un modello #IIFE usiamo i paran attorno a una dichiarazione di funzione per trasformarla in un'espressione di funzione e quindi invocarla. Ora nella funzione freccia IIFE, perché abbiamo bisogno di paran ?! La funzione freccia non è già un'espressione per impostazione predefinita ?!

e questa è la risposta di Kyle Simpson:

una funzione freccia è un'espr, ma abbiamo bisogno delle parentesi circostanti b / c di "precedenza dell'operatore" (sorta), in modo che le parentesi finali per invocare la freccia-IIFE si applichino all'intera funzione e non solo all'ultimo token del suo corpo .

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

vs

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

- getify (@getify) 12 giugno 2020


La mia domanda era: perché ha funzionato su alcuni compilatori e non su altri.
XCS,

Questo perché diversi compilatori si comportano diversamente in alcuni dettagli, proprio come browser diversi, che ovviamente hanno compilatori diversi
Ershad Qaderi

Hai ragione, si comportano diversamente, ma le specifiche JavaScript sono le stesse per tutti. Ero curioso di sapere quale fosse giusto, cosa dicono le specifiche JS su questo caso e soprattutto come potrebbe essere che funzioni senza argomentazioni ma non con argomentazioni. Stavo cercando una risposta più tecnica.
XCS

Il tuo esempio è abbastanza ovvio, nel primo caso dovrebbe effettivamente chiamare console.log(x)(4).
XCS

Sto solo indovinando qui, ma penso che sia molto ragionevole spiegarlo in questo modo: nelle espressioni della funzione freccia quando non utilizziamo un parametro dobbiamo usare le parentesi e questo rende molto chiaro per il motore che questa è una freccia espressione della funzione, ma quando abbiamo un singolo parametro le parentesi sono arbitrarie, il che potrebbe non essere molto chiaro che questa è una funzione e confonde quel motore, per risolvere la confusione dobbiamo mettere una coppia di parentesi attorno all'intera espressione della funzione
Ershad Qaderi il
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.