Syntaxe de fonction JavaScript
Apprenez la syntaxe "new Function" en JavaScript : construire des fonctions depuis des chaînes à l'exécution, règles de portée, sécurité et quand l'utiliser.
La plupart du temps, vous créez une fonction avec une déclaration, une expression ou une fonction fléchée. Mais JavaScript offre une autre possibilité : le constructeur new Function, qui construit une fonction à partir de chaînes de caractères à l'exécution. Cette page se concentre sur cette syntaxe — sa forme exacte, les règles de portée qui surprennent la plupart des développeurs, sa comparaison avec eval, et les rares cas où c'est l'outil adapté.
La syntaxe new Function
La syntaxe new Function vous permet de créer une fonction dont les paramètres et le corps sont fournis sous forme de chaînes de caractères. Comme le corps n'est que du texte jusqu'à ce que le moteur le parse, vous pouvez assembler une fonction dont le code n'est pas connu au moment de l'écriture du programme — seulement au moment de son exécution.
La forme générale est :
let func = new Function([arg1, arg2, ...argN], functionBody);Chaque argument est une string. Les premiers arguments donnent les noms des paramètres ; le dernier argument est toujours le corps de la fonction.
Vous pouvez passer tous les noms de paramètres dans une seule string séparée par des virgules, ce qui est équivalent :
Le mot-clé new est optionnel ici — Function('a', 'b', 'return a + b') produit le même résultat — mais écrire new Function(...) est la forme conventionnelle et plus claire.
Pourquoi ça existe
La principale différence entre new Function et une déclaration normale est que le corps provient d'une string. Cette string peut venir de n'importe où : une réponse serveur, un template, une configuration utilisateur, ou un texte construit à l'exécution. Cette syntaxe existe donc exactement pour les cas où le code que vous souhaitez exécuter n'existe pas encore au moment où vous écrivez le programme.
Portée : le piège majeur
C'est le détail qui piège tout le monde. Une fonction créée avec new Function ne capture pas la portée où elle a été créée. Contrairement à une closure ordinaire, son environnement lexical externe est la portée globale, pas la portée 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 definedUne fonction normale écrite de la même manière fermerait volontiers sur outer. La version new Function ne le peut pas — elle ne voit que ses propres paramètres et la portée globale :
C'est intentionnel. Si new Function pouvait accéder aux variables locales, les minifieurs (qui renomment outer en a, secret en b, etc.) briseraient tout code référençant ces noms sous forme de chaînes. En restreignant l'accès à la portée globale, le langage préserve la compatibilité avec la minification. La conclusion pratique : passez tout ce dont une fonction dynamique a besoin via ses arguments, ne comptez jamais sur la lecture de variables environnantes.
Propriétés d'une fonction dynamique
Une fonction construite de cette façon est un object fonction normal à tous autres égards, avec une particularité — son name est toujours "anonymous" :
Ce nom vide est l'une des raisons pour lesquelles les fonctions dynamiques sont plus difficiles à lire dans les traces de pile — voir la note sur le débogage ci-dessous.
new Function vs. eval
new Function et eval transforment tous deux des strings en code exécutable, mais ils se comportent très différemment :
eval(str)exécutestrdans la portée courante, il peut donc lire et même modifier les variables locales environnantes. Ce couplage fort le rend plus difficile à optimiser et plus facile à mal utiliser.new Functionest isolé de la portée locale (comme montré ci-dessus) et vous renvoie une fonction réutilisable plutôt qu'une évaluation ponctuelle.
Dans le rare cas où vous avez genuinement besoin d'exécuter du code à partir d'une string, new Function est le plus sûr des deux car son rayon d'action se limite à la portée globale et à ses paramètres explicites.
Applications pratiques et exemples
Voici un exemple complet et exécutable que vous pouvez modifier et exécuter dans le navigateur.
Approfondissement de la création de fonctions dynamiques
La création de fonctions dynamiques en JavaScript, facilitée par la syntaxe new Function, est une technique puissante qui permet aux développeurs de construire des fonctions à partir de chaînes de code à l'exécution. Cette capacité est particulièrement utile dans les scénarios où le code à exécuter n'est pas statique ou connu à l'avance, comme dans les applications nécessitant un haut degré de flexibilité ou dans les situations où des scripts sont générés ou modifiés dynamiquement. Dans cette section, nous approfondirons la mécanique, les avantages et les considérations de la création de fonctions dynamiques, en offrant une compréhension plus riche et des exemples pratiques pour illustrer son potentiel.
Mécanique de la création de fonctions dynamiques
La syntaxe new Function crée une nouvelle instance de fonction. Les arguments du constructeur new Function sont des strings représentant les arguments de la fonction, suivies d'une string représentant le corps de la fonction.
Cela équivaut fonctionnellement à déclarer une fonction de manière traditionnelle, mais avec la différence clé que le code de la fonction peut être assemblé dynamiquement, à l'exécution.
Avantages de la création de fonctions dynamiques
- Flexibilité et personnalisation : La création de fonctions dynamiques permet un haut degré de personnalisation, car les fonctions peuvent être générées en fonction des entrées utilisateur, des paramètres de configuration ou d'autres données d'exécution.
- Scripting et templating : Elle est particulièrement utile pour implémenter des solutions de scripting personnalisées ou des moteurs de templates où la logique du template doit être évaluée à l'exécution.
- Isolation et sécurité : Utilisée avec précaution, elle peut exécuter du code dans un environnement plus contrôlé, isolant potentiellement le code exécuté dynamiquement du contexte principal de l'application.
Considérations et bonnes pratiques
La création de fonctions dynamiques est puissante, mais elle s'accompagne d'un ensemble de considérations :
- Sécurité : La principale préoccupation est la sécurité. Comme le code de la fonction est construit à partir de strings, il existe un risque d'exécuter du code malveillant si l'entrée n'est pas correctement assainie. Validez et assainissez toujours les entrées qui seront utilisées pour générer du code de fonction.
- Performance : Les fonctions créées dynamiquement peuvent être moins performantes que leurs équivalentes déclarées statiquement, car le moteur JavaScript doit parser la string du corps de la fonction à chaque création d'une nouvelle fonction. Utilisez cette fonctionnalité avec discernement, en particulier dans les chemins critiques pour les performances.
- Débogage : Le débogage de fonctions générées dynamiquement peut être plus difficile, car le code n'existe pas avant l'exécution. Donner des noms significatifs aux fonctions créées dynamiquement peut aider à atténuer ce problème.
- Limitation de la portée lexicale : Les fonctions créées avec
new Functionne capturent pas la portée locale où elles sont définies. Elles n'ont accès qu'aux variables globales et à leurs propres paramètres. Cela peut entraîner desReferenceErrors si vous attendez qu'elles accèdent à des variables externes. (Voir la section Portée ci-dessus ; passez les données via des arguments.)
Exemple avancé : un moteur de templates simple
Pour illustrer l'utilisation pratique de la création de fonctions dynamiques, considérons l'implémentation d'un moteur de templates simple. Ce moteur remplacera les espaces réservés dans une string de template par des valeurs provenant d'un object de données — et, point crucial, les données sont passées en argument, contournant ainsi la limitation de portée.
Note : La séquence \${ échappe la syntaxe de template littéral. Cela empêche l'espace réservé ${expr} d'être évalué immédiatement, garantissant qu'il est passé en tant que string littérale au corps de la fonction générée.
Cet exemple démontre non seulement la flexibilité offerte par la création de fonctions dynamiques, mais souligne également l'importance d'une construction soignée et de l'assainissement des entrées pour éviter les risques de sécurité.
Résumé
Le constructeur new Function construit une fonction à partir de strings à l'exécution :
let func = new Function([arg1, ..., argN], functionBody);Points clés à retenir :
- Le dernier argument est toujours le corps de la fonction ; les précédents donnent les noms des paramètres.
- La portée externe d'une fonction créée dynamiquement est globale, pas l'endroit où elle a été créée — elle ne peut pas fermer sur des variables locales, donc passez les données via des arguments.
- Elle est généralement plus sûre que
eval(qui s'exécute dans la portée courante), mais les deux évaluent des strings, donc ne l'alimentez jamais qu'avec des entrées fiables et assainies. - Utilisez-la pour du code genuinement dynamique — moteurs de templates, évaluateurs d'expressions en sandbox, gestionnaires générés à l'exécution — et préférez les fonctions ordinaires ou les fonctions fléchées partout ailleurs.
Pour approfondir les sujets connexes, consultez JavaScript Closures, Variable Scope et Function Expressions.