2012-04-18 11 views
56
function foo(a) { 
    if (/*some condition*/) { 
     // perform task 1 
     // perform task 3 
    } 
    else { 
     // perform task 2 
     // perform task 3 
    } 
} 

Mam funkcję, której struktura jest podobna do powyższej. Chcę abstrakcyjne zadanie 3 do funkcji, bar(), ale chcę ograniczyć dostęp tej funkcji tylko do zakresu foo(a).Zdefiniuj funkcję wewnątrz innej funkcji w javascript

Aby osiągnąć to, czego chcę, czy jest to właściwe, aby przejść do następujących?

function foo(a) { 
    function bar() { 
     // perform task 3 
    } 

    if (/*some condition*/) { 
     // perform task 1 
     bar(); 
    } 
    else { 
     // perform task 2 
     bar(); 
    } 
} 

Jeśli powyższe jest prawidłowe, czy bar() się na nowo za każdym razem foo(a) jest wywoływana? (martwiąc się o marnowanie zasobów procesora).

+1

Tak i tak ... – hkf

+1

Sprawdź, czy warto na czas: http://jsperf.com/ Wyobrażam sobie, że to zależy od zadania3. – tomByrer

+1

@tomByer - +1 za sugerowanie narzędzia – tamakisquare

Odpowiedz

86

Tak, to, co tam masz, jest słuszne. Kilka uwag:

  • bar jest tworzony na każdym wywołaniu funkcji, ale:
    • W nowoczesnych przeglądarek jest to bardzo szybki proces. (Niektóre silniki mogą tylko raz skompilować kod , a następnie ponownie użyć tego kodu w innym kontekście, a silnik Google V8 [w Chrome i innych miejscach] robi to w większości przypadków.)
    • W zależności od bar, niektóre silniki mogą określić, że mogą "wstawiać", całkowicie eliminując wywołanie funkcji. V8 to robi i jestem pewien, że to nie jedyny silnik, który to robi. Oczywiście mogą to zrobić tylko wtedy, gdy nie zmienia to zachowania kodu.
  • Wpływ wydajności, o ile występuje, polegający na tym, że bar jest tworzony za każdym razem, będzie bardzo różny w zależności od silników JavaScript. Jeśli bar jest trywialne, będzie się różnić od niewykrywalnych do dość małych. Jeśli nie dzwonisz tysiące razy z rzędu (na przykład z handlerka mousemove), nie martwię się o to. Nawet jeśli tak, martwiłbym się tylko, gdybym zobaczył problem z wolniejszymi silnikami. Here's a test case involving DOM operations, co sugeruje, że istnieje wpływ, ale trywialny (prawdopodobnie wypłukany przez rzeczy DOM). Here's a test case doing pure computation, który pokazuje o wiele większy wpływ, ale, szczerze mówiąc, rozmawiamy o różnicy między micro sekund, ponieważ nawet 92% wzrost na czymś, co zajmuje micro sekund, by nastąpić, jest nadal bardzo, bardzo szybki. Aż do momentu, w którym nie zobaczysz wpływu w realnym świecie, nie ma się czym martwić.
  • bar będzie dostępny tylko z poziomu funkcji i będzie miał dostęp do wszystkich zmiennych i argumentów związanych z tym wywołaniem funkcji. To sprawia, że ​​jest to bardzo przydatny wzór.
  • Zauważ, że ponieważ użyłeś deklarację funkcja , to nie ma znaczenia, gdzie można umieścić deklarację (góra, dół, lub w środku   — tak długo, jak to jest na najwyższym poziomie funkcji, nie Wewnątrz instrukcja sterowania przepływem, która jest błędem składni), zostaje zdefiniowana przed uruchomieniem pierwszej linii kodu krokowego.
+0

Thx za odpowiedź. Więc mówisz, że to nieistotne uderzenie wydajności? (biorąc pod uwagę, że kopia 'bar' jest tworzona przy każdym wywołaniu' foo') – tamakisquare

+2

@ahmoo: Przy wydajności JavaScript odpowiedź jest prawie zawsze: To zależy. :-) To zależy od tego, jaki silnik będzie go obsługiwał i jak często będziesz nazywać 'foo'. Jeśli nie wywołujesz 'foo' tysiące razy z rzędu (na przykład, nie w handler' mousemove'), to nie martwiłbym się wcale. Zauważ, że niektóre silniki (na przykład V8) i tak wbudują kod, całkowicie eliminując wywołanie funkcji, pod warunkiem, że nie zmienia to, co dzieje się w sposób, który można wykryć na zewnątrz. –

+0

Dzięki. Masz kilka świetnych punktów. – tamakisquare

2

Tak, to działa poprawnie.

Funkcja wewnętrzna nie jest odtwarzana za każdym razem, gdy wchodzisz do funkcji zewnętrznej, ale jest ona ponownie przypisywana.

Jeśli przetestować ten kod:

function test() { 

    function demo() { alert('1'); } 

    demo(); 
    demo = function() { alert('2'); }; 
    demo(); 

} 

test(); 
test(); 

pokaże 1, 2, 1, 2, nie 1, 2, 2, 2.

+0

Dzięki za odpowiedź. Czy ponowne przypisanie 'demo()' za każdym razem 'test()' nazywa się problemem wydajności? Czy to zależy od złożoności 'demo()'? – tamakisquare

+1

Zrobiłem test wydajnościowy: http: // jsperf.com/inner-function-vs-global-function Podsumowując, generalnie nie jest to problem związany z wydajnością (ponieważ każdy kod, który umieścisz w funkcjach, będzie trwał dłużej niż sama funkcja), ale jeśli potrzebujesz ta dodatkowa wydajność wymaga napisania innego kodu dla różnych przeglądarek. – Guffa

+0

Thx za poświęcenie czasu na stworzenie testu, a także dzielenie się swoimi punktami na temat wydajności. Bardzo doceniane. – tamakisquare

7
var foo = (function() { 
    var bar = function() { 
     // perform task 3 
    } 
    return function (a) { 

     if (/*some condition*/) { 
      // perform task 1 
      bar(); 
     } 
     else { 
      // perform task 2 
      bar(); 
     } 
    }; 
}()); 

Zamknięcie utrzymuje zakres bar() zawarte, wracając nową funkcję z samodzielnego wykonywania anonimowej funkcji ustawia bardziej widoczny zakres do foo(). Anonimowa funkcja self-execing jest uruchamiana dokładnie raz, więc istnieje tylko jedna instancja bar(), a każda jej realizacja z użyciem foo().

+0

Interesujące. Muszę wtedy sprawdzić zamknięcie. Dzięki. – tamakisquare

9

Po to są zamknięcia.

var foo = (function() { 
    function bar() { 
    // perform task 3 
    }; 

    function innerfoo (a) { 
    if (/* some cond */) { 
     // perform task 1 
     bar(); 
    } 
    else { 
     // perform task 2 
     bar(); 
    } 
    } 
    return innerfoo; 
})(); 

Innerfoo (zamknięcie) posiada odniesienie do baru i tylko odniesienie do innerfoo jest zwracany z anonimowej funkcji, która jest wywoływana tylko raz, aby utworzyć zamknięcie.

Pasek nie jest dostępny z zewnątrz w ten sposób.

+0

Interesujące. Mam ograniczoną ekspozycję na javascript, więc zamknięcie jest dla mnie czymś nowym. Jednak zaznaczyłeś dla mnie punkt wyjścia do przestudiowania zamknięcia. Dzięki. – tamakisquare

Powiązane problemy