Obecnie pracuję nad platformami React JS i React Native . W połowie drogi natknąłem się na bibliotekę Immutability lub Immutable-JS , kiedy czytałem o implementacji Flux i Redux na Facebooku.
Pytanie brzmi: dlaczego niezmienność jest tak ważna? Co jest złego w mutowaniu obiektów? Czy to nie ułatwia rzeczy?
Podając przykład, rozważmy prostą aplikację do czytania Wiadomości, której ekran początkowy stanowi widok listy nagłówków wiadomości.
Jeżeli ustawić powiedzieć o tablicę obiektów o wartości początkowo nie mogę manipulować. Tak mówi zasada niezmienności, prawda? (Popraw mnie, jeśli się mylę.) Ale co, jeśli mam nowy obiekt Wiadomości, który należy zaktualizować? W zwykłym przypadku mogłem właśnie dodać obiekt do tablicy. Jak mogę osiągnąć w tym przypadku? Usunąć sklep i odtworzyć go? Czy dodanie obiektu do tablicy nie jest tańszą operacją?
Odpowiedzi:
Ostatnio badam ten sam temat. Zrobię co w mojej mocy, aby odpowiedzieć na twoje pytanie (pytania) i postaram się podzielić tym, czego nauczyłem się do tej pory.
Zasadniczo sprowadza się to do tego, że niezmienność zwiększa przewidywalność, wydajność (pośrednio) i pozwala na śledzenie mutacji.
Przewidywalność
Mutacja ukrywa zmiany, które powodują (nieoczekiwane) skutki uboczne, które mogą powodować paskudne błędy. Wymuszając niezmienność, możesz uprościć architekturę aplikacji i model mentalny, co ułatwia rozumowanie aplikacji.
Występ
Mimo że dodawanie wartości do niezmiennego obiektu oznacza, że należy utworzyć nową instancję, w której należy skopiować istniejące wartości i dodać nowe wartości do nowego obiektu, które kosztują pamięć, niezmienne obiekty mogą korzystać ze współdzielenia strukturalnego w celu zmniejszenia pamięci nad głową.
Możesz przeczytać więcej na ten temat tutaj .
Śledzenie mutacji
Poza zmniejszonym zużyciem pamięci, niezmienność pozwala zoptymalizować twoją aplikację poprzez wykorzystanie równości odniesienia i wartości. Dzięki temu naprawdę łatwo sprawdzić, czy coś się zmieniło. Na przykład zmiana stanu w składniku reagującym. Możesz użyć,
shouldComponentUpdate
aby sprawdzić, czy stan jest identyczny, porównując obiekty stanowe i zapobiec niepotrzebnemu renderowaniu. Możesz przeczytać więcej na ten temat tutaj .Dodatkowe zasoby:
Tak, to jest poprawne. Jeśli nie masz pewności, jak zaimplementować to w swojej aplikacji, polecam przyjrzeć się, jak robi to redux , aby zapoznać się z podstawowymi koncepcjami, bardzo mi pomogło.
Lubię używać Redux jako przykładu, ponieważ obejmuje niezmienność. Ma jedno niezmienne drzewo stanów (określane jako
store
), w którym wszystkie zmiany stanu są jawne poprzez wysyłanie akcji przetwarzanych przez reduktor, który akceptuje poprzedni stan wraz ze wspomnianymi akcjami (pojedynczo) i zwraca następny stan aplikacji . Możesz przeczytać więcej o jego podstawowych zasadach tutaj .Istnieje świetny kurs redux na egghead.io, w którym Dan Abramov , autor redux, wyjaśnia te zasady w następujący sposób (nieco zmodyfikowałem kod, aby lepiej pasował do scenariusza):
Ponadto filmy te pokazują bardziej szczegółowo, jak osiągnąć niezmienność dla:
źródło
const
nie dotyczy niezmienności. Mathias Bynens napisał o tym świetny artykuł na blogu .Przeciwny pogląd na niezmienność
TL / DR: Niezmienność jest bardziej modnym trendem niż koniecznością w JavaScript. Jeśli używasz React, zapewnia to porządne obejście niektórych mylących wyborów projektowych w zarządzaniu stanem. Jednak w większości innych sytuacji nie doda wystarczającej wartości nad złożonością, którą wprowadza, służąc więcej do uzupełnienia CV niż do zaspokojenia faktycznej potrzeby klienta.
Długa odpowiedź: przeczytaj poniżej.
Cóż, cieszę się, że zapytałeś!
Jakiś czas temu bardzo utalentowany facet o imieniu Dan Abramov napisał bibliotekę zarządzania stanem javascript o nazwie Redux, która wykorzystuje czyste funkcje i niezmienność. Nakręcił także kilka naprawdę fajnych filmów , dzięki którym pomysł był naprawdę łatwy do zrozumienia (i sprzedaży).
Czas był idealny. Nowość Angulara zanikała, a świat JavaScript był gotowy skupić się na najnowszej rzeczy, która miała odpowiedni stopień fajności, a ta biblioteka była nie tylko innowacyjna, ale idealnie pasowała do React, który był sprzedawany przez inną potęgę w Dolinie Krzemowej .
Choć może to być smutne, w świecie JavaScript rządzą mody. Teraz Abramow zostaje okrzyknięty półbogiem i wszyscy, zwykli śmiertelnicy, musimy poddać się Tao Niezmienności ... Czy to ma sens, czy nie.
Nic!
W rzeczywistości programiści mutowali obiekty dla er ... tak długo, jak istnieją obiekty do mutacji. Innymi słowy, ponad 50 lat rozwoju aplikacji.
A po co komplikować? Kiedy masz obiekt
cat
i umiera, czy naprawdę potrzebujesz sekundy,cat
aby śledzić zmianę? Większość ludzi powiedziałabycat.isDead = true
i skończyła.TAK! .. Oczywiście, że tak!
Szczególnie w JavaScript, który w praktyce jest najbardziej przydatny do renderowania widoku stanu, który jest utrzymywany gdzie indziej (np. W bazie danych).
Możesz przejść do tradycyjnego podejścia i zaktualizować
News
obiekt, aby zmieniła się twoja reprezentacja tego obiektu w pamięci (i widok wyświetlany użytkownikowi, a przynajmniej można mieć nadzieję) ...Lub alternatywnie ...
Możesz wypróbować seksowne podejście FP / Immutability i dodać swoje zmiany do
News
obiektu do tablicy śledzącej każdą historyczną zmianę, aby następnie iterować tablicę i dowiedzieć się, jaka powinna być poprawna reprezentacja stanu (uff!).Mody przychodzą i odchodzą, kolego. Istnieje wiele sposobów na skórowanie kota.
Przykro mi, że musicie znosić ciągle zmieniający się zestaw paradygmatów programowania. Ale hej, WITAMY W KLUBIE !!
Teraz kilka ważnych punktów do zapamiętania w odniesieniu do Niezmienności, a dostaniesz je w gorączkową intensywność, którą może zdobyć tylko naiwność.
1) Niezmienność jest świetna do unikania warunków wyścigowych w środowiskach wielowątkowych .
Środowiska wielowątkowe (takie jak C ++, Java i C #) są winne praktyki blokowania obiektów, gdy więcej niż jeden wątek chce je zmienić. Jest to niekorzystne z punktu widzenia wydajności, ale lepsze niż alternatywa uszkodzenia danych. A jednak nie tak dobre, jak uczynić wszystko niezmiennym (chwała Haskellowi!).
ALE NIESTETY! W JavaScript zawsze działasz na jednym wątku . Nawet pracownicy sieci (każdy działa w osobnym kontekście ). Ponieważ więc nie możesz mieć powiązanego z wątkiem stanu wyścigu w kontekście wykonywania (wszystkie te piękne zmienne globalne i zamknięcia), główny punkt na korzyść Niezmienności wychodzi poza okno.
(Mimo, że nie jest zaletą przy użyciu czystych funkcji pracowników internetową, która jest, że będziesz mieć żadnych oczekiwań dotyczących błahy z obiektami na głównym wątku).
2) Niezmienność może (jakoś) uniknąć warunków wyścigu w stanie twojej aplikacji.
Oto prawdziwy sedno sprawy, większość programistów (React) powie ci, że Immutability i FP mogą w jakiś sposób działać na magii, która pozwala na przewidywalność stanu twojej aplikacji.
Oczywiście nie oznacza to, że możesz uniknąć warunków wyścigu w bazie danych , aby to zrobić, musisz koordynować wszystkich użytkowników we wszystkich przeglądarkach , a do tego potrzebujesz technologii push back-end, takiej jak WebSockets ( więcej na ten temat poniżej), które będą rozpowszechniać zmiany wśród wszystkich użytkowników aplikacji.
Nie oznacza to również, że istnieje jakiś nieodłączny problem w JavaScript, w którym stan twojej aplikacji wymaga niezmienności, aby stać się przewidywalnym, każdy programista, który kodował aplikacje frontowe, zanim React powie ci o tym.
To dość mylące twierdzenie oznacza po prostu, że dzięki React stan zgłoszenia stanie się bardziej podatny na warunki wyścigowe , ale ta niezmienność pozwala ci usunąć ten ból. Czemu? Ponieważ React jest wyjątkowy .. został zaprojektowany jako wysoce zoptymalizowana biblioteka renderująca z spójnym zarządzaniem stanem zajmującym drugie miejsce, a zatem stanem komponentu zarządza się poprzez asynchroniczny łańcuch zdarzeń (inaczej „jednokierunkowe wiązanie danych”), nad którymi nie masz kontroli i polegaj na tym, że pamiętasz, aby nie mutować stanu bezpośrednio ...
Biorąc pod uwagę ten kontekst, łatwo jest zauważyć, że potrzeba niezmienności ma niewiele wspólnego z JavaScriptem, a wiele z warunkami wyścigu w React: jeśli masz kilka wzajemnie zależnych zmian w swojej aplikacji i nie ma łatwego sposobu, aby dowiedzieć się, co twój stan jest obecnie w stanie, będziesz się mylić , a zatem nie ma sensu używać niezmienności do śledzenia każdej historycznej zmiany .
3) Warunki wyścigu są kategorycznie złe.
Mogą być, jeśli używasz React. Ale są rzadkie, jeśli wybierzesz inną strukturę.
Poza tym zwykle masz znacznie większe problemy do rozwiązania… Problemy takie jak piekło uzależnienia. Jak rozdęta baza kodu. Jak twój CSS nie ładuje się. Jak powolny proces kompilacji lub utknięcie w monolitycznym zapleczu, który sprawia, że iteracja jest prawie niemożliwa. Jak niedoświadczeni deweloperzy, którzy nie rozumieją, co się dzieje i robią bałagan.
Wiesz. Rzeczywistość. Ale hej, kogo to obchodzi?
4) Niezmienność wykorzystuje typy referencyjne w celu zmniejszenia wpływu na śledzenie każdej zmiany stanu.
Ponieważ poważnie, jeśli zamierzasz kopiować rzeczy za każdym razem, gdy zmienia się twój stan, lepiej upewnij się, że jesteś inteligentny.
5) Niezmienność umożliwia cofanie operacji .
Ponieważ… to jest najważniejsza funkcja, o którą poprosi kierownik projektu, prawda?
6) Niezmienny stan ma wiele fajnych możliwości w połączeniu z WebSockets
Wreszcie, akumulacja delt stanu stanowi dość interesujący przypadek w połączeniu z WebSockets, co pozwala na łatwe wykorzystanie stanu jako przepływu niezmiennych zdarzeń ...
Gdy grosz spadnie na tę koncepcję (stan jest ciągiem wydarzeń - a nie prymitywny zestaw zapisów przedstawiających najnowszą perspektywę), niezmienny świat staje się magicznym miejscem do zamieszkania. Kraina zdarzeniami pozyskiwane zdumienia i możliwości, które wykracza poza sam czas . I kiedy zrobione dobrze to na pewno może robić w czasie rzeczywistym aplikacje easi er osiągnąć, po prostu nadawać przepływ zdarzeń dla wszystkich zainteresowanych, aby mogli budować własną reprezentację teraźniejszości i odpisać swoje zmiany do strumienia komunalnego.
Ale w pewnym momencie budzisz się i zdajesz sobie sprawę, że cały ten cud i magia nie przychodzą za darmo. W przeciwieństwie do twoich chętnych współpracowników, twoi interesariusze (tak, ludzie, którzy ci płacą) mało dbają o filozofię lub modę, a dużo o pieniądze, które płacą, aby zbudować produkt, który mogą sprzedać. Najważniejsze jest to, że trudniej jest napisać niezmienny kod i łatwiej go złamać, a ponadto nie ma sensu mieć niezmiennego interfejsu, jeśli nie masz zaplecza do jego obsługi. Kiedy (i jeśli!) W końcu przekonasz swoich interesariuszy, że powinieneś publikować i konsumować wydarzenia za pomocą technologii push, takiej jak WebSockets, dowiadujesz się, jak trudno jest zwiększyć skalę produkcji .
Teraz kilka porad, jeśli zdecydujesz się je zaakceptować.
Wybór pisania JavaScript za pomocą FP / Immutability to także wybór, aby baza kodu aplikacji była większa, bardziej złożona i trudniejsza w zarządzaniu. Zdecydowanie argumentowałbym za ograniczeniem tego podejścia do reduktorów Redux, chyba że wiesz, co robisz ... A JEŚLI masz zamiar użyć niezmienności niezależnie od tego, następnie zastosuj stan niezmienności do całego stosu aplikacji , a nie tylko po stronie klienta, ponieważ inaczej tracisz jego prawdziwą wartość.
Teraz, jeśli masz szczęście, że możesz dokonywać wyborów w swojej pracy, spróbuj użyć swojej mądrości (lub nie) i rób to, co właściwe, przez osobę, która ci płaci . Możesz oprzeć to na swoim doświadczeniu, na jelitach lub na tym, co się wokół ciebie dzieje (co prawda, jeśli wszyscy używają React / Redux, istnieje uzasadniony argument, że łatwiej będzie znaleźć zasób, aby kontynuować pracę). Alternatywnie, możesz wypróbować metody Resume Driven Development lub Hype Driven Development . Mogą być bardziej twoją rzeczą.
Krótko mówiąc, niezmienność należy powiedzieć, że sprawi, że będziesz modna w stosunku do swoich rówieśników, przynajmniej do następnego szaleństwa, w którym to momencie będziesz zadowolony z przejścia.
Teraz po tej sesji autoterapii chciałbym zaznaczyć, że dodałem to jako artykuł na moim blogu => Niezmienność w JavaScript: pogląd kontrastyka . Nie wahaj się odpowiedzieć, jeśli masz silne uczucia, które chciałbyś również zdjąć z piersi;).
źródło
"hyped" !== "bad"
W rzeczywistości jest odwrotnie: zmienność czyni sprawy bardziej skomplikowanymi, przynajmniej na dłuższą metę. Tak, ułatwia to początkowe kodowanie, ponieważ możesz po prostu zmieniać rzeczy gdziekolwiek chcesz, ale kiedy twój program się powiększy, staje się problemem - jeśli wartość się zmieniła, co ją zmieniło?
Gdy sprawisz, że wszystko będzie niezmienne, oznacza to, że danych nie można już zmienić z zaskoczenia. Wiesz na pewno, że jeśli przekażesz wartość do funkcji, nie można jej zmienić w tej funkcji.
Mówiąc prościej: jeśli używasz niezmiennych wartości, bardzo łatwo jest rozumować twój kod: każdy dostaje unikalną * kopię twoich danych, więc nie może z nią sflashować i złamać innych części twojego kodu. Wyobraź sobie, o ile ułatwia to pracę w środowisku wielowątkowym!
Uwaga 1: Niezmienność może potencjalnie kosztować wydajność w zależności od tego, co robisz, ale rzeczy takie jak Immutable.js optymalizują najlepiej, jak potrafią.
Uwaga 2: W mało prawdopodobnym przypadku, gdy nie byłeś pewien, Immutable.js i ES6
const
oznaczają bardzo różne rzeczy.Tak, twój przykład wiadomości jest całkowicie dobry, a twoje rozumowanie jest dokładnie słuszne: nie możesz po prostu zmienić swojej istniejącej listy, więc musisz utworzyć nową:
źródło
Imagine how much easier this makes working in a multi-threaded environment!
-> Ok dla innych języków, ale nie jest to zaletą w jednowątkowym JavaScript.Chociaż pozostałe odpowiedzi są w porządku, aby odpowiedzieć na twoje pytanie dotyczące praktycznego przypadku użycia (z komentarzy do innych odpowiedzi), pozwól na chwilę wyjść poza twój działający kod i spójrz na wszechobecną odpowiedź pod nosem: git . Co by się stało, gdybyś za każdym razem, gdy wciskałeś zatwierdzenie, nadpisywałeś dane w repozytorium?
Teraz mamy do czynienia z jednym z problemów, przed którymi stoją niezmienne kolekcje: nadęty pamięci. Git jest na tyle inteligentny, że nie tylko tworzy nowe kopie plików za każdym razem, gdy wprowadzasz zmiany, po prostu śledzi różnice .
Chociaż niewiele wiem o wewnętrznym działaniu git, mogę jedynie założyć, że używa strategii podobnej do bibliotek, do których się odwołujesz: współdzielenie strukturalne. Pod maską biblioteki używają prób lub innych drzew do śledzenia tylko różnych węzłów.
Strategia ta jest również dość wydajna w przypadku struktur danych w pamięci, ponieważ istnieją dobrze znane algorytmy operacji drzewa, które działają w czasie logarytmicznym.
Kolejny przypadek użycia: powiedz, że chcesz cofnąć przycisk w swojej aplikacji internetowej. W przypadku niezmiennych reprezentacji danych wdrożenie takich danych jest stosunkowo proste. Ale jeśli polegasz na mutacji, oznacza to, że musisz się martwić o buforowanie stanu świata i dokonywanie aktualizacji atomowych.
Krótko mówiąc, za niezmienność wydajności środowiska wykonawczego i krzywej uczenia się trzeba zapłacić. Ale każdy doświadczony programista powie ci, że czas debugowania przewyższa czas pisania kodu o rząd wielkości. Niewielkie zmniejszenie wydajności środowiska wykonawczego prawdopodobnie przeważają nad błędami związanymi ze stanem, których użytkownicy nie muszą znosić.
źródło
O zmienności
Z technicznego punktu widzenia nie ma nic złego w zmienności. Jest szybki, ponownie wykorzystuje pamięć. Programiści są przyzwyczajeni od samego początku (jak pamiętam). Istnieje problem w stosowaniu zmienności i problemów, które może to przynieść.
Jeśli obiekt nie jest współdzielony z niczym, na przykład istnieje w zakresie funkcji i nie jest wystawiony na zewnątrz, trudno dostrzec korzyści w niezmienności. Naprawdę w tym przypadku nie ma sensu być niezmiennym. Poczucie niezmienności zaczyna się, gdy coś jest udostępniane.
Zmienność bólu głowy
Zmienna wspólna struktura może z łatwością stworzyć wiele pułapek. Każda zmiana w dowolnej części kodu z dostępem do odniesienia ma wpływ na inne części z widocznością tego odniesienia. Taki wpływ łączy wszystkie części razem, nawet jeśli nie powinny być świadome istnienia różnych modułów. Mutacja jednej funkcji może spowodować awarię zupełnie innej części aplikacji. To jest zły efekt uboczny.
Następnym często problemem z mutacją jest stan uszkodzony. Stan uszkodzony może się zdarzyć, gdy procedura mutacji zawiedzie w środku, a niektóre pola zostały zmodyfikowane, a niektóre nie.
Co więcej, przy mutacji trudno jest wyśledzić zmianę. Prosta kontrola referencji nie pokaże różnicy, aby wiedzieć, co zmieniło się, należy dokonać głębokiej kontroli. Aby monitorować zmianę, należy wprowadzić pewne obserwowalne wzorce.
Wreszcie mutacja jest przyczyną deficytu zaufania. Jak możesz być pewien, że jakaś struktura chciała wartości, jeśli można ją zmutować.
Jak pokazuje powyższy przykład, przejście modyfikowalnej struktury zawsze może zakończyć się inną strukturą. Funkcja doSomething mutuje atrybut podany z zewnątrz. Brak zaufania do kodu, naprawdę nie wiesz, co masz i co będziesz miał. Wszystkie te problemy mają miejsce, ponieważ: Zmienne struktury reprezentują wskaźniki pamięci.
Niezmienność dotyczy wartości
Niezmienność oznacza, że zmiany nie dokonuje się na tym samym obiekcie, strukturze, ale zmiana jest reprezentowana w nowym. A to dlatego, że odwołanie reprezentuje wartość nie tylko wskaźnika pamięci. Każda zmiana tworzy nową wartość i nie dotyka starej. Tak jasne reguły przywracają zaufanie i przewidywalność kodu. Z funkcji można bezpiecznie korzystać, ponieważ zamiast mutacji dotyczą one własnych wersji z własnymi wartościami.
Używanie wartości zamiast kontenerów pamięci daje pewność, że każdy obiekt reprezentuje określoną niezmienną wartość i można z niego bezpiecznie korzystać.
Niezmienne struktury reprezentują wartości.
Jeszcze bardziej nurkuję na ten temat w średnim artykule - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310
źródło
Niezmienność można śledzić w różnych kontekstach, ale najważniejsze jest śledzenie jej względem stanu aplikacji i interfejsu użytkownika aplikacji.
Rozważę wzorzec JavaScript Redux jako bardzo modne i nowoczesne podejście i dlatego, że o tym wspomniałeś.
W przypadku interfejsu użytkownika musimy uczynić go przewidywalnym . Będzie to przewidywalne, jeśli
UI = f(application state)
.Aplikacje (w JavaScript) zmieniają stan poprzez akcje realizowane za pomocą funkcji reduktora .
Funkcja reduktora po prostu przyjmuje akcję i stary stan i zwraca nowy stan, utrzymując stary stan nienaruszony.
Korzyścią jest to: podróżujesz w czasie po stanach, ponieważ wszystkie obiekty stanowe są zapisywane, i możesz odtąd renderować aplikację w dowolnym stanie
UI = f(state)
Możesz więc łatwo cofać / powtarzać.
Zdarza się, że tworzenie wszystkich tych stanów wciąż może być wydajne pod względem pamięci, analogia z Gitem jest świetna, i mamy podobną analogię w systemie Linux z linkami symbolicznymi (opartymi na i-węzłach).
źródło
Kolejną zaletą niezmienności w Javascripcie jest to, że redukuje ono sprzężenie skroniowe, co ma znaczne zalety w zakresie projektowania. Rozważ interfejs obiektu za pomocą dwóch metod:
Może się zdarzyć, że wywołanie
baz()
jest wymagane, aby obiekt był w prawidłowym stanie, aby połączeniebar()
działało poprawnie. Ale skąd to wiesz?Aby to rozgryźć, musisz dokładnie zbadać wewnętrzne elementy klasy, ponieważ nie jest to od razu widoczne po badaniu interfejsu publicznego. Ten problem może eksplodować w dużej bazie kodu z dużą ilością zmiennych stanów i klas.
Jeśli
Foo
jest niezmienny, nie stanowi to już problemu. Można bezpiecznie założyć, że możemy zadzwonićbaz
lubbar
w dowolnej kolejności, ponieważ wewnętrzny stan klasy nie może się zmienić.źródło
Pewnego razu był problem z synchronizacją danych między wątkami. Ten problem był wielkim bólem, było ponad 10 rozwiązań. Niektórzy próbowali rozwiązać to radykalnie. To było miejsce narodzin programowania funkcjonalnego. To jest tak jak marksizm. Nie mogłem zrozumieć, jak Dan Abramov sprzedał ten pomysł JS, ponieważ jest on jednowątkowy. On jest geniuszem.
Mogę podać mały przykład. W
__attribute__((pure))
gcc znajduje się atrybut . Kompilatory próbują ustalić, czy twoja funkcja jest czysta, czy nie, jeśli nie specjalnie ją odszyfrujesz. Twoja funkcja może być czysta, nawet twój stan jest zmienny. Niezmienność to tylko jeden z ponad 100 sposobów, aby zagwarantować, że Twoje funkcje będą czyste. W rzeczywistości 95% twoich funkcji będzie czystych.Nie powinieneś używać żadnych ograniczeń (takich jak niezmienność), jeśli faktycznie nie masz poważnego powodu. Jeśli chcesz „cofnąć” jakiś stan, możesz tworzyć transakcje. Jeśli chcesz uprościć komunikację, możesz wysyłać zdarzenia z niezmiennymi danymi. To zależy od Ciebie.
Piszę tę wiadomość z republiki post marksizmu. Jestem pewien, że radykalizacja każdego pomysłu jest niewłaściwa.
źródło
Inne podejście ...
Moja druga odpowiedź dotyczy tego pytania z bardzo praktycznego punktu widzenia i nadal mi się podoba. Postanowiłem dodać to jako kolejną odpowiedź, a nie dodatek do tej, ponieważ jest to nudna filozoficzna rant, która, mam nadzieję, również odpowiada na pytanie, ale tak naprawdę nie pasuje do mojej istniejącej odpowiedzi.
TL; DR
Nawet w małych projektach niezmienność może być przydatna, ale nie zakładaj, że ponieważ istnieje, jest przeznaczona dla Ciebie.
Znacznie dłuższa odpowiedź
UWAGA: dla celów tej odpowiedzi używam słowa „dyscyplina”, aby oznaczać samozaparcie dla pewnych korzyści.
Jest to podobne w formie do innego pytania: „Czy powinienem używać maszynopisu? Dlaczego typy są tak ważne w JavaScript?”. Ma również podobną odpowiedź. Rozważ następujący scenariusz:
Masz teraz wybór, czy przejdziesz do maszynopisu?
Maszynopis ma kilka istotnych zalet: inteligencję, wczesne wykrywanie błędów, określanie interfejsów API z góry, łatwość naprawiania błędów podczas refaktoryzacji, mniej testów. Maszynopis ma również pewne koszty: pewne bardzo naturalne i poprawne idiomy JavaScript mogą być trudne do modelowania w jego niezbyt potężnym systemie typów, adnotacje zwiększają LoC, czas i wysiłek przepisywania istniejącej bazy kodu, dodatkowy krok w procesie kompilacji itp. Mówiąc bardziej ogólnie, tworzy podzbiór możliwych poprawnych programów JavaScript w zamian za obietnicę, że Twój kod będzie bardziej poprawny. Jest to arbitralnie restrykcyjne. O to chodzi: narzucasz dyscyplinę, która Cię ogranicza (mam nadzieję, że postrzelisz się w stopę).
Wracając do pytania, sformułowanego w kontekście powyższego akapitu: czy warto ?
W opisanym scenariuszu twierdziłbym, że jeśli jesteś dobrze zaznajomiony z bazą kodu JS dla małych i średnich firm, wybór użycia Maszynopisu jest bardziej estetyczny niż praktyczny. I to jest w porządku , nie ma nic złego w estetyce, po prostu niekoniecznie są przekonujące.
Scenariusz B:
Kiedy byłeś facetem / dziewczyną w LoK 5k, to nie miało tak wielkiego znaczenia. Nawet dokumentacja nie była aż tak wielką sprawą, nawet powrót do określonej części kodu po 6 miesiącach można było łatwo zrozumieć. Ale teraz dyscyplina jest nie tylko miła, ale konieczna . Ta dyscyplina może nie obejmować Typescript, ale prawdopodobnie będzie obejmować pewną formę analizy statycznej, a także wszystkie inne formy dyscypliny kodowania (dokumentacja, przewodnik po stylu, skrypty budowania, testy regresji, CI). Dyscyplina nie jest już luksusem , jest koniecznością .
Wszystko to miało zastosowanie
GOTO
w 1978 roku: twoja mała, czarna gra w blackjacka w C mogła używaćGOTO
logiki s i spaghetti i po prostu nie było tak wielkim wyborem własnej przygody, ale wraz z rozwojem programów bardziej ambitne, no i niezdyscyplinowane użycieGOTO
nie mogło być utrzymane. Wszystko to dotyczy dziś niezmienności.Podobnie jak w przypadku typów statycznych, jeśli nie pracujesz na dużej bazie kodu z zespołem inżynierów, który ją utrzymuje / rozszerza, wybór zastosowania niezmienności jest bardziej estetyczny niż praktyczny: jego zalety wciąż istnieją, ale mogą jeszcze nie przewyższać kosztów.
Ale tak jak w przypadku wszystkich przydatnych dyscyplin, przychodzi moment, w którym nie jest już opcjonalny. Jeśli chcę utrzymać zdrową wagę, dyscyplina dotycząca lodów może być opcjonalna. Ale jeśli chcę być wyczynowym sportowcem, mój wybór tego, czy jeść lody, zależy od mojego wyboru celów. Jeśli chcesz zmienić świat za pomocą oprogramowania, niezmienność może być częścią tego, czego potrzebujesz, aby uniknąć jego zawalenia pod własnym ciężarem.
źródło
Stworzyłem bibliotekę typu agnostic open source (MIT) dla mutowalnego (lub niezmiennego) stanu, która może zastąpić wszystkie te niezmienne pamięci, takie jak libs (redux, vuex itp.).
Niezmienne stany były dla mnie brzydkie, ponieważ było zbyt wiele do zrobienia (dużo akcji dla prostych operacji odczytu / zapisu), kod był mniej czytelny, a wydajność dużych zestawów danych nie do przyjęcia (ponowne renderowanie całego komponentu: /).
Za pomocą obserwatora stanu głębokiego mogę aktualizować tylko jeden węzeł z notacją kropkową i używać symboli wieloznacznych. Mogę również stworzyć historię stanu (cofnij / przywróć / podróż w czasie) zachowując tylko te konkretne wartości, które zostały zmienione
{path:value}
= mniejsze zużycie pamięci.Dzięki obserwatorowi stanu głębokiego mogę precyzyjnie dostroić rzeczy i mam kontrolę ziarna nad zachowaniem komponentów, dzięki czemu można znacznie poprawić wydajność. Kod jest bardziej czytelny, a refaktoryzacja jest znacznie łatwiejsza - wystarczy wyszukać i zastąpić ciągi ścieżek (nie trzeba zmieniać kodu / logiki).
źródło
Myślę, że głównym powodem niezmiennych obiektów jest utrzymanie stanu tego obiektu.
Załóżmy, że mamy obiekt o nazwie
arr
. Ten obiekt jest ważny, gdy wszystkie elementy są tej samej litery.jeśli
arr
staniemy się niezmiennym obiektem, będziemy mieć pewność, że arr jest zawsze w poprawnym stanie.źródło
arr
fillWithZ