W3docs

Sintassi della funzione JavaScript con new Function

Scopri la sintassi "new Function" in JavaScript: come costruire funzioni da stringhe a runtime, regole di scope, sicurezza rispetto a eval e quando usarla.

La maggior parte delle volte si crea una funzione con una dichiarazione, un'espressione o una arrow function. Ma JavaScript offre un altro modo: il costruttore new Function, che costruisce una funzione da stringhe a runtime. Questa pagina si concentra su quella sintassi — la sua forma esatta, le regole di scope che sorprendono la maggior parte degli sviluppatori, come si confronta con eval e i casi specifici in cui è lo strumento giusto.

La sintassi new Function

La sintassi new Function consente di creare una funzione i cui parametri e corpo vengono forniti come stringhe. Poiché il corpo è solo testo fino a quando il motore non lo analizza, è possibile assemblare una funzione il cui codice non è noto al momento della scrittura del programma — soltanto nel momento in cui viene eseguito.

La forma generale è:

let func = new Function([arg1, arg2, ...argN], functionBody);

Ogni argomento è una string. I primi argomenti nominano i parametri; l'ultimo argomento è sempre il corpo della funzione.

javascript— editable

È possibile passare tutti i nomi dei parametri in una singola string separata da virgole, che è equivalente:

javascript— editable

La parola chiave new è opzionale qui — Function('a', 'b', 'return a + b') produce lo stesso risultato — ma scrivere new Function(...) è la forma convenzionale e più chiara.

Perché esiste

La cosa principale che distingue new Function da una dichiarazione normale è che il corpo proviene da una string. Quella string può essere ricevuta da qualsiasi luogo: una risposta del server, un template, una configurazione utente o testo costruito a runtime. Quindi la sintassi esiste esattamente per i casi in cui il codice che si vuole eseguire non esiste ancora nel momento in cui si scrive il programma.

Scope: il trabocchetto principale

Questo è il dettaglio che mette tutti in difficoltà. Una funzione creata con new Function non cattura lo scope in cui è stata creata. A differenza di una normale closure, il suo ambiente lessicale esterno è lo scope globale, non quello locale.

function makeAdder() {
  let outer = 100;
  // This function tries to read `outer`...
  return new Function('x', 'return x + outer');
}

const add = makeAdder();
add(5); // ReferenceError: outer is not defined

Una funzione regolare scritta allo stesso modo chiuderebbe felicemente su outer. La versione con new Function non può — vede solo i propri parametri e lo scope globale:

javascript— editable

Questo è intenzionale. Se new Function potesse accedere alle variabili locali, i minifier (che rinominano outer in a, secret in b e così via) romperebbero qualsiasi codice che facesse riferimento a quei nomi come stringhe. Limitando l'accesso allo scope globale, il linguaggio mantiene la minificazione sicura. La conclusione pratica: passa tutto ciò di cui una funzione dinamica ha bisogno tramite i suoi argomenti, senza mai aspettarsi che legga le variabili circostanti.

Proprietà di una funzione dinamica

Una funzione costruita in questo modo è un normale function object sotto ogni altro aspetto, con una particolarità — il suo name è sempre "anonymous":

javascript— editable

Quel nome quasi vuoto è uno dei motivi per cui le funzioni dinamiche sono più difficili da leggere negli stack trace — vedi la nota sul debugging qui sotto.

new Function vs. eval

Sia new Function che eval trasformano stringhe in codice in esecuzione, ma si comportano in modo molto diverso:

  • eval(str) esegue str nello scope corrente, quindi può leggere e persino modificare le variabili locali vicine. Quell'accoppiamento stretto lo rende più difficile da ottimizzare e più facile da usare in modo improprio.
  • new Function è isolata dallo scope locale (come mostrato sopra) e restituisce una funzione riutilizzabile piuttosto che una valutazione una tantum.

Per il raro caso in cui si ha davvero bisogno di eseguire codice da una string, new Function è la scelta più sicura tra le due perché il suo raggio d'azione è limitato allo scope globale e ai suoi parametri espliciti.

Applicazioni pratiche ed esempi

Di seguito è riportato un esempio completo ed eseguibile che puoi modificare ed eseguire nel browser.

"Provalo tu stesso" non è disponibile per questo esempio.

Approfondimento sulla creazione dinamica di funzioni

La creazione dinamica di funzioni in JavaScript, facilitata dalla sintassi new Function, è una tecnica potente che consente agli sviluppatori di costruire funzioni da stringhe di codice a runtime. Questa capacità è particolarmente utile negli scenari in cui il codice da eseguire non è statico o noto in anticipo, come nelle applicazioni che richiedono un alto grado di flessibilità o nelle situazioni in cui gli script vengono generati o modificati dinamicamente. In questa sezione, approfondiremo i meccanismi, i vantaggi e le considerazioni della creazione dinamica di funzioni, offrendo una comprensione più ricca ed esempi pratici per illustrarne il potenziale.

Meccanismi della creazione dinamica di funzioni

La sintassi new Function crea una nuova istanza di funzione. Gli argomenti del costruttore new Function sono stringhe che rappresentano gli argomenti della funzione, seguiti da una string che rappresenta il corpo della funzione.

javascript— editable

Questo equivale funzionalmente alla dichiarazione di una funzione nel modo tradizionale, ma con la differenza fondamentale della capacità di assemblare il codice della funzione in modo dinamico, a runtime.

Vantaggi della creazione dinamica di funzioni

  1. Flessibilità e personalizzazione: La creazione dinamica di funzioni consente un alto grado di personalizzazione, poiché le funzioni possono essere generate in base all'input dell'utente, alle impostazioni di configurazione o ad altri dati di runtime.
  2. Scripting e templating: È particolarmente utile nell'implementazione di soluzioni di scripting personalizzate o motori di template in cui la logica del template deve essere valutata a runtime.
  3. Isolamento e sicurezza: Se usato con cura, può eseguire il codice in un ambiente più controllato, isolando potenzialmente il codice eseguito dinamicamente dal contesto principale dell'applicazione.

Considerazioni e best practice

Sebbene la creazione dinamica di funzioni sia potente, presenta le sue considerazioni:

  1. Sicurezza: La preoccupazione principale è la sicurezza. Poiché il codice della funzione viene costruito da stringhe, c'è il rischio di eseguire codice malevolo se l'input non viene opportunamente sanificato. Valida e sanifica sempre l'input che verrà utilizzato per generare il codice della funzione.
  2. Prestazioni: Le funzioni create dinamicamente possono essere meno performanti rispetto alle loro controparti dichiarate staticamente, poiché il motore JavaScript deve analizzare la string del corpo della funzione ogni volta che viene creata una nuova funzione. Usa questa funzionalità con giudizio, specialmente nei percorsi critici per le prestazioni.
  3. Debugging: Il debugging delle funzioni generate dinamicamente può essere più impegnativo, poiché il codice non esiste fino al runtime. Fornire nomi significativi alle funzioni create dinamicamente può aiutare a mitigare questo problema.
  4. Limitazione dello scope lessicale: Le funzioni create con new Function non catturano lo scope locale in cui sono definite. Hanno accesso solo alle variabili globali e ai propri parametri. Questo può portare a ReferenceError se ci si aspetta che accedano a variabili esterne. (Vedi la sezione sullo Scope sopra; passa i dati tramite argomenti.)

Esempio avanzato: un semplice motore di template

Per illustrare l'uso pratico della creazione dinamica di funzioni, considera l'implementazione di un semplice motore di template. Questo motore sostituirà i segnaposto in una string di template con valori provenienti da un object dati — e, aspetto cruciale, i dati vengono passati come argomento, aggirando la limitazione dello scope.

javascript— editable

Nota: La sequenza \${ fa l'escape della sintassi del template literal. Questo impedisce che il segnaposto ${expr} venga valutato immediatamente, assicurando che venga passato come string letterale al corpo della funzione generata.

Questo esempio dimostra non solo la flessibilità offerta dalla creazione dinamica di funzioni, ma evidenzia anche l'importanza di costruire e sanificare attentamente l'input per evitare rischi di sicurezza.

Il costruttore new Function costruisce una funzione da stringhe a runtime:

let func = new Function([arg1, ..., argN], functionBody);

Punti chiave da ricordare:

  • L'ultimo argomento è sempre il corpo della funzione; i precedenti nominano i parametri.
  • Lo scope esterno di una funzione creata dinamicamente è globale, non il luogo in cui è stata creata — non può chiudersi su variabili locali, quindi passa i dati tramite argomenti.
  • È generalmente più sicura di eval (che viene eseguita nello scope corrente), ma entrambe valutano stringhe, quindi fornisci sempre input attendibile e sanificato.
  • Usala per codice genuinamente dinamico — motori di template, valutatori di espressioni in sandbox, handler generati a runtime — e preferisci le normali functions o le arrow functions in tutti gli altri casi.

Per approfondire gli argomenti correlati, vedi JavaScript Closures, Variable Scope e Function Expressions.

Esercitazione

Pratica
Quali sono le caratteristiche e le best practice associate alla sintassi new function in JavaScript?
Quali sono le caratteristiche e le best practice associate alla sintassi new function in JavaScript?
Was this page helpful?