it-swarm-eu.dev

Má použití anonymních funkcí vliv na výkon?

Zajímalo by mě, existuje rozdíl ve výkonu mezi používanými funkcemi a anonymními funkcemi ve Javascriptu? 

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;
}

První z nich je čistší, protože neřeší váš kód se zřídka používanými funkcemi, ale je důležité, že tuto funkci opakovaně deklarujete několikrát?

80
nickf

Problémem výkonu jsou náklady na vytvoření nového objektu funkce při každé iteraci smyčky a ne skutečnost, že používáte anonymní funkci:

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

Vytváříte tisíc různých objektů funkcí, přestože mají stejné tělo kódu a žádné vazby na lexikální rozsah ( uzavření ). Na druhou stranu se zdá rychlejší, protože jednoduše přiřazuje odkaz funkce stejný na prvky pole v rámci smyčky:

function myEventHandler() {
    // do something
}

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

Pokud jste před vstupem do smyčky vytvořili anonymní funkci, přiřaďte do ní pouze odkazy na prvky pole, zatímco ve smyčce zjistíte, že neexistuje žádný výkon ani sémantický rozdíl ve srovnání s verzí pojmenované funkce:

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

Stručně řečeno, neexistuje žádná pozorovatelná cena za použití anonymních funkcí.

Odchylně se může zdát, že neexistuje rozdíl mezi:

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

a:

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

První z nich je deklarace function, zatímco druhá je přiřazení proměnné k anonymní funkci. I když se zdá, že mají stejný účinek, JavaScript s nimi zachází poněkud jinak. Pro pochopení tohoto rozdílu doporučuji číst „ JavaScript dvojznačnost deklarace funkcí “.

Skutečný čas provedení jakéhokoli přístupu bude do značné míry diktován implementací kompilátoru a runtime prohlížeče. Pro kompletní srovnání moderního výkonu prohlížeče navštivte stránku JS Perf site

81
Atif Aziz

Zde je můj testovací kód:

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

Výsledky:
Test 1: 142ms Test 2: 1983ms

Zdá se, že JS engine nerozpoznává, že je to stejná funkce v Test2 a pokaždé ji kompiluje.

21
nickf

Jako obecný princip designu byste se měli vyhnout napodobování stejného kódu vícekrát. Místo toho byste měli zrušit společný kód do funkce a provést tuto (obecnou, dobře testovanou, snadno upravitelnou) funkci z více míst.

Pokud (na rozdíl od toho, co vyvodíte z vaší otázky) deklarujete interní funkci jednou a jednou použijete tento kód (a ve vašem programu nemáte nic jiného), pak se s anonomní funkcí pravděpodobně (to je hádání lidí) zachází s překladačem jako normální pojmenovaná funkce.

Je to velmi užitečná funkce v konkrétních případech, ale neměla by být použita v mnoha situacích.

2
Tom Leys

Tam, kde můžeme mít vliv na výkonnost, je fungování deklaračních funkcí. Zde je měřítko deklarování funkcí v kontextu jiné funkce nebo mimo ni:

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

V prohlížeči Chrome je operace rychlejší, pokud tuto funkci deklarujeme venku, ale ve Firefoxu je to naopak.

V jiném příkladu vidíme, že pokud vnitřní funkce není čistá funkce, bude mít nedostatek výkonu i ve Firefoxu: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

Nečekal bych velký rozdíl, ale pokud existuje, bude to pravděpodobně lišit skriptovacím motorem nebo prohlížečem. 

Pokud se vám zdá, že kód je jednodušší grok, výkon je problém, pokud neočekáváte volání funkce miliónykrát.

1
Joe Skora

Anonymní objekty jsou rychlejší než pojmenované objekty. Ale volání více funkcí je dražší, a to do té míry, že zatemní veškeré úspory, které můžete získat z používání anonymních funkcí. Každá funkce nazývá přidává do zásobníku volání, který zavádí malé, ale netriviální množství režie.

Ale pokud píšete rutiny šifrování/dešifrování nebo něco podobně citlivého na výkon, jak mnozí jiní uvedli, že je vždy lepší optimalizovat pro elegantní, snadno čitelný kód přes rychlý kód.

Za předpokladu, že píšete dobře vytvořený kód, pak otázky rychlosti by měly být odpovědností těch, kteří psali tlumočníky/kompilátory.

1
pcorcoran

@nickf

To je ale poněkud vyčerpávající test, když porovnáváte výkon a kompilaci čas tam, který je zřejmě nákladovou metodou 1 (kompiluje N krát, motor závisí na JS) s metodou 2 (kompilace jednou). Nedokážu si představit, že by vývojář JS takovým způsobem předal svůj zkušební kód.

Mnohem realističtější přístup je anonymní zadání, protože ve skutečnosti používáte metodu document.onclick spíše jako následující, což ve skutečnosti mírně zvýhodňuje metodu anon.

Použití podobného testovacího rámce jako Vy:


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

(přeji si, aby měl zástupce jen komentář, ale právě jsem našel tyto stránky)

Mým bodem je, že zde existuje zmatek mezi jmenovanými/anonymními funkcemi a případem použití + kompilace v iteraci. Jak jsem ilustroval, rozdíl mezi anon + pojmenovaným je sám o sobě zanedbatelný - říkám, že je to případ použití, který je chybný.

Zdá se mi to jasné, ale pokud ne, myslím si, že nejlepší radou je "nedělej hloupé věci" (z čehož je konstantní posun bloku + vytvoření objektu tohoto případu použití) a pokud si nejste jisti, otestujte!

0
annakata

ANO! Anonymní funkce jsou rychlejší než běžné funkce. Možná, že pokud je rychlost nanejvýš důležitá ... důležitější než opakované použití kódu, pak zvažte použití anonymních funkcí.

Tam je opravdu dobrý článek o optimalizaci javascript a anonymní funkce zde:

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

0

odkaz je téměř vždy pomalejší než věc, na kterou odkazuje. Přemýšlejte o tom tímto způsobem - řekněme, že chcete tisknout výsledek přidání 1 + 1. Co dává větší smysl:

alert(1 + 1);

nebo

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

Uvědomuji si, že je to opravdu zjednodušující způsob, jak se na to podívat, ale je to ilustrativní, že? Použijte odkaz pouze v případě, že se bude používat vícekrát - například, který z těchto příkladů dává větší smysl:

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

nebo

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

Druhá je lepší praxe, i když má více řádků. Doufejme, že toto všechno je užitečné. (a syntaxe jquery nehodila nikoho mimo)

0
matt lohkamp

Jak bylo uvedeno v komentářích k odpovědi @nickf: Odpověď na 

Vytváření funkce je rychlejší než vytvoření milionkrát

je prostě ano. Ale jak ukazuje jeho výkon JS, není to pomalejší faktor, který ukazuje, že se v průběhu času ve skutečnosti dostane rychleji.

Zajímavější otázkou je:

Jak se opakované spuštění create + porovná s vytvořením jednou + opakování spuštění.

Pokud funkce provádí komplexní výpočet, je čas vytvoření objektu funkce s největší pravděpodobností zanedbatelný. Ale co přes hlavu vytvořit v případech, kdy spustit je rychlý? Například:

// 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);
}

Tento JS Perf ukazuje, že vytvoření funkce jen jednou je rychlejší, než se očekávalo. Nicméně i při velmi rychlém provozu, jako je jednoduchý doplněk, je režie opakovaného vytváření funkce jen několik procent.

Rozdíl se pravděpodobně stane významný pouze v případech, kdy je vytváření funkčního objektu složité, přičemž se zachová zanedbatelná doba běhu, např. Pokud je celé funkční tělo zabaleno do if (unlikelyCondition) { ... }.

0
bluenote10

Co určitě zrychlí vaši smyčku v různých prohlížečích, zejména v prohlížečích IE, se opakuje takto:

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

Do stavu smyčky jste vložili libovolný počet 1000, ale pokud jste chtěli projít všechny položky v poli, dostanete svůj drift.

0
Sarhanis