W tym temacie opowiem o Metaprogramowaniu w JavaScript.
- Introspekcja : oznacza to, że kod może się przeglądać, ma zdolność czytania struktury programu.
- Samomodyfikacja : oznacza to, że kod może się zmienić.
- Wstawiennictwo : Oznacza to, że kod może zmienić swoją semantykę. Kod ma zdolność podejmowania decyzji na podstawie samego siebie.
Przyjrzyjmy się temu kodowi.
Stworzyliśmy obiekt o nazwie cars i używamy metody Object.keys i przekazujemy nasz obiekt car do pobrania całego klucza. Obiekt ma kilka metod uzyskania wszystkich jego właściwości, co oznacza, że ma możliwość odczytania kodu i w tym przypadku zwraca swoje właściwości. Okazało się, że piszę kod, aby sprawdzić mój kod (w tym konkretnym przypadku case car obiekt) i uzyskiwanie jego właściwości, a następnie zapętlam go i po prostu drukuję. Więc Object.keys to Introspekcja.
Samodzielna modyfikacja
Weźmy na to przykład.
Napisaliśmy funkcję, która przyjmuje pewne argumenty i obok instrukcji if zwraca tylko sumę tych dwóch liczb. Ale kiedy wywołamy tę funkcję i przekażemy pierwszy argument większy lub równy 3, zmieni się i teraz zwraca 0 za każdym razem, gdy ją wywołujemy i niezależnie od tego, jaki argument przekazujemy.
Wstawiennictwo — kodeks podejmuje decyzje w oparciu o siebie
Jak powiedzieliśmy, chodzi o zmianę semantyki języka. Przyjrzyjmy się zatem obiektowi Object.defineProperty. Wymaga 3 argumentów, rzeczywistego obiektu, właściwości i obiektu, który nazywamy deskryptorami właściwości. Mamy dwa rodzaje deskryptorów: deskryptory danych i deskryptory dostępu. Deskryptor danych to właściwość z wartością, która może być zapisywana lub nie. Deskryptor akcesora to właściwość opisana przez parę funkcji pobierających i ustawiających. Zróbmy przykład z deskryptorem danych .
Definiując właściwość i jej deskryptor zmieniliśmy zachowanie właściwości age , uczyniłem z niej właściwość tylko do odczytu , definiując zapisywalny jako false . Jeśli spróbuję przypisać jakąś wartość do właściwości age, wyrzuci to TypeError w trybie ścisłym. Ustawiając enumerable na false ukrywamy to przed zapętleniem. Na przykład Object.keys(exampleObject) nie zawiera już właściwości wieku. Istnieje znacznie więcej metod refleksji dla obiektu Javascript.
Ale co się stanie, jeśli dla niektórych metod wstawiennictwa obiektu właściwość nie zostanie pomyślnie zdefiniowana? Na przykład, jeśli spróbujemy określić akcesory i wartość w tym samym czasie w deskryptorze, wyrzuci błąd TypeError.
Aby uniknąć tego zachowania, mamy Reflect . Reflect jest wbudowanym obiektem statycznym, który udostępnia metody Javascript Reflection. Wszystkie właściwości i metody Reflectsą statyczne (podobnie jak Mathobiekt). Zawiera kilka metod podobnych do Object, takich jak defineProperty, ale z niewielką różnicą. Jeśli wystąpi jakikolwiek błąd, po prostu zwróci false . Na podstawie poprzedniego przykładu możemy sprawdzić, czy(Reflect.defineProperty(…)) jest prawdziwe, a następnie kontynuować, czy nie. Więc metody statyczne Reflect zwracają wartość logiczną.
Odzwierciedlaj.stosuj
Jedną z najbardziej użytecznych metod jestReflect.apply(target, context, args);
Pobiera funkcję docelową, wartość this podaną dla wywołania celu i listę argumentów. Na podstawie kontekstu wywołuje funkcję docelową i przekazuje argumenty jako listę argumentów.
Odzwierciedlaj.konstrukcję
Reflect.construct () to metoda statyczna, która działa jak operator new, ale jako funkcja. Wymaga wywołania funkcji docelowej jako new target(…) i argumentList, aby przekazać ją do funkcji.
Symbol
Zanim przejdziemy dalej, zróbmy sobie małą przerwę i przyjrzyjmy się nowemu typowi prymitywnemu w ES6 o nazwie Symbol . Symbol to nowy prymitywny typ, taki jak Number, Boolean i String, i gwarantuje, że będzie unikalny. Głównym celem jest to — aby osiągnąć unikalną wartość. Jest często używany do dodawania unikalnego klucza właściwości do obiektu, który nie będzie kolidował z kluczami, które jakikolwiek inny kod mógłby dodać do obiektu. Weźmy kilka przykładów.
Do czego służy Symbol
Daje nam to zupełnie wyjątkowe walory i właściwości. Na przykład możemy łatwo zapisać każdy kontekst, który wywołuje naszą funkcję za pomocą tego podejścia.
Jak powiedzieliśmy wcześniej Symbole mogą być używane jako właściwości obiektów.
Mamy dobrze znane symbole w javascript. Wszystkie statyczne właściwości konstruktora Symbol , takie jak Symbol.hasInstance , są samymi symbolami, których wartości są zawsze stałe. Są one znane jako dobrze znane symbole i są używane do niektórych wbudowanych operacji JavaScript. Na przykład, jeśli klasa ma static jako Symbol.hasInstanceswoją nazwę, ta metoda zakoduje swoje zachowanie z instanceofoperatorem.
Możemy więc łatwo modyfikować na przykład zachowanie tego słowa kluczowego instanceof .
Rozważmy inny dobrze znany symbol [Symbol.toStringTag] , który koduje ciąg znaków opisujący obiekt. Weźmy więc przykład i zdefiniujmy jego domyślny ciąg znaków.
Zwykle, gdy piszemy .toString() dla obiektu, który drukuje
oo.toString() -> [object Object]
Więc teraz, jeśli napiszemy metodę .toString() dla tego obiektu, zwróci ona coś takiego.
oo.toString() -> '[object "Saba Shavidze"]'
Teraz chcę się skupić na jednej rzeczy. Jak widać, możemy ingerować w zachowanie określonej właściwości obiektu, ale co się stanie, gdy chcemy, aby ta funkcja get , którą zdefiniowaliśmy, działała nie dla określonej właściwości, ale dla wszystkich właściwości obiektu obiekt.Oto nowy obiekt JavaScript Proxy.
Zdefiniujmy więc, co to jest? Obiekt Proxyumożliwia utworzenie obiektu, którego można użyć zamiast oryginalnego obiektu, ale który może na nowo zdefiniować podstawowe Objectoperacje, takie jak pobieranie, ustawianie i definiowanie właściwości. Wymaga 2 parametrów target — rzeczywisty obiekt do proxy i handler — obiekt taki jak deskryptor często nazywany pułapkami . Proxy is revocable oznacza, że możemy wywołać revoke i wtedy proxy przestanie działać.
Rozważmy bezpośrednio prosty przykład. Załóżmy, że chcemy ograniczyć dostęp do jakiejś właściwości obiektu.
Definiujemy więc metodę pułapki — poproś nasz obiekt o obsłużenie przypadku, gdy ktoś chce uzyskać dostęp do właściwości zwanej hasłem.
Mamy wiele metod pułapek w obiekcie obsługi: get, set, apply,struct, defineProperty itd. Wydaje się to znajome? Mamy wszystkie te metody również na obiekcie Reflect, więc zamiast target[property] zwróciliśmy w get trap, możemy łatwo zwrócić Reflect.get(target,property) po prostu. Co w tym takiego dobrego? Metody odzwierciedlania unikają przypadków, w których musimy popełniać błędy i obsługiwać różne nieprawidłowe przypadki. On sam to zrobi.
Pełnomocnictwo daje nam więc pełną zdolność do wstawiennictwa.
Śledź historię obiektu
Nie, chcę napisać jeden dobry przykład wstawiennictwa z pełnomocnikiem. Chcę mieć historię tego, jak zmienił się mój obiekt. Napiszmy funkcję, która pobiera obiekt i zwraca nowy Proxy na podstawie tego obiektu i obsługuje historię zmian, które zachodziły podczas ustawiania lub uzyskiwania właściwości z tego obiektu.
Tak więc trackObject otrzymał obiekt jako argument, a następnie na jego podstawie tworzymy obiekt proxy. Definiujemy funkcję get i set w obiekcie obsługi, aby obsłużyć gettera i settera. Jeśli użytkownik będzie próbował ustawić właściwość o wartości, najpierw sprawdzamy, czy spróbuje ustawić właściwość historii, czy nie, ponieważ nie powinna być ustawiana przez użytkownika. Następnie ustawiamy właściwość z wartością i wpychamy nowy obiekt do naszej tablicy o nazwie historia, aby zapisać właściwość i wartość, która została ustawiona oraz znacznik czasu, w którym to zrobiono. Podczas gdy użytkownik próbuje uzyskać jakąś właściwość, sprawdzamy, czy chce uzyskać historię, zwracamy tablicę historii, w przeciwnym razie otrzymujemy wartość właściwości.
Zarejestruj nową nieruchomość
Innym fajnym przykładem, którym chcę się podzielić, jest napisanie funkcji, która umożliwia dołączenie nowej właściwości Symbol do obiektu i zarejestrowanie w nim wartości. Więc napiszmy to.
Tak więc nasza funkcja przyjmuje obiekt, do którego chcemy dołączyć nową właściwość i zwraca funkcję, która przyjmuje funkcję, której chcemy nadać nową właściwość. Teraz możemy zarejestrować dowolną nową właściwość z funkcją, którą do niej przekazujemy. Więc pierwsze, co przychodzi mi do głowy, jak mogę zastosować to podejście, to stworzenie potoku dla obiektów. Czym w tym przypadku jest potok dla obiektów? Dało nam to możliwość dołączania funkcji do obiektu i przekazywania funkcji, która zwraca zmodyfikowany obiekt. Jak możemy to osiągnąć? Możemy najpierw dołączyć nową właściwość do klasy Object.prototype, ponieważ jest to klasa bazowa dla wszystkich obiektów. Tak więc nasza nowa właściwość Symbol zwróci funkcję i chcemy przekazać do niej nasz obiekt, więc chcemy mieć funkcję, która otrzymuje funkcję jako argument i wywołuje ją z obiektem, do którego jest dołączona, i zwraca ją. Napiszmy.

![Czym w ogóle jest lista połączona? [Część 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































