Sind eval () und new Function () dasselbe?


89

Tun diese beiden Funktionen hinter den Kulissen dasselbe? (in Einzelanweisungsfunktionen)

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'));

3
Tatsächlich wird dies in Zeile 573 von jQuery 1.7.2 verwendet. Allerdings mit "einigen Sicherheitsüberprüfungen"
PicoCreator

2
Warum wird newin dieser Diskussion berücksichtigt? Functionimplizit instanziiert a function object. Durch das Ausschließen newwird der Code überhaupt nicht geändert. Hier ist eine jsfiddle, die das demonstriert: jsfiddle.net/PcfG8
Travis J

Antworten:


113

Nein, sie sind nicht gleich.

  • eval() wertet eine Zeichenfolge als JavaScript-Ausdruck im aktuellen Ausführungsbereich aus und kann auf lokale Variablen zugreifen.
  • new Function()analysiert den in einer Zeichenfolge gespeicherten JavaScript-Code in ein Funktionsobjekt, das dann aufgerufen werden kann. Es kann nicht auf lokale Variablen zugreifen, da der Code in einem separaten Bereich ausgeführt wird.

Betrachten Sie diesen Code:

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

Bei new Function('return (a = 22);')()Verwendung awürde die lokale Variable ihren Wert behalten. Einige JavaScript-Programmierer wie Douglas Crockford sind jedoch der Ansicht, dass keines von beiden verwendet werden sollte, es sei denn, dies ist unbedingt erforderlich , und das Auswerten / Verwenden des FunctionKonstruktors für nicht vertrauenswürdige Daten ist unsicher und unklug.


10
"sollte niemals verwendet werden" ist eine so breite Aussage. Wie wäre es, sollte niemals verwendet werden, wenn einer der Eingänge außerhalb Ihrer Kontrolle liegt. Trotzdem musste ich es nie verwenden, außer um JSON zu analysieren. Aber ich habe hier Fragen beantwortet, die als gültige Verwendungszwecke erschienen. Es ging darum, Administratoren das Generieren von Vorlagenfunktionen zu ermöglichen, die Endbenutzer verwenden können. In diesem Fall wurde die Eingabe von einem Administrator generiert, so dass es akzeptabel war
Juan Mendes

3
@Juan: Crockfords Argument ist, dass die Bewertung häufig missbraucht wird (abhängig von Ihrer Perspektive), daher sollte sie von einem JavaScript mit "Best Practices" ausgeschlossen werden. Ich stimme jedoch zu, dass es für Anwendungen wie JSON oder das Parsen von arithmetischen Ausdrücken nützlich ist, wenn die Eingabe zum ersten Mal ordnungsgemäß validiert wird. Sogar Crockford verwendet es in seiner JSON-Bibliothek json2.js.
PleaseStand

Bedeutet dies also, dass dies new Function(code)derselbe ist eval('(function(){'+code+'})()')oder ein völlig neuer Ausführungskontext?
George Mauer

5
@ GeorgeMauer: Ich denke du meinst eval('(function(){'+code+'})'), was ein Funktionsobjekt zurückgeben würde. Laut MDN erstellen "mit dem Funktionskonstruktor erstellte Funktionen keine Abschlüsse für ihre Erstellungskontexte; sie werden immer im Fensterkontext ausgeführt (es sei denn, der Funktionskörper beginnt mit einer Anweisung" use strict "; in diesem Fall ist der Kontext undefiniert). . "
PleaseStand

6

Nein.

In Ihrem Update rufen die Aufrufe dasselbe Ergebnis auf evaluateund funcführen zu demselben Ergebnis. Aber sie tun definitiv nicht "das Gleiche hinter den Kulissen". Die funcFunktion erstellt eine neue Funktion, führt sie jedoch sofort aus, während die evaluateFunktion den Code einfach vor Ort ausführt.

Aus der ursprünglichen Frage:

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

Dies führt zu sehr unterschiedlichen Ergebnissen:

evaluate('0) + (4');
func('0) + (4');

In jedem Fall sind beide in allen außer den außergewöhnlichsten Fällen eine schlechte Praxis.
Palswim

8
Nun, Sie haben Ihr Beispiel gemäß seiner Implementierung verfälscht. Hätte er evaluiert wie return eval('(' + string + ')');damals, würden sie die gleichen Ergebnisse liefern.
Juan Mendes

@ Juan Ich habe nicht daran gedacht, aber ja. Bearbeitet die Frage
qwertymk

6

new Function erstellt eine Funktion, die wiederverwendet werden kann. evalführt nur die angegebene Zeichenfolge aus und gibt das Ergebnis der letzten Anweisung zurück. Ihre Frage ist falsch, als Sie versucht haben, eine Wrapper-Funktion zu erstellen, die Function verwendet, um eine Bewertung zu emulieren.

Stimmt es, dass sie Code hinter den Vorhängen teilen? Ja, sehr wahrscheinlich. Genau der gleiche Code? Nein, sicher.

Zum Spaß ist hier meine eigene unvollständige Implementierung, bei der eval zum Erstellen einer Funktion verwendet wird. Hoffe, es bringt etwas Licht in den Unterschied!

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]');
}

Der größte Unterschied zwischen dieser und der neuen Funktion besteht darin, dass die Funktion nicht lexikalisch festgelegt ist. Es hätte also keinen Zugriff auf Schließungsvariablen und meine würde.


warum nicht arguments.slice()anstelle der for-Schleife verwenden? Oder wenn Argumente kein echtes Array sind , [].slice.call(arguments, 1).
Xeoncross

3

Ich möchte nur auf die in den Beispielen verwendete Syntax und deren Bedeutung hinweisen:

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

Beachten Sie, dass die Funktion (...) () am Ende das "()" hat. Diese Syntax bewirkt, dass func die neue Funktion ausführt und die Zeichenfolge zurückgibt, keine Funktion, die eine Zeichenfolge zurückgibt. Wenn Sie jedoch Folgendes verwenden:

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

Jetzt gibt func eine Funktion zurück, die einen String zurückgibt.


2

Wenn Sie meinen, wird es die gleichen Ergebnisse liefern, dann ja ... aber nur zu bewerten (auch bekannt als "Bewertung dieser JavaScript-Zeichenfolge") wäre viel einfacher.

BEARBEITEN Unten:

Es ist wie zu sagen ... sind diese beiden mathematischen Probleme gleich:

1 + 1

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


analysieren beide die Zeichenfolge erneut?
qwertymk

Beide analysieren die Zeichenfolge (in Ihrem Codebeispiel). Ich weiß nicht, was du mit "neu analysieren" meinst.
Timothy Khouri

1
Ich bin sicher, dass beide die Zeichenfolge irgendwann analysieren (auswerten), aber das FunctionObjekt speichert die Zeichenfolge wahrscheinlich in einer privaten Mitgliedsvariablen, bis evalSie die Funktion aufrufen.
Palswim

1

In diesem Beispiel sind die Ergebnisse dieselben, ja. Beide führen den übergebenen Ausdruck aus. Das macht sie so gefährlich.

Aber sie machen verschiedene Dinge hinter der Kulisse. Derjenige new Function(), der hinter den Kulissen involviert ist, erstellt aus dem von Ihnen angegebenen Code eine anonyme Funktion, die ausgeführt wird, wenn die Funktion aufgerufen wird.

Das JavaScript, das Sie übergeben, wird technisch erst ausgeführt, wenn Sie die anonyme Funktion aufrufen. Dies steht im Gegensatz dazu, eval()dass der Code sofort ausgeführt wird und keine darauf basierende Funktion generiert wird.


Können Sie etwas mehr erklären? Werden sie nicht beide in der return-Anweisung ausgeführt? Verwendet die Funktion () eine andere Stapelaufrufebene als eval?
qwertymk

Does the Function() one use another stack call level than eval?: Ich würde es annehmen, weil eval()es keine Funktion aus Ihrer Eingabe erstellt - es führt sie nur aus. new Function()erstellt eine neue Funktion, die dann aufgerufen wird.
Chris Laplante

@SimpleCoder: Function () fügt dem Aufrufstapel nichts hinzu. Nur wenn Sie die resultierende Funktion aufrufen. eval macht genau das Gleiche (wenn im Zusammenhang mit der Frage angewendet)
Juan Mendes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.