Perché usare le espressioni di funzione denominate?

Abbiamo due modi diversi per fare un’espressione di funzione in JavaScript:

Espressione funzione denominata (NFE) :

var boo = function boo () { alert(1); }; 

Espressione di funzione anonima :

 var boo = function () { alert(1); }; 

Ed entrambi possono essere chiamati con boo(); . Non riesco davvero a capire perché / quando dovrei usare le funzioni anonime e quando dovrei usare Named Function Expressions. Che differenza c’è tra loro?

Nel caso dell’espressione di funzione anonima, la funzione è anonima – letteralmente, non ha nome. La variabile a cui stai assegnando ha un nome, ma la funzione no. (Aggiornamento: Questo era vero attraverso ES5. A partire da ES2015 [aka ES6], spesso una funzione creata con un’espressione anonima ottiene un vero nome, continua a leggere …)

I nomi sono utili I nomi possono essere visti in tracce di stack, pile di chiamate, elenchi di punti di interruzione, ecc. I nomi sono una buona cosa ™.

Bisogna fare attenzione alle espressioni di funzioni con nome nelle versioni precedenti di IE (IE8 e seguenti), perché IE crea erroneamente due oggetti funzione completamente separati in due tempi completamente diversi (più nel mio articolo del blog Double take ). Se è necessario supportare IE8, è probabilmente meglio attenersi alle espressioni di funzioni anonime o alle dichiarazioni di funzione, ma evitare espressioni di funzioni con nome.

A partire da ES2015, tuttavia, molte espressioni di funzioni “anonime” creano funzioni con nomi, e questo è stato anticipato da vari motori JavaScript moderni essendo abbastanza intelligenti nell’inferire nomi dal contesto. In ES2015, l’espressione della funzione anonima risulta in una funzione con il nome boo . Questo è disseminato per tutta la specifica piuttosto che essere definito in un unico posto con una serie di regole: cerca le occorrenze di “SetFunctionName”, attualmente trovato in:

  • §12.2.6.9 (semantica di inizializzazione delle proprietà)
  • §12.14.4 (semantica degli operatori di assegnazione)
  • §12.14.5.2 e §12.14.5.4 (semantica di assegnazione distruttiva)
  • §13.3.1.4 (semantica di let e const declaration)
  • … e un sacco di altri posti.

La versione breve è praticamente ogni volta che un’espressione di funzione anonima appare sul lato destro di qualcosa come un incarico o un’inizializzazione, come:

 var boo = function() { /*...*/ }; 

(o potrebbe essere let o const piuttosto che var ) , o

 var obj = { boo: function() { /*...*/ } }; 

o

 doSomething({ boo: function() { /*...*/ } }); 

(questi ultimi due sono davvero la stessa cosa) , la funzione risultante avrà un nome ( boo , negli esempi).

Esiste un’eccezione importante e intenzionale: Assegnazione a una proprietà su un object esistente:

 obj.boo = function() { /*...*/ }; // <== Does not get a name 

Ciò era dovuto a problemi di perdita di informazioni sollevati quando la nuova funzionalità stava attraversando il processo di aggiunta; dettagli nella mia risposta ad un'altra domanda qui .

Le funzioni di denominazione sono utili se devono fare riferimento a se stesse (ad esempio per le chiamate ricorsive). Infatti, se si passa un’espressione di una funzione letterale come argomento direttamente a un’altra funzione, quell’espressione di funzione non può fare direttamente riferimento a se stessa nella modalità rigorosa ES5, a meno che non sia denominata.

Ad esempio, considera questo codice:

 setTimeout(function sayMoo() { alert('MOO'); setTimeout(sayMoo, 1000); }, 1000); 

Sarebbe imansible scrivere questo codice abbastanza chiaramente se l’espressione della funzione passata a setTimeout fosse anonima; avremmo bisogno di assegnarlo a una variabile invece prima della chiamata setTimeout . In questo modo, con un’espressione di funzione con nome, è leggermente più corto e più ordinato.

È stato storicamente ansible scrivere codice come questo anche usando un’espressione di funzione anonima, sfruttando arguments.callee .

 setTimeout(function () { alert('MOO'); setTimeout(arguments.callee, 1000); }, 1000); 

… ma arguments.callee è deprecato ed è completamente vietato in modalità rigorosa ES5. Quindi MDN consiglia:

Evitare l’uso di arguments.callee() dando a espressioni di funzione un nome o utilizzare una dichiarazione di funzione in cui una funzione deve chiamarsi.

(sottolineatura mia)

Se una funzione è specificata come espressione funzione, può essere assegnato un nome.

Sarà disponibile solo all’interno della funzione (tranne IE8-).

Questo nome è inteso per una funzione di chiamata ricorsiva affidabile, anche se è scritto su un’altra variabile.

Si noti che con la dichiarazione delle funzioni questo non può essere fatto. Questo nome di funzione interno “speciale” è specificato solo nella syntax dell’espressione di funzione.

 var f = function sayHi(name) { alert( sayHi ); // Inside the function you can see the function code }; alert( sayHi ); // (Error: undefined variable 'sayHi') 

Inoltre, il nome NFE (Named Function Expression) non può essere sovrascritto:

 var test = function sayHi(name) { sayHi = "тест"; // try to redefine alert( sayHi ); // function... (redefinition is unsuccessful ) }; test(); 

L’utilizzo delle espressioni di funzione con nome è migliore, quando si vuole essere in grado di fare riferimento alla funzione in questione senza dover fare affidamento su funzionalità deprecate come arguments.callee .

Dovresti sempre usare le espressioni di funzione NAMED.

  1. Puoi usare il nome di quella funzione quando hai bisogno di ricorsione.

2. Le funzioni anonime non aiutano quando si esegue il debug in quanto non è ansible vedere il nome della funzione che causa problemi.

3.Quando non si nomina una funzione, in seguito è più difficile capire cosa sta facendo. Dargli un nome lo rende più facile da capire.

 var foo = function bar() { //some code... }; foo(); bar(); // Error! 

Qui, ad esempio, poiché la barra del nome viene utilizzata all’interno di un’espressione di funzione, non viene dichiarata nell’ambito esterno. Con le espressioni di funzione denominate, il nome dell’espressione di funzione è racchiuso all’interno del proprio ambito.