Owijanie funkcji JavaScript pozwala na dodanie wspólnej logiki do funkcji, których nie kontrolujesz, jak funkcje natywne i zewnętrzne. Wiele bibliotek JavaScript, takich jak agenci TrackJS, potrzebuje owijać zewnętrzne funkcje, aby wykonać swoją pracę. Dodanie wrapperów pozwala nam słuchać Telemetrii, błędów i logów w Twoim kodzie, bez konieczności wywoływania naszego API.
Możesz chcieć zawinąć funkcję, aby dodać oprzyrządowanie lub tymczasową logikę debugowania. Możesz także zmienić zachowanie zewnętrznej biblioteki bez potrzeby modyfikowania źródła.
Podstawowe zawijanie funkcji
Ponieważ JavaScript jest cudownie dynamiczny, możemy zawijać funkcje przez proste przedefiniowanie funkcji z czymś nowym. Na przykład, rozważ to zawijanie myFunction
:
W tym trywialnym przykładzie, zawinęliśmy oryginalną myFunction
i dodaliśmy komunikat logowania. Ale jest wiele rzeczy, których nie obsłużyliśmy:
- Jak przekazujemy argumenty funkcji?
- Jak utrzymujemy zakres (wartość
this
)? - Jak otrzymujemy wartość zwracaną?
- Co się stanie, jeśli wystąpi błąd?
Aby poradzić sobie z tymi rzeczami, musimy się trochę bardziej postarać w naszym opakowywaniu.
Zauważ, że w tym przykładzie nie tylko wywołujemy funkcję, ale call
-ing ją z wartością dla this
i argumentami a
, b
, i c
. Wartość this
zostanie przekazana z miejsca, w którym dołączasz swoją zawiniętą funkcję, Window
w tym przykładzie.
Otoczyliśmy również całą funkcję w bloku try/catch
, abyśmy mogli wywołać własną logikę w przypadku błędu, wyrzucić go ponownie lub zwrócić wartość domyślną.
Zaawansowane zawijanie funkcji
Podstawowy przykład zawijania będzie działał przez 90% czasu, ale jeśli budujesz współdzielone biblioteki, takie jak agenci TrackJS, to nie wystarczy! Aby zawijać nasze funkcje jak profesjonalista, istnieją pewne przypadki brzegowe, z którymi powinniśmy sobie poradzić:
- Co z niezadeklarowanymi lub nieznanymi argumentami?
- Jak dopasować sygnaturę funkcji?
- Jak odzwierciedlić właściwości funkcji?
Istnieją 3 subtelne, ale ważne zmiany. Po pierwsze (#1), nazwaliśmy funkcję. Wydaje się to zbędne, ale kod użytkownika może sprawdzić wartość function.name
, więc ważne jest, aby zachować nazwę podczas zawijania.
Druga zmiana (#2) dotyczy tego, jak nazwaliśmy zawijaną funkcję, używając apply
zamiast call
. Pozwala nam to na przekazanie obiektu arguments
, który jest tablicowym obiektem wszystkich argumentów przekazanych do funkcji w czasie wykonywania. To pozwala nam na obsługę funkcji, które mogą mieć niezdefiniowaną lub zmienną liczbę argumentów.
Z apply
, nie potrzebujemy argumentów a
, b
, i c
zdefiniowanych w sygnaturze funkcji. Ale poprzez dalsze deklarowanie tych samych argumentów, co w oryginalnej funkcji, zachowujemy ciągłość funkcji. To znaczy, Function.length
zwraca liczbę argumentów zdefiniowanych w sygnaturze, a to odzwierciedla oryginalną funkcję.
Ostatnia zmiana (#3) kopiuje wszelkie właściwości określone przez użytkownika z oryginalnej funkcji na nasze zawijanie.
Ograniczenia
To zawijanie jest dokładne, ale zawsze są ograniczenia w JavaScript. W szczególności, trudno jest poprawnie zawinąć funkcję z niestandardowym prototypem, takim jak konstruktor obiektu. Jest to przypadek użycia lepiej rozwiązany przez dziedziczenie.
Ogólnie rzecz biorąc, zmiana prototypu funkcji jest możliwa, ale nie jest to dobry pomysł. Istnieją poważne implikacje wydajności i niezamierzone efekty uboczne w manipulowaniu prototypami.
Szanuj środowisko
Owijanie funkcji daje ci dużo mocy do instrumentowania i manipulowania środowiskiem JavaScript. Masz obowiązek mądrze władać tą mocą. Jeśli budujesz wrappery funkcji, upewnij się, że szanujesz użytkownika i środowisko, w którym działasz. Mogą istnieć inne owijki, inni słuchacze dołączeni do zdarzeń i oczekiwania co do API funkcji. Postępuj delikatnie i nie łam zewnętrznego kodu.