Articles

krótkie wprowadzenie do Metaprogramowania w JavaScript

odbicie

odbicie, w przeciwieństwie do „generowania kodu”, jest procesem zmieniającym podstawową mechanikę języka. Odbicie może nastąpić w czasie kompilacji lub w trybie runtime, ale będziemy trzymać się odbicia w trybie runtime, ponieważ mówimy o JavaScript, więc odbicie w czasie kompilacji nie będzie możliwe. Jednak koncepcje omawiane tutaj mogą mieć zastosowanie również do skompilowanego języka.

ponieważ zrozumieliśmy, że refleksja polega na zmianie podstawowej mechaniki języka, została ona podzielona na trzy główne kategorie, a mianowicie. introspekcja, wstawiennictwo i modyfikacja.

introspekcja

introspekcja jest procesem analizy programu. Jeśli wiesz, co robi program, możesz go zmodyfikować według swoich upodobań. Mimo że niektóre języki programowania nie obsługują funkcji generowania lub modyfikacji kodu, ale najprawdopodobniej pozwalają na introspekcję.

prostym przykładem introspekcji byłoby użycietypeof lubinstanceof operatorów w JavaScript. typeof zwraca bieżący typ danych wartości (lub wyrażenia, które zwraca wartość), podczas gdy instanceof zwraca true lub false, jeśli wartość LHS jest instancją klasy RHS. Zobaczmy ich w akcji.

(wprowadzenie/introspekcja.js)

w powyższym programie użyliśmy typeof I instanceof operatorów w funkcji coerce do wąchania typu danych przychodzących value. Jest to podstawowa demonstracja introspekcji. Jednak język specjalnie zaprojektowany do metaprogramowania może dostarczyć potężnych narzędzi do introspekcji.

możesz użyć in, aby sprawdzić, czy właściwość istnieje w obiekcie. Funkcja globalnaisNaN sprawdza, czy obiekt jestNaN. Istnieje kilka statycznych metod zbudowanych wokół Object, aby sprawdzić wartości typu Object, takich jak Object.isFrozen(value), aby sprawdzić, czy value jest zamrożone lub Object.keys(value) aby uzyskać nazwy właściwości obiektu value.

do ES5 mieliśmy te operatory i te metody do pracy. W ES2015 (ES6) JavaScript wprowadził Reflect obiekt dostarczający pewne metody statyczne (podobnie jak Object), ale specjalnie zaprojektowany do introspekcji. Ponieważ mamy osobną lekcję na temat Reflect, metody te są tam omówione.

wstawiennictwo

wstawiennictwo jest procesem ingerencji w procesy JavaScript i modyfikowania standardowego zachowania procesu. JavaScript zapewnia świetne narzędzia do wstawiennictwa jednym z nich jest Proxy.

KlasaProxy została wprowadzona w ES2015 (ES6) do przechwytywania (ingerowania) podstawowych operacji JavaScript wokół obiektów, tak jak widzieliśmy powyżej, ale w znacznie ładniejszy sposób. Mamy osobną lekcję na tematProxy (wkrótce), ale w skrócie,Proxy zawija przechwytywalną logikę wokół obiektu.

var targetWithProxy = new Proxy(target, handler);

tutajtarget jest obiektem, ahandler jest przechwytywaczem. handler jest również zwykłym obiektem JavaScript, ale z pewnymi znaczącymi polami. For example, handler.get would be a function that returns a custom value when target.prop (here, prop is any property) is accessed.

(introduction/proxy.js)

Proxy is a great way to provide abstractions over your not-so-public data. Na przykład w powyższym programie podaliśmy abstrakty nadtarget obiekt i dostosowaliśmy sposób, w jaki powinien się prezentować publicznie.

niektóre wstawiennictwo było możliwe również w ES5, na przykład użyciegetter Isetters na deskryptorach właściwości, ale spowodowałoby to mutację obiektutargetProxy zapewnia znacznie czystszy sposób na uzyskanie wstawiennictwa bez konieczności modyfikowania oryginalnego obiektu (target).

modyfikacja

modyfikacja odnosi się do modyfikacji zachowania programu poprzez mutację. W przypadku wstawiennictwa przechwyciliśmy tylko standardowe procesy JavaScript, dodając logikę przechwytującą między celem a odbiornikiem bez szkody dla celu. W tym przypadku modyfikacja, zmieniamy zachowanie samego celu tak dopasować odbiornika.

nadpisanie implementacji funkcji byłoby dobrym przykładem modyfikacji. Na przykład, jeśli funkcja jest zaprojektowana, aby zachowywać się w określony sposób, ale chcemy czegoś innego warunkowo, możemy to zrobić, projektując funkcję samoistną. Zobaczmy przykład.

(wprowadzenie/modyfikacja funkcji.js)

w powyższym przykładzie stworzyliśmy funkcję, która nadpisuje się nową implementacją funkcji. Byłby to najsurowszy przykład modyfikacji, ale mamy inne, być może bardziej znaczące przypadki użycia.

(wprowadzenie/readonly-object.js)

w powyższym przykładzie użyliśmyObject.defineProperty()metody do zmiany domyślnego deskryptora właściwościname w celu uczynienia go tylko do odczytu. Możesz również użyć metodyObject.freeze(), aby zablokować cały obiekt, aby uniknąć jakichkolwiek mutacji.

niektóre wstawiennictwo może się zdarzyć poprzez modyfikacje, jak można z powyższego przykładu. Ustawiając writable:false w deskryptorze właściwości obiektu, a więc mutując obiekt (wewnętrzna implementacja), uruchomiliśmy operację przypisania wartości.

Jeśli nie znasz metody valueOf, jest ona używana do wymuszania obiektu na prymitywnej wartości. Jeśli więc mam obiekt i ma onvalueOf metodę na sobie lub na swoim prototypowym łańcuchu, to ta metoda jest wywoływana przez JavaScript, gdy próbujesz wykonać na nim operację arytmetyczną. Domyślnie Object posiada metodę valueOf, która zwraca samą siebie (obiekt).

(wstęp/valueof.js)

jak widać w powyższym przykładzie,emp1/10spowodowałoNaN, ponieważ obiekt nie może być podzielony jak liczby naturalne. Ale później dodaliśmyvalueOf metodę naEmployee klasę, która zwracasalary wartość obiektu. Dlategoemp2/10 zwrócił200, ponieważemp2.salary jest200. Podobnie, emp3/10 zwrócił 300 jak dodaliśmy valueOf metoda bezpośrednio na emp3.

Tak więc na każdym kroku w powyższym przykładzie ingerujemy w sposób, w jaki obiekt jest prezentowany do standardowej operacji JavaScript i zmieniamy jego zachowanie na nasze upodobania. To tylko wstawiennictwo.

w ES2015 (ES6) JavaScript wprowadził nowy prymitywny typ danych, który jestsymbol. To nic takiego, jak widzieliśmy wcześniej i nie może być reprezentowane w dosłownej formie. Można ją skonstruować tylko przez wywołanie funkcjiSymbol.

var sym1 = Symbol();
var sym2 = Symbol();
var sym3 = Symbol('description'); // description for debugging aidsym1 === sym2 // false
sym1 === sym2 // falsetypeof sym1 // 'symbol'console.log( sym1 ); // 'Symbol()'
console.log( sym3 ); // 'Symbol(description)'

w skrócie, tworzy unikalne wartości, które mogą być również używane jako zwykłe klucze obiektów przy użyciu notacjiobj, gdziekey byłby symbolem.

var key = Symbol();var obj = {
name: 'Ross',
: 200
};console.log( obj.name ); // 'Ross'
console.log( obj ); // 200
Object.keys(obj); // obj = 300;

ponieważ są one unikalne, nie ma możliwości utworzenia duplikatu symbolu przez przypadek. Każdy nowy symbol jest unikalny (utworzony za pomocą Symbol()), co oznacza, że jeśli chcesz użyć tego samego symbolu, musisz zapisać go w zmiennej i przekazać tę zmienną, aby odnosić się do tego samego symbolu.

w przykładzievalueOf możesz zauważyć problem, jeśli nie jesteśmy ostrożni lub świadomi. PonieważvalueOf jest właściwościąstring (tak jak w emp3), każdy może ją nadpisać przypadkowo lub ktoś, kto nie wie o valueOf może zaplanować jej użycie na własny użytek, myśląc ” co jest w imieniu?”.

ponieważ symbole mogą być również używane jako klucze obiektów, JavaScript dostarczył kilka globalnych symboli, które powinny być używane jako klucze obiektów dla niektórych standardowych operacji JavaScript. Ponieważ symbole te są dobrze znane deweloperowi, nazywane są”dobrze znanymi symbolami”. Te dobrze znane symbole są udostępniane publicznie jako statyczne właściwości funkcjiSymbol.

jednym z dobrze znanych symboli jest Symbol.toPrimitive, który powinien być użyty jako klucz obiektu w celu uzyskania jego pierwotnej wartości. Tak, dobrze myślisz, jest to zamiennik metodyvalueOf I jest preferowany.

(wprowadzenie/symbol-toPrimitive.js)

💡 metodatoPrimitive nie tylko zwraca wartość liczbową obiektu. Przeczytaj lekcje symboli, aby dowiedzieć się więcej na ten temat.

JavaScript zapewnia wiele takich dobrze znanych symboli do przechwytywania i modyfikowania domyślnego zachowania JavaScript wokół obiektów. Omówimy to i symbole w ogólności w lekcji Symbole.