Błąd JSlint ' nie tworzy funkcji w pętli."prowadzi do pytania o sam Javascript

Mam jakiś kod, który wywołuje funkcje anonimowe w pętli, coś takiego jak ten pseudo przykład:

for (i = 0; i < numCards; i = i + 1) {
    card = $('<div>').bind('isPopulated', function (ev) {
        var card = $(ev.currentTarget);
        ....

JSLint zgłasza błąd " nie rób funkcji w pętli."Lubię utrzymywać mój kod JSLint w czystości. Wiem, że mogę przenieść funkcję anonimową z pętli i wywołać ją jako nazwaną funkcję. To na marginesie, oto moje pytanie:

Czy interpreter Javascript naprawdę stworzy instancję funkcji podczas iteracji? Czy naprawdę jest tylko jeden instancja funkcji "skompilowana" i ten sam kod jest wykonywany wielokrotnie? Oznacza to, że czy jslint "sugestia", aby przenieść funkcję z pętli, faktycznie wpływa na wydajność kodu?

Author: Josh Kelley, 2010-10-13

4 answers

Czy interpreter Javascript naprawdę stworzy instancję funkcji podczas iteracji?

Musi, ponieważ nie wie, czy obiekt funkcji zostanie zmodyfikowany gdzie indziej. Pamiętaj, że funkcje są standardowymi obiektami JavaScript, więc mogą mieć właściwości jak każdy inny obiekt. Kiedy to robisz:

card = $('<div>').bind('isPopulated', function (ev) { ... })

Z tego co wiesz, bind może zmodyfikować obiekt, na przykład:

function bind(str, fn) {
  fn.foo = str;
}

Oczywiście spowodowałoby to niewłaściwe zachowanie, gdyby obiekt funkcji został udostępniony we wszystkich iteracjach.

 24
Author: casablanca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-13 19:25:03

Częściowo zależy to od tego, czy używasz wyrażenia funkcji czy deklaracji funkcji . Są to różne rzeczy, dzieją się w różnym czasie i mają inny wpływ na otaczający zakres. Zacznijmy więc od rozróżnienia.

A function expression jest produkcją function, w której używasz wyniku jako wartości prawej ręki - np. przypisujesz wynik do zmiennej lub Właściwości lub przekazujesz go do funkcji jako parametr itp. Są to wszystkie wyrażenia funkcji :

setTimeout(function() { ... }, 1000);

var f = function() {  ... };

var named = function bar() { ... };

(nie używaj tego ostatniego-który nazywa się nazwanym wyrażeniem funkcji - implementacje mają błędy, szczególnie IE.)

Natomiast jest to deklaracja funkcji :

function bar() { ... }

Jest samodzielna, nie używasz wyniku jako wartości prawej ręki.

Dwie główne różnice między nimi:

  1. Wyrażenia funkcyjne są oceniane, gdzie są spotykane w przepływie programu. Deklaracje są oceniane, gdy kontrola wchodzi w zakres zawierający (np. funkcja zawierająca lub zakres globalny).

  2. Nazwa funkcji (jeśli taką posiada) jest zdefiniowana w deklaracji zawierającej zakres funkcji . Nie jest to funkcja wyrażenie (z wyjątkiem błędów przeglądarki).

Twoje anonimowe funkcje są wyrażeniami funkcyjnymi , a więc o ile interpreter nie zrobi optymalizacja (co jest bezpłatne), zostaną odtworzone na każdej pętli. Więc twoje użycie jest w porządku, jeśli uważasz, że implementacje zoptymalizują, ale podzielenie go na nazwaną funkcję ma inne korzyści i-co ważne-nic cię nie kosztuje. Zobacz także casablanca ' s answer , aby dowiedzieć się, dlaczego interpreter może nie być w stanie zoptymalizować odtwarzanie funkcji na każdej iteracji, w zależności od tego, jak głęboko sprawdza kod.

Większym problemem byłoby, gdyby użyłeś funkcji deklaracji W pętli, ciała warunkowego itp.:

function foo() {
    for (i = 0; i < limit; ++i) {
        function bar() { ... } // <== Don't do this
        bar();
    }
}
Technicznie rzecz biorąc, dokładne zapoznanie się z gramatyką spec pokazuje, że jest to nieprawidłowe, chociaż praktycznie żadna implementacja tego nie wymusza. To, co robią implementacje jest zróżnicowane i najlepiej trzymać się od tego z daleka.

Za moje pieniądze, najlepiej będzie użyć jednej deklaracji funkcji, takiej jak ta:

function foo() {
    for (i = 0; i < limit; ++i) {
        bar();
    }

    function bar() {
        /* ...do something, possibly using 'i'... */
    }
}

Otrzymujesz ten sam wynik, nie ma możliwości że implementacja utworzy nową funkcję na każdej pętli, otrzymujesz korzyść funkcji o nazwie i nic nie tracisz.

 42
Author: T.J. Crowder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:15

Interpreter może faktycznie utworzyć nowy obiekt funkcji z każdą iteracją, choćby dlatego, że funkcja ta może być zamknięciem, które musi przechwycić bieżącą wartość dowolnej zmiennej w jej zewnętrznym zakresie.

Dlatego JSLint chce cię odstraszyć od tworzenia wielu anonimowych funkcji w ciasnej pętli.

 2
Author: Frédéric Hamidi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-13 19:03:08

Boo to JSLint. To jak tępy instrument na głowie. Po każdym napotkaniu function tworzony jest nowy obiekt funkcji (jest to Instrukcja/wyrażenie, nie deklaracja -- edit: to jest białe kłamstwo. Zobacz odpowiedzi T. J. Crowdersa ). Zwykle odbywa się to w pętli do zamknięcia, itp. Większym problemem jest tworzenie fałszywych zamknięć .

Na przykład:

for (var i = 0; i < 10; i++) {
  setTimeout(function () {
    alert(i)
  }, 10)
}

Spowoduje "dziwne" zachowanie. To nie jest problem z " tworzeniem funkcji w pętli tak bardzo, jak nie zrozumienie zasad, których używa JS dla zakresów zmiennych i zamknięć (zmienne nie są związane w zamknięciach, zakresy -- konteksty wykonania -- są).

Możesz jednak chcieć utworzyć zamknięcie w funkcji. Rozważ ten mniej zaskakujący kod:

for (var i = 0; i < 10; i++) {
  setTimeout((function (_i) { 
    return function () {
      alert(_i)
    }
  })(i), 10)
}
O nie! Nadal stworzyłem funkcję!
 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-13 22:10:52