2013-08-15 19 views
16

Czy ktoś może wyjaśnić, w jaki sposób funkcja ta jest ostrzeżona, gdy więcej nie podano nawiasów parametrów. Nie mogłem tego jasno zrozumieć.Zamknięcie w JavaScript z wieloma nawiasami

function sum(a) { 

    var sum = a 

    function f(b) { 
    sum += b 
    return f 
    } 

    f.toString = function() { return sum } 

    return f 
} 

alert(sum(1)(2)) // 3 
alert(sum(5)(-1)(2)) // 6 
alert(sum(6)(-1)(-2)(-3)) // 0 
alert(sum(0)(1)(2)(3)(4)(5)) // 15 

Odpowiedz

16

Przy pierwszym wywołaniu funkcji pierwsza wartość jest przechowywana w sum. Następnie zostanie zwrócone function f(b), zachowując tymczasowy wynik w sum. Przy każdym kolejnym połączeniu wykonuje się funkcję f - wykonuje się sum += b i ponownie zwraca f. Jeśli wymagany jest kontekst ciągowy (na przykład w alert lub console.log), to wywoływany jest zamiast tego zwrot().

function sum(a) { 

    var sum = a 

    function f(b) { 
    sum += b 
    return f //<- from second call, f is returned each time 
       // so you can chain those calls indefinitely 
       // function sum basically got "overridden" by f 
    } 

    f.toString = function() { return sum } 

    return f //<- after first call, f is returned 
} 

Objaśnienie:

alert(sum(6)(-1)(-2)(-3)) // 0 
      /\ function sum called, f returned 
       /\ the returned function f is called, f returns itself 
        /\ again 
        /\ and again 
         /\ at last, alert() requires string context, 
          so f.toString is getting invoked now instead of f 
+0

Zasadniczo w pierwszej sumie alertów (1) (2), która jest a & b, a w drugim alarmie jest to suma (6) (- 1) (2) teraz, jak funkcja rozumie, że (2) jest również f (b) argument. – PCA

+0

@Babu pozbyć się myślenia aib. Można by pomyśleć o tym, że 'sum()' jest wywoływane raz, inicjując 'var sum', a następnie przejmując po pierwszym wywołaniu, przetwarzając każde kolejne wywołanie przez wywołanie' f (b) '(ponieważ samo f za każdym razem zwraca się, umożliwiając wykonywanie dalszych łańcuchów), chyba że wystąpi kontekst łańcuchowy. Czy teraz jest jaśniejsze? – Christoph

+0

świetne wyjaśnienie. Czy możesz mi powiedzieć zamiast f.toString możesz pokazać mi kod do zwrotu sumy bezpośrednio. suma funkcji (a) { suma var = a funkcja F (b) { suma suma b + = powrotu } // f.toString = funkcja() {powrotu suma} zwrotny f } suma (1) (2); – PCA

2

alert oczekuje ciągu znaków. Jeśli nie otrzyma łańcucha znaków, spróbuje przekształcić dowolny obiekt, który otrzymuje (i funkcję jest typem obiektu) w jeden. Jeśli obiekt ma metodę toString, zostanie ona wywołana w celu przeprowadzenia wspomnianej konwersji.

+2

Wszystkie obiekty mają funkcję 'toString', w większości przypadków jest to domyślny format" [nazwa konstruktora] ". W takim przypadku ta metoda zostanie zastąpiona, aby uzyskać wynik. –

3

Rzecz patrzeć na ten kawałek kodu

function f(b) { 
    sum += b 
    return f 
    } 

Funkcja ta zwraca referencję do siebie tak to można nazwać, jak wiele razy, jak to możliwe. Ważną rzeczą jest to, że posiada funkcję toString, która jest wywoływana i od toString jest zdefiniowana wewnątrz funkcji sum() ma dostęp do zmiennej sum i jego wartości bieżącej wartości (która jest zmieniana przez f())

1

sum i f funkcje zawsze zwracają funkcję f, można wywoływać ją w nieskończonych czasach bez uzyskiwania wyniku innego niż funkcja.

Wartość zwracana jest jedynie nadpisane toString metody f (który faktycznie zwraca liczbę):

console.log(sum(1)(2)) // Function(){} 
console.log(sum(1)(2).toString()) // 3 

Funkcja alert niejawnie wywołuje metodę toString gdy rzuca swoje argumenty do strun.

0

To nie działa zgodnie z przeznaczeniem we wszystkich przypadkach ... Problemem jest to, że oczekuje się .toString powrócić ciąg, więc metody ciągów w przewidzianym realizacji, nie zadziała, e. sol. sum(2)(3).split() spowoduje błąd.

Chociaż możemy założyć, że sum() wynik zawsze będzie liczbą, może nie być prawdą w niektórych przypadkach i może być trudny do debugowania, np. sol. Zauważyłem problem podczas testowania kodu początkowo napisanego pod numerem .toString tylko na jsbin.com (ma on wartość split na wewnętrznym argumencie console.log, przesłaniając go).

Zamiast tego .toString powinien wyglądać jak return String(result);. Dobrze, że .toString (gdy nie ma .valueOf lub nowoczesnej Symbol.toPrimitive) zajmie się konwersją prymitywów, więc kod oczekujący liczby również będzie działał. Możliwym problemem może być tutaj "podwójna" konwersja.

Lepszym rozwiązaniem może być użycie pary .toString i .valueOf lub pojedynczego Symbol.toPrimitive, jeśli kierujesz reklamy tylko na nowoczesne przeglądarki.

Przykład wykorzystania Symbol.toPrimitive:

function sum(a) { 
    let result = a; 

    function f(b) { 
    result += b; 

    return f; 
    } 

    f[Symbol.toPrimitive] = hint => hint === 'string' ? String(result) : result; 

    return f; 
} 

Przykład wykorzystania .toString i .valueOf parę.

function sum(a) { 
    var result = a; 

    function f(b) { 
    result += b; 

    return f; 
    } 

    // avoiding double conversion which will happen in case of .toString 
    f.valueOf = function() { return result; }; 
    f.toString = function() { return String(result); }; 

    return f; 
} 
Powiązane problemy