Il existe une autre façon de créer une fonction. C'est rarement utilisé, mais parfois il n'y a pas d'alternative.
La syntaxe pour créer une fonction :
let func = new Function ([arg1, arg2, ...argN], functionBody);
La fonction est créée avec les arguments arg1...argN
et le functionBody
donné.
C'est plus facile à comprendre en regardant un exemple. Voici une fonction avec deux arguments :
let sum = new Function('a', 'b', 'return a + b'); alerte( somme(1, 2) ); // 3
Et ici, il y a une fonction sans arguments, avec seulement le corps de la fonction :
let sayHi = new Function('alert("Bonjour")'); disBonjour(); // Bonjour
La principale différence par rapport aux autres méthodes que nous avons vues est que la fonction est créée littéralement à partir d'une chaîne transmise au moment de l'exécution.
Toutes les déclarations précédentes exigeaient que nous, programmeurs, écrivions le code de la fonction dans le script.
Mais new Function
permet de transformer n’importe quelle chaîne en fonction. Par exemple, nous pouvons recevoir une nouvelle fonction d'un serveur puis l'exécuter :
let str = ... recevoir le code d'un serveur de manière dynamique ... let func = new Function(str); fonction();
Il est utilisé dans des cas très spécifiques, comme lorsque nous recevons du code d'un serveur, ou pour compiler dynamiquement une fonction à partir d'un modèle, dans des applications Web complexes.
Habituellement, une fonction se souvient de son lieu de naissance dans la propriété spéciale [[Environment]]
. Il fait référence à l'environnement lexical à partir duquel il a été créé (nous en avons parlé dans le chapitre Portée des variables, fermeture).
Mais lorsqu'une fonction est créée à l'aide de new Function
, son [[Environment]]
est défini pour faire référence non pas à l'environnement lexical actuel, mais à l'environnement global.
Ainsi, une telle fonction n’a pas accès aux variables externes, uniquement aux variables globales.
fonction getFunc() { laissez valeur = "test" ; let func = new Function('alerte(valeur)'); fonction de retour ; } getFunc()(); // erreur : la valeur n'est pas définie
Comparez-le avec le comportement régulier :
fonction getFunc() { laissez valeur = "test" ; let func = function() { alert(value); } ; fonction de retour ; } getFunc()(); // "test", issu de l'Environnement Lexical de getFunc
Cette particularité de new Function
semble étrange, mais semble très utile en pratique.
Imaginez que nous devons créer une fonction à partir d'une chaîne. Le code de cette fonction n'est pas connu au moment de l'écriture du script (c'est pourquoi nous n'utilisons pas de fonctions régulières), mais il le sera au cours du processus d'exécution. Nous pouvons le recevoir du serveur ou d'une autre source.
Notre nouvelle fonction doit interagir avec le script principal.
Et s’il pouvait accéder aux variables externes ?
Le problème est qu'avant que JavaScript ne soit publié en production, il est compressé à l'aide d'un minifier – un programme spécial qui réduit le code en supprimant les commentaires et les espaces supplémentaires et, ce qui est important, renomme les variables locales en variables plus courtes.
Par exemple, si une fonction a let userName
, minifier le remplace par let a
(ou une autre lettre si celle-ci est occupée), et le fait partout. C'est généralement une chose sûre à faire, car la variable est locale, rien en dehors de la fonction ne peut y accéder. Et à l’intérieur de la fonction, le minifier remplace chaque mention de celle-ci. Les minificateurs sont intelligents, ils analysent la structure du code pour ne rien casser. Il ne s’agit pas simplement d’une simple recherche et remplacement.
Ainsi, si new Function
avait accès aux variables externes, elle ne pourrait pas trouver userName
renommé.
Si new Function
avait accès aux variables externes, elle aurait des problèmes avec les minificateurs.
En outre, un tel code serait architecturalement mauvais et sujet aux erreurs.
Pour transmettre quelque chose à une fonction créée en tant que new Function
, nous devons utiliser ses arguments.
La syntaxe :
let func = new Function ([arg1, arg2, ...argN], functionBody);
Pour des raisons historiques, les arguments peuvent également être donnés sous forme de liste séparée par des virgules.
Ces trois déclarations signifient la même chose :
new Function('a', 'b', 'return a + b'); // syntaxe de base new Function('a,b', 'retour a + b'); // séparés par des virgules new Function('a, b', 'return a + b'); // séparés par des virgules par des espaces
Les fonctions créées avec new Function
ont [[Environment]]
faisant référence à l'environnement lexical global, et non à l'environnement externe. Ils ne peuvent donc pas utiliser de variables externes. Mais c'est en fait une bonne chose, car cela nous assure contre les erreurs. Passer explicitement des paramètres est une bien meilleure méthode sur le plan architectural et ne pose aucun problème avec les minificateurs.