it-swarm-eu.dev

Beeinflusst die Verwendung anonymer Funktionen die Leistung?

Ich habe mich gefragt, gibt es einen Leistungsunterschied zwischen benannten Funktionen und anonymen Funktionen in Javascript? 

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

vs

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Die erste ist sauberer, da der Code nicht mit selten verwendeten Funktionen verstopft wird. Ist es jedoch wichtig, dass Sie diese Funktion mehrmals neu deklarieren?

80
nickf

Das Leistungsproblem ist hier der Aufwand für die Erstellung eines neuen Funktionsobjekts bei jeder Iteration der Schleife und nicht die Tatsache, dass Sie eine anonyme Funktion verwenden:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

Sie erstellen tausend verschiedene Funktionsobjekte, obwohl sie denselben Code und keine Bindung an den lexikalischen Geltungsbereich haben ( closed ). Das folgende scheint jedoch schneller zu sein, da es den Array-Elementen in der gesamten Schleife einfach die Funktionsreferenz same zuweist:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Wenn Sie die anonyme Funktion vor dem Eintritt in die Schleife erstellen und dann den Array-Elementen nur innerhalb der Schleife Verweise zuweisen, werden Sie feststellen, dass es im Vergleich zur genannten Funktionsversion keinerlei Performance oder semantische Unterschiede gibt:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

Kurz gesagt, es gibt keine beobachtbaren Leistungskosten für die Verwendung von anonymen Funktionen über benannte Funktionen.

Abgesehen davon kann es von oben scheinen, dass es keinen Unterschied gibt zwischen:

function myEventHandler() { /* ... */ }

und:

var myEventHandler = function() { /* ... */ }

Ersteres ist eine Funktionsdeklaration, während letztere eine variable Zuordnung zu einer anonymen Funktion ist. Obwohl sie den gleichen Effekt zu haben scheinen, behandelt JavaScript sie etwas anders. Um den Unterschied zu verstehen, empfehle ich zu lesen: " JavaScript-Funktionsdeklaration Mehrdeutigkeit ".

Die tatsächliche Ausführungszeit für jeden Ansatz wird weitgehend von der Implementierung des Compilers und der Laufzeit durch den Browser bestimmt. Einen vollständigen Vergleich der modernen Browserleistung finden Sie auf der JS Perf-Website

81
Atif Aziz

Hier ist mein Testcode:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

Die Ergebnisse:
Test 1: 142 ms Test 2: 1983ms

Es scheint, dass die JS-Engine nicht erkennt, dass sie in Test2 dieselbe Funktion hat, und sie jedes Mal kompiliert.

21
nickf

Als generelles Konstruktionsprinzip sollten Sie vermeiden, denselben Code mehrmals zu implementieren. Stattdessen sollten Sie allgemeinen Code in eine Funktion aufnehmen und diese (allgemeine, gut getestete, leicht zu modifizierende) Funktion an mehreren Stellen ausführen.

Wenn Sie (im Gegensatz zu dem, was Sie aus Ihrer Frage schließen, die interne Funktion einmal deklariert und diesen Code einmal verwendet (und nichts anderes in Ihrem Programm identisch ist), dann wird eine anonyme Funktion wahrscheinlich (das ist eine Vermutung, Leute) behandelt vom Compiler genauso wie eine normale benannte Funktion.

Dies ist in bestimmten Fällen eine sehr nützliche Funktion, sollte jedoch in vielen Situationen nicht verwendet werden.

2
Tom Leys

Wo wir einen Einfluss auf die Leistung haben können, ist der Betrieb von deklarierenden Funktionen. Hier ist ein Benchmark für das Deklarieren von Funktionen im Kontext einer anderen Funktion oder außerhalb:

http://jsperf.com/function-context-benchmark

In Chrome ist der Vorgang schneller, wenn wir die Funktion außerhalb deklarieren. In Firefox ist es umgekehrt.

In einem anderen Beispiel sehen wir, dass, wenn die innere Funktion keine reine Funktion ist, auch in Firefox die Leistung darunter leidet: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

Ich würde nicht viel Unterschied erwarten, aber wenn es einen gibt, wird es sich wahrscheinlich nach Skript-Engine oder Browser unterscheiden. 

Wenn Sie feststellen, dass der Code leichter zu grokieren ist, ist Leistung kein Problem, sofern Sie nicht erwarten, dass Sie die Funktion Millionen Mal aufrufen.

1
Joe Skora

Anonyme Objekte sind schneller als benannte Objekte. Das Aufrufen weiterer Funktionen ist jedoch teurer und in einem Maße möglich, in dem die Einsparungen durch die Verwendung anonymer Funktionen in den Schatten gestellt werden. Jede aufgerufene Funktion fügt den Aufrufstapel hinzu, wodurch ein geringer, aber nicht unbedeutender Overhead entsteht.

Wenn Sie jedoch keine Verschlüsselungs-/Entschlüsselungsroutinen oder etwas Ähnliches in Bezug auf die Leistung erstellen, ist es, wie viele andere bereits bemerkt haben, immer besser, die Optimierung von elegantem, leicht lesbarem Code über schnellen Code zu optimieren.

Vorausgesetzt, Sie schreiben einen gut strukturierten Code, sollten Geschwindigkeitsfragen in der Verantwortung derjenigen liegen, die Dolmetscher/Compiler schreiben.

1
pcorcoran

@nickf

Dies ist jedoch ein ziemlich beschämender Test. Sie vergleichen die Ausführungszeit {und die Kompilierung Zeit, die offensichtlich Methode 1 (N-mal kompiliert, je nach JS-Engine) mit Methode 2 (einmal kompiliert) kostet. Ich kann mir keinen JS-Entwickler vorstellen, der auf diese Weise den Code für die Bewährungshilfe übergeben würde.

Ein weitaus realistischerer Ansatz ist die anonyme Zuweisung, da Sie tatsächlich für Ihr document verwenden. Die Methode onclick ähnelt eher der folgenden, die die anon-Methode in gewisser Weise bevorzugt.

Verwenden Sie ein ähnliches Testframework wie Ihr:


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}
0
annakata

@nickf

(Ich wünschte, ich hätte den Vertreter einfach kommentieren können, aber ich habe gerade erst diese Seite gefunden.)

Mein Punkt ist, dass hier Verwirrung zwischen benannten/anonymen Funktionen und dem Anwendungsfall der Ausführung + Kompilierung in einer Iteration besteht. Wie ich illustriert habe, ist der Unterschied zwischen anon + named an sich vernachlässigbar - ich sage, der Anwendungsfall ist fehlerhaft.

Es erscheint mir offensichtlich, aber wenn nicht, denke ich, ist der beste Rat "mache keine dummen Dinge" (von denen die konstante Blockverschiebung + Objekterstellung dieses Anwendungsfalls eins ist). Wenn Sie sich nicht sicher sind, testen Sie!

0
annakata

JA! Anonyme Funktionen sind schneller als normale Funktionen. Wenn Geschwindigkeit von größter Bedeutung ist ... wichtiger als die Wiederverwendung von Code, sollten Sie anonyme Funktionen in Betracht ziehen.

Es gibt hier einen wirklich guten Artikel über die Optimierung von Javascript und anonymen Funktionen:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

0

eine Referenz ist fast immer langsamer als die Sache, auf die sie verweist. Stellen Sie sich das so vor: Nehmen wir an, Sie möchten das Ergebnis der Addition von 1 + 1 drucken. Das macht mehr Sinn:

alert(1 + 1);

oder

a = 1;
b = 1;
alert(a + b);

Mir ist klar, dass dies eine sehr einfache Sichtweise ist, aber es ist illustrativ, richtig? Verwenden Sie eine Referenz nur, wenn sie mehrfach verwendet werden soll. Welches dieser Beispiele ist beispielsweise sinnvoller:

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

oder

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

Die zweite ist eine bessere Übung, auch wenn sie mehr Zeilen hat. Hoffentlich ist das alles hilfreich. (und die Jquery-Syntax hat niemanden abgeworfen)

0
matt lohkamp

Wie in den Kommentaren zu @nickf Antwort darauf hingewiesen: Die Antwort auf 

Erzeugt eine Funktion einmal schneller als eine Million Mal

ist einfach ja. Aber wie seine JS perf zeigt, ist sie nicht um einen Faktor von einer Million langsamer, was zeigt, dass sie mit der Zeit tatsächlich schneller wird.

Die interessantere Frage an mich ist:

Wie wird ein wiederholtes create + run verglichen, um es einmal zu erstellen + wiederholt run.

Wenn eine Funktion eine komplexe Berechnung durchführt, ist die Zeit zum Erstellen des Funktionsobjekts höchstwahrscheinlich vernachlässigbar. Aber was ist mit dem Overhead von create in Fällen, in denen run schnell ist? Zum Beispiel:

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

Diese JS Perf zeigt, dass das einmalige Erstellen der Funktion schneller ist als erwartet. Selbst bei einer sehr schnellen Operation wie einem einfachen Hinzufügen ist der Aufwand für das wiederholte Erstellen der Funktion jedoch nur einige Prozent.

Der Unterschied wird wahrscheinlich nur in Fällen wichtig, in denen das Erstellen des Funktionsobjekts komplex ist, während eine vernachlässigbare Laufzeit beibehalten wird, z. B. wenn der gesamte Funktionskörper in eine if (unlikelyCondition) { ... } eingeschlossen ist.

0
bluenote10

Was die Schleife in einer Vielzahl von Browsern auf jeden Fall schneller macht, insbesondere in IE - Browsern, ist wie folgt:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

Sie haben eine beliebige 1000 in die Loop-Bedingung eingefügt, aber Sie bekommen meinen Drift, wenn Sie alle Elemente im Array durchgehen möchten.

0
Sarhanis