Encapsulation in JavaScript

to będzie mój ostatni post w serii „JavaScript for (C/)Al Developers” dzisiaj. Gdybym kontynuował blogowanie o prawie czystych rzeczach JavaScript, mógłbyś rozsądnie zapytać, czy w rzeczywistości jest to blog NAV, czy JavaScript. To wciąż NAV, i podczas gdy rzeczy, o których mam zamiar napisać, są czysto pojęciem JavaScript, uważam, że są bardzo istotne dla każdego programisty dodatków sterujących. Więc, Wstrzymaj moje piwo, i wytrzymaj ze mną na kolejne.

jedna ze skarg, które często słyszę na temat JavaScript, jest taka, że w JavaScript nie ma enkapsulacji. Jest to prawie całkowicie prawda, z wyjątkiem faktu, że jest to całkowicie fałszywe.

gdzie jest problem w pierwszej kolejności, a potem jakie jest rozwiązanie? Zanurzmy się.

problem

wyobraź sobie, że deklarujesz konstruktora. Wyobraźmy sobie przez chwilę, że jesteśmy w świecie ES5 (którego niestety musimy użyć, jeśli chcemy, aby nasze dodatki kontrolne były w pełni kompatybilne ze wszystkimi platformami, na których obsługiwane są zarówno NAV, jak i Business Central).

to jest mój kod:

oczywiście, to pokazuje 42, czyli mój obecny wiek.

niedługo będą moje urodziny, więc to by pokazało 43:

teraz, chociaż bardzo chciałbym móc zrobić coś takiego w prawdziwym życiu:

… tak się nie stanie. W przyzwoitym świecie zorientowanym obiektowo „wiek” powinien być zamkniętą właściwością i nie powinieneś być w stanie nazwać vjeko.wiek = 25 lat. C# i „normalne” języki obiektowe mają koncepcję enkapsulacji, mogą sobie z tym poradzić za pomocą pól prywatnych, ale JavaScript nie ma pojęcia prywatnego. Wszystko zdefiniowane w konstruktorze obiektu (lub później w instancji zbudowanego obiektu) jest w pełni dostępne dla całego kodu, który ma dostęp do tej instancji. W moim przykładzie powyżej, każdy może ustawić wiek i uciec z nim.

można powiedzieć, że JavaScript nie ma enkapsulacji. I jak powiedziałem powyżej, całkowicie się mylisz.

rozwiązanie

tak oczywiste, jak to jest, że nie możemy zadeklarować niczego jako prywatnego bezpośrednio, wciąż są rzeczy, których możemy użyć. Jedna piękna koncepcja, która się przydaje, nazywa się zamknięciami. Zamknięcia są szczegółowo wyjaśnione na milionie blogów, witryn z dokumentacją i przykładami kodu w całym Internecie, a Ty możesz wygooglować z nich bejesus we własnym tempie, więc nie będę zagłębiał się w wyjaśnianie zamknięć tutaj. Po prostu przeskoczę od razu do zastosowania ich do rozwiązania problemu enkapsulacji.

są co najmniej dwa sposoby, jak możesz sobie z tym poradzić. Najpierw zróbmy bardziej oczywistą: funkcję dostępu.

wyobraź sobie świat bez właściwości, w którym nie możesz zrobić obiektu.właściwość = wartość (jak vjeko.wiek = 25 w naszym przypadku, tak bardzo, jak bardzo bym to pokochał!). W tym świecie masz funkcje getter i setter:

(zignoruj przez chwilę fakt, że nadal tego używam.wiek do „encapsulate „”property”)

oczywiście można by je tak nazwać:

następnie, jeśli chcesz mieć wiek jako Tylko do odczytu, po prostu porzucisz funkcję setAge setter. Jeśli to.wiek był naprawdę prywatny (co nie jest), to by Ci pomogło. Problem w tym, że nic zdefiniowanego na tym nie jest prywatne, jest dostępne dla każdego, kto ma dostęp do dowolnej instancji obiektu. To jest tak publiczne, jak to tylko możliwe.

aby rozwiązać ten problem, pierwszą rzeczą, którą musimy zrobić, to przenieść deklarację funkcji getter z prototypu do instancji. Prototype members są najbliżej tego, co nazwalibyśmy statycznymi w C#, mimo że w runtime mają pewne cechy zachowania zarówno statycznych, jak I instancji. Jednak najpierw przenieśmy element z dala od prototypu i na instancję:

to rozwiązuje tylko pierwszą część problemu, fakt, że getAge został zdefiniowany na prototypie, a nie na instancji. Jednak z taką prostą zmianą:

… rozwiązujemy problem całkowicie:

wiek jest teraz w pełni zamknięty. Możesz uzyskać do niego dostęp za pomocą funkcji getter, ale nie możesz ustawić go bezpośrednio, ponieważ jest dostępny tylko w zakresie zamknięcia tego.funkcja instancji getAge.

aby w pełni zaimplementować klasę Person, musisz przenieść funkcję growOlder z prototypu do instancji:

i to działa dokładnie tak, jak chcesz, aby działało:

ale dlaczego to działa? Działa z powodu zamknięcia. Parametr age konstruktora został przechwycony w zakresie zamknięcia zarówno funkcji getAge, jak i growOlder, umożliwiając dostęp do jego wartości z obu tych funkcji, ale czyniąc go całkowicie niedostępnym dla kogokolwiek innego, gdziekolwiek indziej.

jeszcze lepsze rozwiązanie

można powiedzieć, że nie chcesz uzyskać do niego dostępu za pomocą funkcji getter i że potrzebujesz pełnowymiarowej składni właściwości tylko do odczytu. Chcesz, aby twój wiek był tylko do odczytu i zamknięty w tym samym czasie. Mam cię! JavaScript nie może tego ująć! Poza tym, że absolutnie może.

zwróciłem się do obiektu.metoda defineProperty w moim poprzednim poście, a jeśli przeczytasz tę, możesz od razu zobaczyć, jak można ją zastosować tutaj.

zdefiniujmy więc właściwość instancji tylko do odczytu klasy Person:

tam. Nie bolało. I to działa:

no i proszę. Pełnowartościowa enkapsulacja w JavaScript, aby pomóc ci napisać dobry, izolowany kod i przejąć kontrolę nad dodatkami na prawdziwy poziom kick-a$$.

Szczęśliwego Javascriptingu i mam nadzieję, że będę miał więcej czasu na blogowanie o innych fajnych i przydatnych wskazówkach i Sztuczkach JavaScript dla programistów (C/) AL.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.