Zastanawiałem się, czy istnieje różnica w wydajności między używaniem funkcji nazwanych a funkcjami anonimowymi w 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;
}
Pierwsza jest bardziej uporządkowana, ponieważ nie zaśmieca twojego kodu rzadko używanymi funkcjami, ale czy ma znaczenie, że deklarujesz tę funkcję wiele razy?
Odpowiedzi:
Problem z wydajnością jest tutaj kosztem tworzenia nowego obiektu funkcji przy każdej iteracji pętli, a nie faktem, że używasz funkcji anonimowej:
for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = function() { // do something }; }
Tworzysz tysiąc różnych obiektów funkcji, mimo że mają ten sam kod i nie mają powiązania z zakresem leksykalnym ( zamknięcie ). Z drugiej strony, poniższe wydaje się szybsze, ponieważ po prostu przypisuje to samo odwołanie do funkcji do elementów tablicy w całej pętli:
function myEventHandler() { // do something } for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = myEventHandler; }
Jeśli miałbyś utworzyć anonimową funkcję przed wejściem do pętli, a następnie przypisać do niej odwołania do elementów tablicy tylko wewnątrz pętli, zauważysz, że nie ma żadnej różnicy wydajności lub semantycznej w porównaniu z wersją funkcji o nazwie:
var handler = function() { // do something }; for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = handler; }
Krótko mówiąc, korzystanie z funkcji anonimowych zamiast nazwanych nie wiąże się z żadnymi zauważalnymi kosztami wydajności.
Nawiasem mówiąc, z góry może się wydawać, że nie ma różnicy między:
function myEventHandler() { /* ... */ }
i:
var myEventHandler = function() { /* ... */ }
Pierwsza jest deklaracją funkcji, podczas gdy druga jest przypisaniem zmiennej do funkcji anonimowej. Chociaż może się wydawać, że mają ten sam efekt, JavaScript traktuje je nieco inaczej. Aby zrozumieć różnicę, polecam przeczytanie „ Niejednoznaczności deklaracji funkcji JavaScript ”.
Rzeczywisty czas wykonania dowolnego podejścia będzie w dużej mierze podyktowany implementacją kompilatora i środowiska wykonawczego w przeglądarce. Aby uzyskać pełne porównanie wydajności współczesnych przeglądarek, odwiedź witrynę JS Perf
źródło
node.js
aplikacji internetowych lepiej jest tworzyć funkcje poza przepływem żądań i przekazywać je jako wywołania zwrotne, niż tworzyć anonimowe wywołania zwrotne?Oto mój kod testowy:
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)); }
Wyniki:
Test 1: 142 ms Test 2: 1983 ms
Wygląda na to, że silnik JS nie rozpoznaje, że jest to ta sama funkcja w Test2 i kompiluje ją za każdym razem.
źródło
Zgodnie z ogólną zasadą projektowania, należy unikać wielokrotnego implementowania tego samego kodu. Zamiast tego powinieneś podnieść wspólny kod do funkcji i wykonać tę (ogólną, dobrze przetestowaną, łatwą do modyfikacji) funkcję z wielu miejsc.
Jeśli (w przeciwieństwie do tego, co wnioskujesz ze swojego pytania) raz deklarujesz funkcję wewnętrzną i raz używasz tego kodu (i nie masz nic identycznego w swoim programie), to prawdopodobnie funkcja anonomiczna (to jest przypuszczenie) zostanie potraktowana w ten sam sposób przez kompilator jako normalną nazwaną funkcję.
Jest to bardzo przydatna funkcja w określonych przypadkach, ale nie powinna być używana w wielu sytuacjach.
źródło
Nie spodziewałbym się dużej różnicy, ale jeśli istnieje, prawdopodobnie będzie się różnić w zależności od silnika skryptowego lub przeglądarki.
Jeśli okaże się, że kod jest łatwiejszy do zrozumienia, wydajność nie stanowi problemu, chyba że spodziewasz się wywoływać funkcję miliony razy.
źródło
Tam, gdzie możemy mieć wpływ na wydajność, jest operacja deklarowania funkcji. Oto wzorzec deklarowania funkcji w kontekście innej funkcji lub na zewnątrz:
http://jsperf.com/function-context-benchmark
W Chrome operacja przebiega szybciej, jeśli zadeklarujemy funkcję na zewnątrz, ale w Firefoksie jest odwrotnie.
W innym przykładzie widzimy, że jeśli funkcja wewnętrzna nie jest czystą funkcją, będzie miała brak wydajności również w przeglądarce Firefox: http://jsperf.com/function-context-benchmark-3
źródło
To, co zdecydowanie przyspieszy twoją pętlę w różnych przeglądarkach, zwłaszcza przeglądarkach IE, zapętla się w następujący sposób:
for (var i = 0, iLength = imgs.length; i < iLength; i++) { // do something }
Podałeś dowolne 1000 w warunku pętli, ale otrzymujesz mój dryf, jeśli chcesz przejrzeć wszystkie elementy w tablicy.
źródło
odniesienie prawie zawsze będzie wolniejsze niż rzecz, do której się odnosi. Pomyśl o tym w ten sposób - powiedzmy, że chcesz wydrukować wynik dodania 1 + 1. Co ma większy sens:
alert(1 + 1);
lub
a = 1; b = 1; alert(a + b);
Zdaję sobie sprawę, że to naprawdę uproszczony sposób patrzenia na to, ale jest to przykładowe, prawda? Używaj odwołania tylko wtedy, gdy będzie używane wiele razy - na przykład, który z tych przykładów ma większy sens:
$(a.button1).click(function(){alert('you clicked ' + this);}); $(a.button2).click(function(){alert('you clicked ' + this);});
lub
function buttonClickHandler(){alert('you clicked ' + this);} $(a.button1).click(buttonClickHandler); $(a.button2).click(buttonClickHandler);
Druga to lepsza praktyka, nawet jeśli ma więcej linii. Mam nadzieję, że to wszystko jest pomocne. (a składnia jquery nikogo nie zrzuciła)
źródło
@nickf
(szkoda, że nie mam przedstawiciela do komentowania, ale dopiero co znalazłem tę witrynę)
Chodzi mi o to, że istnieje zamieszanie między nazwanymi / anonimowymi funkcjami a przypadkiem użycia wykonywania + kompilacji w iteracji. Jak zilustrowałem, różnica między anon + named jest sama w sobie pomijalna - mówię, że to przypadek użycia jest wadliwy.
Wydaje mi się to oczywiste, ale jeśli nie, myślę, że najlepszą radą jest „nie rób głupich rzeczy” (jednym z nich jest ciągłe przesuwanie bloków + tworzenie obiektów w tym przypadku użycia), a jeśli nie jesteś pewien, przetestuj!
źródło
TAK! Funkcje anonimowe są szybsze niż zwykłe funkcje. Być może, jeśli szybkość jest najważniejsza ... ważniejsza niż ponowne wykorzystanie kodu, rozważ użycie funkcji anonimowych.
Jest tutaj naprawdę dobry artykuł o optymalizacji javascript i funkcji anonimowych:
http://dev.opera.com/articles/view/efficient-javascript/?page=2
źródło
Obiekty anonimowe są szybsze niż obiekty nazwane. Jednak wywoływanie większej liczby funkcji jest droższe i w stopniu, który przyćmiewa wszelkie oszczędności, jakie można uzyskać dzięki korzystaniu z funkcji anonimowych. Każda wywoływana funkcja dodaje do stosu wywołań, co wprowadza niewielką, ale nietrywialną ilość narzutu.
Ale jeśli nie piszesz procedur szyfrowania / deszyfrowania lub czegoś podobnie wrażliwego na wydajność, jak zauważyło wielu innych, zawsze lepiej jest zoptymalizować pod kątem eleganckiego, łatwego do odczytania kodu niż szybki kod.
Zakładając, że piszesz dobrze zaprojektowany kod, za kwestie szybkości powinny odpowiadać osoby piszące interpretery / kompilatory.
źródło
@nickf
To raczej głupi test, porównujesz tam czas wykonania i kompilacji, co oczywiście będzie kosztować metodę 1 (kompiluje N razy, w zależności od silnika JS) z metodą 2 (kompiluje się raz). Nie wyobrażam sobie programisty JS, który zdałby swój okres próbny, pisząc kod w taki sposób.
O wiele bardziej realistycznym podejściem jest przypisanie anonimowe, ponieważ w rzeczywistości używasz do dokumentu. Metoda onclick jest bardziej podobna do poniższej, która w rzeczywistości łagodnie faworyzuje metodę anon.
Używając podobnej struktury testowej do twojej:
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"); }
źródło
Jak wskazano w komentarzach do @nickf odpowiedź: odpowiedź na
jest po prostu tak. Ale jak pokazuje jego perf w JS, nie jest wolniejszy nawet milion razy, co pokazuje, że z czasem staje się coraz szybszy.
Bardziej interesujące dla mnie pytanie brzmi:
Jeśli funkcja wykonuje złożone obliczenia, czas potrzebny na utworzenie obiektu funkcji jest najprawdopodobniej pomijalny. Ale co z nad głową tworzenia w przypadkach, gdy bieg jest szybki? Na przykład:
// 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); }
Ten JS Perf pokazuje, że jednokrotne utworzenie funkcji jest szybsze zgodnie z oczekiwaniami. Jednak nawet przy bardzo szybkiej operacji, takiej jak proste dodawanie, narzut związany z wielokrotnym tworzeniem funkcji wynosi tylko kilka procent.
Różnica prawdopodobnie stanie się znacząca tylko w przypadkach, gdy tworzenie obiektu funkcji jest złożone, przy zachowaniu znikomego czasu wykonywania, np. Jeśli cała treść funkcji jest opakowana w plik
if (unlikelyCondition) { ... }
.źródło