Eval () et new Function () sont-ils la même chose?

89

Ces deux fonctions font-elles la même chose dans les coulisses? (dans les fonctions d'instruction unique)

var evaluate = function(string) {
    return eval('(' + string + ')');
}

var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

console.log(evaluate('2 + 1'));
console.log(func('2 + 1'));
qwertymk
la source
3
En fait, cela est utilisé dans jQuery 1.7.2 ligne 573. Bien qu'avec "quelques contrôles de sécurité"
PicoCreator
2
Pourquoi est newconsidéré dans cette discussion? Functioninstancie implicitement un function object. L'exclusion newne changera pas du tout le code. Voici un jsfiddle démontrant que: jsfiddle.net/PcfG8
Travis J

Réponses:

114

Non, ce ne sont pas les mêmes.

  • eval() évalue une chaîne en tant qu'expression JavaScript dans la portée d'exécution actuelle et peut accéder aux variables locales.
  • new Function()analyse le code JavaScript stocké dans une chaîne dans un objet fonction, qui peut ensuite être appelé. Il ne peut pas accéder aux variables locales car le code s'exécute dans une portée distincte.

Considérez ce code:

function test1() {
    var a = 11;
    eval('(a = 22)');
    alert(a);            // alerts 22
}

Si elles new Function('return (a = 22);')()étaient utilisées, la variable locale aconserverait sa valeur. Néanmoins, certains programmeurs JavaScript tels que Douglas Crockford pensent que ni l'un ni l'autre ne devrait être utilisé à moins que cela ne soit absolument nécessaire , et l'évaluation / l'utilisation du Functionconstructeur sur des données non fiables n'est pas sécurisée et imprudente.

Veuillez vous lever
la source
10
"ne devrait jamais être utilisé" est une déclaration si large. Que diriez-vous, ne devrait jamais être utilisé lorsque l'une des entrées est hors de votre contrôle. Cela dit, je n'ai jamais eu besoin de l'utiliser, sauf pour analyser JSON. Mais j'ai répondu ici à des questions qui semblaient être des utilisations valides, cela impliquait quelque chose comme permettre aux administrateurs de générer des fonctions de modèle qu'un utilisateur final pourrait utiliser. Dans ce cas, l'entrée a été générée par un administrateur, donc c'était acceptable
Juan Mendes
3
@Juan: Le point de Crockford est que eval est souvent mal utilisé (cela dépend de votre point de vue), il devrait donc être exclu d'un JavaScript des «meilleures pratiques». Je conviens, cependant, qu'il est utile pour des utilisations telles que JSON ou l'analyse d'expressions arithmétiques lorsque l'entrée est correctement validée pour la première fois. Même Crockford l'utilise dans sa bibliothèque JSON json2.js.
PleaseStand
Cela signifie-t-il que new Function(code)c'est la même chose eval('(function(){'+code+'})()')ou s'agit-il d'un contexte d'exécution entièrement nouveau?
George Mauer
5
@GeorgeMauer: Je pense que vous voulez dire eval('(function(){'+code+'})'), ce qui renverrait un objet fonction. Selon MDN , «les fonctions créées avec le constructeur Function ne créent pas de fermetures dans leurs contextes de création; elles s'exécutent toujours dans le contexte de la fenêtre (sauf si le corps de la fonction commence par une instruction" use strict ";, auquel cas le contexte n'est pas défini) . "
PleaseStand
6

Non.

Dans votre mise à jour, les appels à evaluateet funcproduisent le même résultat. Mais ils ne "font certainement pas la même chose dans les coulisses". La funcfonction crée une nouvelle fonction, mais l'exécute immédiatement, alors que la evaluatefonction exécute simplement le code sur place.

De la question d'origine:

var evaluate = function(string) {
    return eval(string);
}
var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

Ceux-ci vous donneront des résultats très différents:

evaluate('0) + (4');
func('0) + (4');
palswim
la source
Dans tous les cas, les deux sont de mauvaises pratiques dans tous les cas sauf les plus exceptionnels.
palswim
8
Eh bien, vous avez truqué votre exemple en fonction de sa mise en œuvre. S'il avait mis en œuvre, évaluer car return eval('(' + string + ')');alors ils donneraient les mêmes résultats.
Juan Mendes
@Juan, je n'y ai pas pensé mais ouais. A
modifié
6

new Functioncrée une fonction qui peut être réutilisée. evalexécute simplement la chaîne donnée et renvoie le résultat de la dernière instruction. Votre question est erronée lorsque vous avez tenté de créer une fonction wrapper qui utilise Function pour émuler une évaluation.

Est-il vrai qu'ils partagent du code derrière les rideaux? Oui, très probablement. Exactement le même code? Non, certainement.

Pour le plaisir, voici ma propre implémentation imparfaite utilisant eval pour créer une fonction. J'espère que cela éclairera la différence!

function makeFunction() {
  var params = [];
  for (var i = 0; i < arguments.length -  1; i++) {
    params.push(arguments[i]);
  }
  var code = arguments[arguments.length -  1];


 // Creates the anonymous function to be returned
 // The following line doesn't work in IE
 // return eval('(function (' + params.join(',')+ '){' + code + '})');
 // This does though
 return eval('[function (' + params.join(',')+ '){' + code + '}][0]');
}

La plus grande différence entre cette fonction et la nouvelle fonction est que la fonction n'est pas de portée lexicale. Il n'aurait donc pas accès aux variables de fermeture et la mienne le ferait.

Juan Mendes
la source
pourquoi ne pas utiliser à la arguments.slice()place de la boucle for? Ou si les arguments n'est pas un tableau vrai, [].slice.call(arguments, 1).
Xeoncross
3

Je veux juste souligner une syntaxe utilisée dans les exemples ici et ce que cela signifie:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' )());
 }

notez que la fonction (...) () a le "()" à la fin. Cette syntaxe obligera func à exécuter la nouvelle fonction et à renvoyer la chaîne, pas une fonction qui renvoie une chaîne, mais si vous utilisez ce qui suit:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' ));
 }

Maintenant func retournera une fonction qui retourne une chaîne.

Jack D Menendez
la source
2

Si vous voulez dire, cela donnera-t-il les mêmes résultats, alors oui ... mais juste pour eval (aka, "évaluer cette chaîne de JavaScript") serait beaucoup plus simple.

EDIT ci-dessous:

C'est comme dire ... ces deux problèmes mathématiques sont-ils les mêmes:

1 + 1

1 + 1 + 1 - 1 + 1 - 1 * 1/1

Timothy Khouri
la source
analysent-ils tous les deux la chaîne?
qwertymk
ils analyseront tous les deux la chaîne (dans votre exemple de code). ne sais pas ce que vous entendez par "re-parse".
Timothy Khouri
1
Je suis sûr qu'ils analysent tous les deux (évaluent) la chaîne à un moment donné, mais l' Functionobjet stocke probablement la chaîne dans une variable de membre privé evallorsque vous appelez la fonction.
palswim
1

Dans cet exemple, les résultats sont les mêmes, oui. Les deux exécutent l'expression que vous transmettez. C'est ce qui les rend si dangereux.

Mais ils font des choses différentes derrière le scense. Celui qui implique new Function(), en coulisses, crée une fonction anonyme à partir du code que vous fournissez, qui est exécutée lorsque la fonction est appelée.

Le JavaScript que vous lui transmettez n'est techniquement pas exécuté tant que vous n'appelez pas la fonction anonyme. Cela contraste avec eval()qui exécute le code tout de suite et ne génère pas de fonction basée sur celui-ci.

Chris Laplante
la source
Pouvez-vous en dire un peu plus? Ne sont-ils pas tous les deux exécutés dans l'instruction de retour? Le Function () one utilise-t-il un autre niveau d'appel de pile que eval?
qwertymk
Does the Function() one use another stack call level than eval?: Je suppose que oui, car eval()ne crée pas de fonction à partir de votre entrée - il l'exécute simplement. new Function()crée une nouvelle fonction, qu'il appelle ensuite.
Chris Laplante
@SimpleCoder: Function () n'ajoute rien à la pile d'appels. Uniquement si vous appelez la fonction résultante. eval fait exactement la même chose (si appliquée dans le contexte de la question)
Juan Mendes