2010-06-11 10 views
36

Wie wiederholt gesagt, ist es eine schlechte Praxis betrachtet die Function constructor (siehe auch die ECMAScript Language Specification, 5 th Auflage, § 15.3.2.1) zu verwenden:legitime Nutzungen der Funktion Konstruktor

new Function ([arg1[, arg2[, … argN]],] functionBody) 

(wo alle Argumente sind Strings, die Argumentnamen enthalten und der letzte (oder einzige) String enthält den Funktionskörper).

Um rekapitulieren, so heißt es, langsam sein, wie von the Opera team erklärt:

Jedes Mal, [...] der Function Konstruktor an einer Schnur genannt wird Quellcode darstellt, muss das Skript Motor starten Die Maschine, die konvertiert den Quellcode in ausführbare Code. Dies ist in der Regel teuer für Leistung - leicht hundert Mal teurer als eine einfache Funktion Aufruf, zum Beispiel. (Mark ‚Tarquin‘ Wilton-Jones)

Obwohl es nicht dass schlecht, nach this post am MDC (Ich habe das nicht teste ich die aktuelle Version von Firefox, obwohl).

Crockford adds die

[d] ie Zitierkonventionen der Sprache es sehr schwierig, machen richtig eine Funktion Körper als String auszudrücken. In der Zeichenkettenform kann die frühe Fehlerüberprüfung nicht getan werden. [...] Und es ist Verschwendung von Speicher, weil jede Funktion eine eigene unabhängige Implementierung benötigt.

Ein weiterer Unterschied besteht darin, dass

eine Funktion durch eine Funktion Konstruktor definiert erbt keinen Spielraum andere als die globale Reichweite (die alle Funktionen erben). (MDC)

Davon abgesehen, müssen Sie aufmerksam seine Injektion von bösartigem Code zu vermeiden, wenn Sie einen new Function mit dynamischen Inhalten erstellen.

Das sagte T.J. Crowder sagt in an answer dass

[t] hier ist so gut wie nie eine Notwendigkeit für die ähnlich [...] neue Funktion (...), entweder wieder mit Ausnahme von einigen fortgeschrittenen Grenzfällen.

Also, jetzt frage ich mich: Was sind diese "Advanced Edge Cases"? Gibt es legitime Verwendungen des Konstruktors Function?

+0

ES5 im strikten Modus wird nicht geworfen, wenn 'neue Funktion' verwendet wird ... –

+0

@ Šime: Gemäß der Spezifikation (Anhang C) sollte es:" Versuch, eine solche strikte Modusfunktion dynamisch mit der Funktion zu definieren Konstruktor (15.3.2) wird eine "SyntaxError" -Ausnahme auslösen, aber ein Test ergab, dass dies nicht der Fall ist. Versteh ich etwas falsch? –

+0

Nun, lies den vorhergehenden Satz. Eine solche strikte Modusfunktion bezieht sich auf eine Funktion, bei der entweder der Name oder einer der Parameter entweder "eval" oder "arguments" ist. –

Antwort

8

jQuery verwendet es zum Analysieren von JSON-Zeichenfolgen, wenn ein JSON-Parserobjekt nicht verfügbar ist. Scheint mir echt :)

 // Try to use the native JSON parser first 
     return window.JSON && window.JSON.parse ? 
      window.JSON.parse(data) : 
      (new Function("return " + data))(); 
+1

Die Verwendung des vollständigen Interpreters zum Analysieren eines Datenformats ist nicht ideal; Es öffnet die Tür zur Skript-Injektion. (* Viele * Leute tun es, aber es ist nicht nur jQuery. :)) –

+1

Die Daten sind bereits zu diesem Zeitpunkt von jQuery bereinigt, dies geschieht rein für Geschwindigkeit. –

6

ich den new Function() Konstruktor als in-line JS-Interpreter in einer der Web-Anwendungen Ich entwickle verwenden:

function interpret(s) { 
    //eval(s); <-- even worse practice 
    try { 
     var f = new Function(s); 
     f(); 
    } 
    catch (err) { 
     //graceful error handling in the case of malformed code 
    } 
} 

Wie ich Zeug Streaming über AJAX (nicht ein iframe), ich kontinuierlich interpret() es auf readyStateChange == 3. Dies funktioniert überraschend gut.

bearbeiten: hier ist ein klarer Fall Studie, die zeigt, dass new Function() als eval() kategorisch schneller ist. I.e. Sie sollten niemals (selten?) eval anstelle von new Function() verwenden.

http://polyfx.com/stuff/bsort.html < - die 1000 Iteration Version, kann Ihr Browser abstürzen

http://polyfx.com/stuff/bsort10.html < - die kürzere Version

Eval im Durchschnitt fast 8mal langsamer als new Function().

+5

Was genau ist eine "schlechtere Praxis" bei der Auswertung von Code-Strings über 'eval' im Vergleich zum' Function'-Konstruktor? – kangax

+2

Es läuft schneller und es ist einfacher zu verstehen: http://www.go4expert.com/forums/showthread.php?t=13979 Auch ist es einfacher, 'f()' in einem bestimmten Umfang oder Schließung, wenn nötig (aus Sicherheitsgründen). –

+2

Bearbeitete meine Antwort mit einer Fallstudie, die ich gerade schrieb. Eval ist in manchen Browsern fast 10 mal langsamer. –

2

Dies ist ein separater Fall von meiner anderen Antwort.

Ich habe den Function-Konstruktor eine Weile verwendet, um benutzerdefinierte String-Formatierer zu erstellen, die wiederholt aufgerufen wurden. Der Overhead beim Erstellen der Funktion (was ich als Leistungsproblem empfinde) wurde durch die verbesserte Leistung der benutzerdefinierten Funktionen, die zur Laufzeit speziell für die Verarbeitung einer bestimmten Formatzeichenfolge erstellt wurden, bei weitem übertroffen brauchte nicht Tonnen irrelevanter Fälle — auszuwerten oder eine Formatzeichenkette zu analysieren, für diese Angelegenheit. Es ist ein bisschen so, als würde man einen regulären Ausdruck erstellen, nehme ich an.

6

John Resig verwendete den Konstruktor Function, um "kompilierte" Versionen von clientseitigen Vorlagen zu erstellen, die in einer ASP-Syntax geschrieben waren. http://ejohn.org/blog/javascript-micro-templating/

+0

Die Implementierung von John Resig inspirierte die Micro-Templating-Sprache des Unterstrichs, die den gleichen Ansatz verwendet (dh den 'Function'-Konstruktor): http://underscorejs.org/docs/underscore.html#section-143 – turtlemonvh

18

NWMatcher - Javascript CSS-Selektor und Matcher, von Diego Perini - verwendet Function Konstruktor (1, 2, 3, 4 usw.) ("kompiliert") hocheffiziente Versionen des Wählers Matcher zu erstellen.

Die benchmark spricht (was ich gerade lief auf Chrome 5) für mich:

alt text

Beachten Sie den Unterschied zwischen NWMatcher und Sizzle, die eine sehr ähnliche Selektor-Engine ist, nur ohne Funktion Kompilation :)

Nebenbei bemerkt, ECMAScript 5 wirft keine Fehler bei Aufruf von . Weder in strengen noch in "Standard" -Modi.Strict-Modus führt jedoch einige Einschränkungen auf das Vorhandensein von Kennungen wie „eval“ und „Argumente“:

  • Sie können keine Variablen/Funktionen/Argumente mit solchen Namen erklären haben:

    function eval() { } 
    var eval = { }; 
    function f(eval) { } 
    var o = { set f(eval){ } }; 
    
  • Sie können auf diese Kennung nicht zugeordnet werden:

    eval = { }; 
    

auch im Strict-Modus beachten Sie, dass eval Semantik ist etwas anders als in ES3. Strict-Modus-Code können keine Variablen instanziiert oder Funktionen in der Umgebung, von der es hieß:

eval(' "use strict"; var x = 1; '); 
typeof x; // "undefined" 
+0

Ext.js 'Selektor-Engine (http://code.google.com/p/extjs-public/source/browse/trunk/release/source/core/DomQuery.js) hat die Funktionszusammenstellung auch für die Ewigkeit benutzt (siehe 'kompilieren') –

+2

Ich habe aktualisiert die Quellcode-Referenzen. Aber der Benchmark ist zu einer toten Verbindung geworden. – galambalazs

+0

[Es sei denn, ich vermisse etwas] (http: // mathiasbynens.be/notes/javascript-properties), erlaubt ES5 strict mode sowohl 'var o = {eval: 1}' als auch 'myObject.eval = 1;'. Ihre anderen Beispiele sehen Sie, obwohl. Hat sich die Spezifikation geändert, seit Sie diese Antwort geschrieben haben? –

2

Die einzige legitime Anwendung ich für sie gekommen sind, ist, wenn ich dies schrieb:

Function.prototype.New = (function() { 
    var fs = []; 
    return function() { 
    var f = fs [arguments.length]; 
    if (f) { 
     return f.apply (this, arguments); 
    } 
    var argStrs = []; 
    for (var i = 0; i < arguments.length; ++i) { 
     argStrs.push ("a[" + i + "]"); 
    } 
    f = new Function ("var a=arguments;return new this(" + argStrs.join() + ");"); 
    if (arguments.length < 100) { 
     fs [arguments.length] = f; 
    } 
    return f.apply (this, arguments); 
    }; 
})(); 

Die Code ermöglicht Ihnen, Function.prototype.apply zu verwenden, während Sie das Schlüsselwort new verwenden.

Beispiel:

function Foo (x, y, z) { 
    this.x = x; 
    this.y = y; 
    this.z = z; 
    this.otherArgs = Array.prototype.slice.call (arguments, 3); 
} 

var foo = Function.prototype.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]); 
// /*equiv*/ var foo = Foo.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]); 
// /*equiv*/ var foo = Foo.New (1, 2, 3, 4, 5, 6, 7); 
var bool = true 
    && foo.x == 1 
    && foo.y == 2 
    && foo.z == 3 
    && foo.otherArgs.length == 4 
    && foo.otherArgs [0] == 4 
    && foo.otherArgs [1] == 5 
    && foo.otherArgs [2] == 6 
    && foo.otherArgs [3] == 7 
    ; 

alert (bool); 
+0

Dieser Anwendungsfall kann nun mit "Object.create" in ES5-kompatiblen Browsern erreicht werden: 'var newObj = Object.create (MyClass.prototype); var ret = MyClass.apply (newObj, KonstruktorArgs); if (ret && typeof ret === "Objekt") {newObj = ret; } ' – kpozin

1

Sie könnten eine Reihe von Code mehr als einmal ausgeführt werden sollen. Wenn Sie den Konstruktor Function verwenden, müssen Sie ihn nur einmal kompilieren.

Sie könnten Argumente an den Code übergeben, zum Beispiel, wenn Sie ein Ereignis polieren, können Sie das Ereignisattribut abrufen und eine Funktion erstellen, die ein Ereignisargument erwartet.

Sie können die beiden kombinieren und an einem Ort kompilieren und an einem anderen Ort ausführen und dennoch Argumente in den Variablen übergeben, die der Code erwartet.

Verwandte Themen