Ich arbeite derzeit an React JS- und React Native- Frameworks. Auf halbem Weg stieß ich auf Immutability oder die Immutable-JS-Bibliothek , als ich über die Flux- und Redux-Implementierung von Facebook las.
Die Frage ist, warum Unveränderlichkeit so wichtig ist. Was ist falsch daran, Objekte zu mutieren? Macht es die Dinge nicht einfach?
Betrachten wir als Beispiel eine einfache Newsreader- App, deren Startbildschirm eine Listenansicht der Schlagzeilen ist.
Wenn ich ein Set sagt Array von Objekten mit einem Wert zunächst kann ich es nicht manipulieren. Das sagt das Unveränderlichkeitsprinzip, richtig? (Korrigieren Sie mich, wenn ich falsch liege.) Aber was ist, wenn ich ein neues News-Objekt habe, das aktualisiert werden muss? Im Normalfall hätte ich das Objekt einfach zum Array hinzufügen können. Wie erreiche ich in diesem Fall? Laden löschen und neu erstellen? Ist das Hinzufügen eines Objekts zum Array nicht eine kostengünstigere Operation?
Antworten:
Ich habe kürzlich das gleiche Thema untersucht. Ich werde mein Bestes tun, um Ihre Frage (n) zu beantworten und zu teilen, was ich bisher gelernt habe.
Grundsätzlich kommt es darauf an, dass Unveränderlichkeit die Vorhersagbarkeit und Leistung (indirekt) erhöht und die Verfolgung von Mutationen ermöglicht.
Vorhersagbarkeit
Mutation verbirgt Veränderungen, die (unerwartete) Nebenwirkungen hervorrufen, die böse Fehler verursachen können. Wenn Sie Unveränderlichkeit erzwingen, können Sie Ihre Anwendungsarchitektur und Ihr mentales Modell einfach halten, was es einfacher macht, über Ihre Anwendung nachzudenken.
Performance
Obwohl das Hinzufügen von Werten zu einem unveränderlichen Objekt bedeutet, dass eine neue Instanz erstellt werden muss, in der vorhandene Werte kopiert und neue Werte zum neuen Objekt hinzugefügt werden müssen, was Speicher kostet, können unveränderliche Objekte die strukturelle Freigabe verwenden, um den Speicher zu reduzieren Overhead.
Mehr dazu lesen Sie hier .
Mutationsverfolgung
Neben der reduzierten Speichernutzung können Sie durch Unveränderlichkeit Ihre Anwendung optimieren, indem Sie Referenz- und Wertegleichheit verwenden. Dies macht es wirklich einfach zu sehen, ob sich etwas geändert hat. Zum Beispiel eine Zustandsänderung in einer Reaktionskomponente. Sie können verwenden
shouldComponentUpdate
, um zu überprüfen, ob der Status identisch ist, indem Sie Statusobjekte vergleichen und unnötiges Rendern verhindern. Mehr dazu lesen Sie hier .Zusätzliche Ressourcen:
Ja das ist korrekt. Wenn Sie sich nicht sicher sind, wie Sie dies in Ihrer Anwendung implementieren sollen, würde ich Ihnen empfehlen, sich anzusehen, wie Redux dies tut, um sich mit den Kernkonzepten vertraut zu machen. Das hat mir sehr geholfen.
Ich verwende Redux gerne als Beispiel, weil es Unveränderlichkeit umfasst. Es hat einen einzelnen unveränderlichen Statusbaum (als bezeichnet
store
), in dem alle Statusänderungen explizit sind, indem Aktionen ausgelöst werden, die von einem Reduzierer verarbeitet werden, der den vorherigen Status zusammen mit diesen Aktionen akzeptiert (einzeln ) und den nächsten Status Ihrer Anwendung zurückgibt . Weitere Informationen zu den Grundprinzipien finden Sie hier .Auf egghead.io gibt es einen ausgezeichneten Redux-Kurs, in dem Dan Abramov , der Autor von Redux, diese Prinzipien wie folgt erklärt (ich habe den Code ein wenig modifiziert, um besser zum Szenario zu passen):
Außerdem zeigen diese Videos detaillierter, wie Unveränderlichkeit erreicht werden kann für:
quelle
const
geht es nicht um Unveränderlichkeit. Mathias Bynens hat einen großartigen Blog-Artikel darüber geschrieben.Eine konträre Sicht der Unveränderlichkeit
TL / DR: Unveränderlichkeit ist in JavaScript eher ein Modetrend als eine Notwendigkeit. Wenn Sie React verwenden, können einige verwirrende Entwurfsoptionen in der Statusverwaltung ordentlich umgangen werden. In den meisten anderen Situationen wird die damit verbundene Komplexität jedoch nicht ausreichend aufgewertet, da sie eher dazu dient , einen Lebenslauf aufzufüllen, als einen tatsächlichen Kundenbedarf zu decken.
Lange Antwort: lesen Sie unten.
Nun, ich bin froh, dass du gefragt hast!
Vor einiger Zeit schrieb ein sehr talentierter Mann namens Dan Abramov eine Javascript State Management Library namens Redux, die reine Funktionen und Unveränderlichkeit verwendet. Er hat auch einige wirklich coole Videos gemacht , die die Idee wirklich leicht zu verstehen (und zu verkaufen) machten.
Das Timing war perfekt. Die Neuheit von Angular verblasste und die JavaScript-Welt war bereit, sich auf das Neueste zu konzentrieren, das den richtigen Grad an Coolness aufwies. Diese Bibliothek war nicht nur innovativ, sondern passte auch perfekt zu React, das von einem anderen Silicon Valley-Kraftpaket verkauft wurde .
So traurig es auch sein mag, in der Welt von JavaScript herrscht Mode. Jetzt wird Abramov als Halbgott gefeiert und wir Sterblichen müssen uns alle dem Dao der Unveränderlichkeit unterwerfen ... Ob es Sinn macht oder nicht.
Nichts!
Tatsächlich haben Programmierer Objekte für ähm ... mutiert, solange es Objekte zum Mutieren gab. Mit anderen Worten: Über 50 Jahre Anwendungsentwicklung.
Und warum die Dinge komplizieren? Wenn Sie ein Objekt haben
cat
und es stirbt, brauchen Sie wirklich eine Sekundecat
, um die Änderung zu verfolgen? Die meisten Leute würden einfach sagencat.isDead = true
und damit fertig sein.JA! .. Natürlich tut es das!
Insbesondere in JavaScript, das in der Praxis am nützlichsten ist, um eine Ansicht eines Status zu rendern, der an anderer Stelle (wie in einer Datenbank) verwaltet wird.
Nun, Sie können den herkömmlichen Ansatz wählen und das
News
Objekt aktualisieren , sodass sich Ihre speicherinterne Darstellung dieses Objekts ändert (und die Ansicht, die dem Benutzer angezeigt wird, oder so würde man hoffen) ...Oder alternativ...
Sie können den sexy FP / Immutability-Ansatz ausprobieren und Ihre Änderungen am
News
Objekt einem Array hinzufügen, das jede historische Änderung verfolgt, sodass Sie das Array durchlaufen und herausfinden können, wie die korrekte Zustandsdarstellung aussehen sollte (puh!).Mode kommt und geht Kumpel. Es gibt viele Möglichkeiten, eine Katze zu häuten.
Es tut mir leid, dass Sie die Verwirrung eines sich ständig ändernden Satzes von Programmierparadigmen ertragen müssen. Aber hey, willkommen im Club!
Nun ein paar wichtige Punkte, die Sie in Bezug auf Unveränderlichkeit beachten sollten, und Sie werden diese mit der fieberhaften Intensität auf Sie werfen, die nur Naivität aufbringen kann.
1) Unveränderlichkeit ist fantastisch, um Rennbedingungen in Umgebungen mit mehreren Threads zu vermeiden .
Multithread-Umgebungen (wie C ++, Java und C #) haben sich der Praxis schuldig gemacht, Objekte zu sperren, wenn mehr als ein Thread sie ändern möchte. Dies ist schlecht für die Leistung, aber besser als die Alternative der Datenbeschädigung. Und doch nicht so gut wie alles unveränderlich zu machen (Herr preise Haskell!).
ABER leider! In JavaScript arbeiten Sie immer mit einem einzelnen Thread . Sogar Web-Worker (jeder läuft in einem separaten Kontext ). Da Sie in Ihrem Ausführungskontext keine Thread-bezogenen Race-Bedingungen haben können (all diese schönen globalen Variablen und Abschlüsse), geht der Hauptpunkt zugunsten der Unveränderlichkeit aus dem Fenster.
(Having said that, es ist ein Vorteil der Verwendung von reinen Funktionen in Web - Arbeitern, die sind , dass Sie mit Objekten auf dem Haupt - Thread keine Erwartungen über das Hantieren haben werden.)
2) Unveränderlichkeit kann (irgendwie) Rennbedingungen im Zustand Ihrer App vermeiden.
Und hier ist der eigentliche Kern der Sache: Die meisten (React-) Entwickler werden Ihnen sagen, dass Unveränderlichkeit und FP diese Magie irgendwie anwenden können, die es ermöglicht, dass der Status Ihrer Anwendung vorhersehbar wird.
Dies bedeutet natürlich nicht, dass Sie Rennbedingungen in der Datenbank vermeiden können. Um diese zu erreichen, müssten Sie alle Benutzer in allen Browsern koordinieren. Dazu benötigen Sie eine Back-End-Push-Technologie wie WebSockets ( mehr dazu weiter unten), das Änderungen an alle überträgt, die die App ausführen.
Es bedeutet auch nicht, dass JavaScript ein inhärentes Problem aufweist, bei dem Ihr Anwendungsstatus unveränderlich sein muss, um vorhersehbar zu werden. Jeder Entwickler, der Front-End-Anwendungen vor React codiert hat, würde Ihnen dies mitteilen.
Diese ziemlich verwirrende Behauptung bedeutet einfach, dass mit React Ihr Bewerbungsstatus anfälliger für Rennbedingungen wird , aber dass Unveränderlichkeit es Ihnen ermöglicht, diesen Schmerz zu lindern. Warum? Da React etwas Besonderes ist, wurde es als hochoptimierte Rendering-Bibliothek konzipiert, wobei die kohärente Statusverwaltung an zweiter Stelle steht. Daher wird der Komponentenstatus über eine asynchrone Ereigniskette (auch als "Einweg-Datenbindung" bezeichnet) verwaltet, auf die Sie keinen Einfluss haben über und verlassen Sie sich darauf, dass Sie daran denken, den Zustand nicht direkt zu mutieren ...
In diesem Zusammenhang ist leicht zu erkennen, dass die Notwendigkeit der Unveränderlichkeit wenig mit JavaScript und viel mit den Rennbedingungen in React zu tun hat: Wenn Ihre Anwendung eine Reihe von voneinander abhängigen Änderungen enthält und keine einfache Möglichkeit besteht, herauszufinden, was Ihr Zustand befindet sich derzeit in einem Zustand, in dem Sie verwirrt sein werden. Daher ist es durchaus sinnvoll, die Unveränderlichkeit zu verwenden, um jede historische Veränderung zu verfolgen .
3) Die Rennbedingungen sind kategorisch schlecht.
Nun, sie könnten sein, wenn Sie React verwenden. Sie sind jedoch selten, wenn Sie ein anderes Framework auswählen.
Außerdem haben Sie normalerweise weitaus größere Probleme … Probleme wie die Hölle der Abhängigkeit. Wie eine aufgeblähte Codebasis. Als würde Ihr CSS nicht geladen. Wie ein langsamer Build-Prozess oder das Festhalten an einem monolithischen Back-End, das das Iterieren fast unmöglich macht. Wie unerfahrene Entwickler, die nicht verstehen, was los ist, und die Dinge durcheinander bringen.
Wissen Sie. Wirklichkeit. Aber hey, wen interessiert das?
4) Bei der Unveränderlichkeit werden Referenztypen verwendet , um die Auswirkungen der Verfolgung jeder Zustandsänderung auf die Leistung zu verringern.
Denn im Ernst, wenn Sie jedes Mal, wenn sich Ihr Status ändert, etwas kopieren, sollten Sie besser sicherstellen, dass Sie klug sind.
5) Unveränderlichkeit ermöglicht es Ihnen, Sachen rückgängig zu machen .
Weil ähm ... das ist die Nummer eins, nach der Ihr Projektmanager fragen wird, oder?
6) Unveränderlicher Zustand hat in Kombination mit WebSockets viel cooles Potenzial
Last but not least ist die Anhäufung von Zustandsdeltas in Kombination mit WebSockets ein ziemlich überzeugender Fall, der einen einfachen Verbrauch des Zustands als Fluss unveränderlicher Ereignisse ermöglicht ...
Sobald der Penny auf dieses Konzept fällt (Zustand ist ein Fluss von Ereignissen - und nicht eine grobe Reihe von Aufzeichnungen, die die neueste Sichtweise darstellen), wird die unveränderliche Welt zu einem magischen Ort zum Wohnen. Ein Land voller Wunder und Möglichkeiten, die über die Zeit hinausgehen . Und wenn es richtig getan kann auf jeden Fall machen Echtzeit - EASI apps er zu erreichen, Sie übertragen nur den Fluss der Ereignisse an alle Interessierten , so können sie ihre eigene Darstellung bauen der Gegenwart und schreiben ihre eigenen Änderungen in den kommunalen Fluss zurück.
Aber irgendwann wachst du auf und merkst, dass all diese Wunder und Magie nicht umsonst sind. Im Gegensatz zu Ihren eifrigen Kollegen kümmern sich Ihre Stakeholder (ja, die Leute, die Sie bezahlen) wenig um Philosophie oder Mode und viel um das Geld, das sie bezahlen, um ein Produkt zu bauen, das sie verkaufen können. Und das Fazit ist, dass es schwieriger ist, unveränderlichen Code zu schreiben und ihn leichter zu brechen. Außerdem macht es wenig Sinn, ein unveränderliches Front-End zu haben, wenn Sie kein Back-End haben, das ihn unterstützt. Wenn Sie (und wenn!) Ihre Stakeholder endgültig davon überzeugen, dass Sie Ereignisse über eine Push-Technologie wie WebSockets veröffentlichen und nutzen sollten, finden Sie heraus , wie schwierig es ist, die Produktion zu skalieren .
Nun zu einigen Ratschlägen, sollten Sie sich dafür entscheiden, diese zu akzeptieren.
Die Wahl, JavaScript mit FP / Immutability zu schreiben, ist auch eine Wahl, um Ihre Anwendungscodebasis größer, komplexer und schwieriger zu verwalten. Ich würde mich nachdrücklich dafür aussprechen, diesen Ansatz auf Ihre Redux-Reduzierungen zu beschränken, es sei denn, Sie wissen, was Sie tun ... Und wenn Sie die Unveränderlichkeit unabhängig davon verwenden, wenden Sie den unveränderlichen Status auf Ihren gesamten Anwendungsstapel an und nicht nur auf den clientseitig, da Ihnen sonst der wahre Wert fehlt.
Wenn Sie das Glück haben, Entscheidungen in Ihrer Arbeit treffen zu können, versuchen Sie, Ihre Weisheit zu nutzen (oder nicht) und tun Sie, was von der Person, die Sie bezahlt, richtig ist . Sie können dies auf Ihre Erfahrung, Ihren Bauch oder das, was um Sie herum vor sich geht, stützen (zugegebenermaßen, wenn jeder React / Redux verwendet, gibt es ein gültiges Argument dafür, dass es einfacher ist, eine Ressource zu finden, um Ihre Arbeit fortzusetzen). Sie können entweder Resume Driven Development- oder Hype Driven Development- Ansätze ausprobieren . Sie könnten eher deine Art von Dingen sein.
Kurz gesagt, die Sache, die für Unveränderlichkeit gesagt werden muss, ist, dass es Sie mit Ihren Kollegen in Mode bringt , zumindest bis der nächste Wahnsinn kommt, und an diesem Punkt werden Sie froh sein, weiterzumachen.
Nach dieser Selbsttherapiesitzung möchte ich darauf hinweisen, dass ich dies als Artikel in meinem Blog hinzugefügt habe => Unveränderlichkeit in JavaScript: Eine konträre Ansicht . Fühlen Sie sich frei, dort zu antworten, wenn Sie starke Gefühle haben, die Sie auch von Ihrer Brust bekommen möchten;).
quelle
"hyped" !== "bad"
Eigentlich ist das Gegenteil der Fall: Veränderlichkeit macht die Dinge zumindest auf lange Sicht komplizierter. Ja, es erleichtert Ihnen die anfängliche Codierung, da Sie die Dinge einfach ändern können, wo immer Sie möchten, aber wenn Ihr Programm größer wird, wird es zu einem Problem - wenn sich ein Wert geändert hat, was hat es geändert?
Wenn Sie alles unveränderlich machen, können Daten nicht mehr überraschend geändert werden. Sie wissen mit Sicherheit, dass ein Wert, der an eine Funktion übergeben wird, in dieser Funktion nicht geändert werden kann.
Einfach ausgedrückt: Wenn Sie unveränderliche Werte verwenden, ist es sehr einfach, über Ihren Code nachzudenken: Jeder erhält eine eindeutige * Kopie Ihrer Daten, sodass er nicht damit herumfummeln und andere Teile Ihres Codes beschädigen kann. Stellen Sie sich vor, wie viel einfacher dies das Arbeiten in einer Umgebung mit mehreren Threads macht!
Hinweis 1: Die Unveränderlichkeit verursacht potenzielle Leistungskosten, je nachdem, was Sie tun. Dinge wie Immutable.js werden jedoch so gut wie möglich optimiert.
Hinweis 2: In dem unwahrscheinlichen Fall, dass Sie sich nicht sicher waren,
const
bedeuten Immutable.js und ES6 sehr unterschiedliche Dinge.Ja, Ihr Nachrichtenbeispiel ist vollkommen gut und Ihre Argumentation ist genau richtig: Sie können Ihre vorhandene Liste nicht einfach ändern, sondern müssen eine neue erstellen:
quelle
Imagine how much easier this makes working in a multi-threaded environment!
-> Ok für andere Sprachen, aber dies ist kein Vorteil in Single-Threaded-JavaScript.Obwohl die anderen Antworten in Ordnung sind, können Sie zur Beantwortung Ihrer Frage zu einem praktischen Anwendungsfall (aus den Kommentaren zu den anderen Antworten) für eine Minute aus Ihrem laufenden Code herausgehen und sich die allgegenwärtige Antwort direkt unter Ihrer Nase ansehen: git . Was würde passieren, wenn Sie jedes Mal, wenn Sie ein Commit drücken, die Daten im Repository überschreiben würden?
Jetzt haben wir eines der Probleme, mit denen unveränderliche Sammlungen konfrontiert sind: das Aufblähen des Gedächtnisses. Git ist intelligent genug, um nicht bei jeder Änderung einfach neue Kopien von Dateien zu erstellen, sondern einfach die Unterschiede zu verfolgen .
Obwohl ich nicht viel über das Innenleben von Git weiß, kann ich nur davon ausgehen, dass es eine ähnliche Strategie verwendet wie die Bibliotheken, auf die Sie verweisen: strukturelles Teilen. Unter der Haube verwenden die Bibliotheken Versuche oder andere Bäume, um nur die Knoten zu verfolgen, die sich unterscheiden.
Diese Strategie ist auch für speicherinterne Datenstrukturen einigermaßen leistungsfähig, da es bekannte Baumoperationsalgorithmen gibt, die in logarithmischer Zeit arbeiten.
Ein weiterer Anwendungsfall: Angenommen, Sie möchten eine Schaltfläche zum Rückgängigmachen in Ihrer Webanwendung. Bei unveränderlichen Darstellungen Ihrer Daten ist die Implementierung dieser Daten relativ trivial. Wenn Sie sich jedoch auf Mutationen verlassen, müssen Sie sich darum kümmern, den Zustand der Welt zwischenzuspeichern und atomare Aktualisierungen vorzunehmen.
Kurz gesagt, es gibt einen Preis für die Unveränderlichkeit der Laufzeitleistung und der Lernkurve. Jeder erfahrene Programmierer wird Ihnen jedoch sagen, dass die Debugging-Zeit die Code-Schreibzeit um eine Größenordnung überwiegt. Und die leichte Beeinträchtigung der Laufzeitleistung wird wahrscheinlich durch die staatlichen Fehler aufgewogen, die Ihre Benutzer nicht ertragen müssen.
quelle
Über Veränderlichkeit
Aus technischer Sicht ist an der Veränderlichkeit nichts falsch. Es ist schnell, es verwendet den Speicher wieder. Entwickler sind von Anfang an daran gewöhnt (wie ich mich erinnere). Das Problem besteht in der Verwendung von Veränderlichkeit und Problemen, die diese Verwendung mit sich bringen kann.
Wenn das Objekt mit nichts geteilt wird, beispielsweise im Umfang der Funktion vorhanden ist und nicht nach außen zeigt, ist es schwierig, Vorteile in der Unveränderlichkeit zu erkennen. In diesem Fall macht es wirklich keinen Sinn, unveränderlich zu sein. Das Gefühl der Unveränderlichkeit beginnt, wenn etwas geteilt wird.
Mutabilitätskopfschmerz
Eine veränderbare gemeinsame Struktur kann leicht viele Fallstricke verursachen. Jede Änderung an einem Teil des Codes mit Zugriff auf die Referenz wirkt sich auf andere Teile mit Sichtbarkeit dieser Referenz aus. Ein solcher Aufprall verbindet alle Teile miteinander, auch wenn ihnen unterschiedliche Module nicht bekannt sein sollten. Eine Mutation in einer Funktion kann einen völlig anderen Teil der App zum Absturz bringen. So etwas ist eine schlimme Nebenwirkung.
Als nächstes ist das Problem mit der Mutation häufig ein beschädigter Zustand. Ein beschädigter Zustand kann auftreten, wenn das Mutationsverfahren in der Mitte fehlschlägt und einige Felder geändert wurden und andere nicht.
Darüber hinaus ist es bei Mutationen schwierig, die Änderung zu verfolgen. Eine einfache Referenzprüfung zeigt keinen Unterschied. Um zu wissen, was sich geändert hat, muss eine gründliche Prüfung durchgeführt werden. Um die Änderung zu überwachen, muss auch ein beobachtbares Muster eingeführt werden.
Schließlich ist die Mutation der Grund für das Vertrauensdefizit. Wie Sie sicher sein können, dass eine Struktur einen gewünschten Wert hat, wenn sie mutiert werden kann.
Wie das obige Beispiel zeigt, kann das Übergeben einer veränderlichen Struktur immer mit einer anderen Struktur abgeschlossen werden. Die Funktion doSomething mutiert das von außen angegebene Attribut. Kein Vertrauen in den Code, Sie wissen wirklich nicht, was Sie haben und was Sie haben werden. All diese Probleme treten auf, weil: Veränderbare Strukturen Zeiger auf den Speicher darstellen.
Bei der Unveränderlichkeit geht es um Werte
Unveränderlichkeit bedeutet, dass Änderungen nicht an demselben Objekt und derselben Struktur vorgenommen werden, sondern dass Änderungen in neuen Objekten dargestellt werden. Und das liegt daran, dass die Referenz nicht nur den Speicherzeiger darstellt. Jede Änderung schafft neuen Wert und berührt den alten nicht. Solche klaren Regeln geben das Vertrauen und die Vorhersehbarkeit des Codes zurück. Funktionen sind sicher zu verwenden, da sie anstelle von Mutationen eigene Versionen mit eigenen Werten behandeln.
Die Verwendung von Werten anstelle von Speichercontainern gibt die Gewissheit, dass jedes Objekt einen bestimmten unveränderlichen Wert darstellt und sicher verwendet werden kann.
Unveränderliche Strukturen repräsentieren Werte.
Ich beschäftige mich noch mehr mit dem Thema in einem mittleren Artikel - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310
quelle
Die Unveränderlichkeit kann in verschiedenen Kontexten verfolgt werden. Am wichtigsten ist jedoch, sie anhand des Anwendungsstatus und der Benutzeroberfläche der Anwendung zu verfolgen.
Ich werde das JavaScript-Redux-Muster als sehr trendigen und modernen Ansatz betrachten und weil Sie das erwähnt haben.
Für die Benutzeroberfläche müssen wir sie vorhersehbar machen . Es wird vorhersehbar sein, ob
UI = f(application state)
.Anwendungen (in JavaScript) ändern den Status über Aktionen, die mit der Reduzierungsfunktion implementiert werden .
Die Reduzierungsfunktion übernimmt einfach die Aktion und den alten Zustand und gibt den neuen Zustand zurück, wobei der alte Zustand intakt bleibt.
Der Vorteil ist: Sie reisen zeitlich durch die Status, da alle Statusobjekte gespeichert sind, und Sie können die App seitdem in jedem Status rendern
UI = f(state)
So können Sie einfach rückgängig machen / wiederholen.
Das Erstellen all dieser Zustände kann immer noch speichereffizient sein, eine Analogie zu Git ist großartig, und wir haben die ähnliche Analogie unter Linux mit symbolischen Links (basierend auf den Inodes).
quelle
Ein weiterer Vorteil der Unveränderlichkeit in Javascript besteht darin, dass die zeitliche Kopplung verringert wird, was im Allgemeinen erhebliche Vorteile für das Design hat. Betrachten Sie die Schnittstelle eines Objekts mit zwei Methoden:
Es kann vorkommen, dass ein Aufruf von
baz()
erforderlich ist, um das Objekt in einen gültigen Zustand zu versetzen,bar()
damit ein Aufruf ordnungsgemäß funktioniert. Aber woher weißt du das?Um dies herauszufinden, müssen Sie die Interna der Klasse überprüfen, da dies bei der Untersuchung der öffentlichen Schnittstelle nicht sofort ersichtlich ist. Dieses Problem kann in einer großen Codebasis mit vielen veränderlichen Zuständen und Klassen explodieren.
Wenn
Foo
unveränderlich, ist dies kein Problem mehr. Es ist sicher anzunehmen, dass wir anrufen könnenbaz
oderbar
in beliebiger Reihenfolge, da sich der innere Zustand der Klasse nicht ändern kann.quelle
Es war einmal ein Problem mit der Datensynchronisation zwischen Threads. Dieses Problem war ein großer Schmerz, es gab mehr als 10 Lösungen. Einige Leute versuchten es radikal zu lösen. Es war ein Ort, an dem funktionale Programmierung geboren wurde. Es ist wie im Marxismus. Ich konnte nicht verstehen, wie Dan Abramov diese Idee an JS verkauft hat, weil sie Single-Threaded ist. Er ist ein Genie.
Ich kann ein kleines Beispiel geben.
__attribute__((pure))
In gcc gibt es ein Attribut . Compiler versuchen zu lösen, ob Ihre Funktion rein ist oder nicht, wenn Sie sie nicht speziell deklarieren. Ihre Funktion kann rein sein, auch wenn Ihr Zustand veränderlich ist. Unveränderlichkeit ist nur eine von über 100 Möglichkeiten, um sicherzustellen, dass Ihre Funktion rein ist. Tatsächlich sind 95% Ihrer Funktionen rein.Sie sollten keine Einschränkungen (wie Unveränderlichkeit) anwenden, wenn Sie tatsächlich keinen ernsthaften Grund haben. Wenn Sie einen Status "rückgängig machen" möchten, können Sie Transaktionen erstellen. Wenn Sie die Kommunikation vereinfachen möchten, können Sie Ereignisse mit unveränderlichen Daten senden. Es liegt an dir.
Ich schreibe diese Nachricht aus der Postmarxismus-Republik. Ich bin sicher, dass die Radikalisierung einer Idee ein falscher Weg ist.
quelle
Eine andere Einstellung ...
Meine andere Antwort befasst sich mit der Frage von einem sehr praktischen Standpunkt aus, und ich mag sie immer noch. Ich habe beschlossen, dies als eine andere Antwort und nicht als Nachtrag zu dieser hinzuzufügen, da es eine langweilige philosophische Parole ist, die hoffentlich auch die Frage beantwortet, aber nicht wirklich zu meiner bestehenden Antwort passt.
TL; DR
Selbst in kleinen Projekten kann Unveränderlichkeit nützlich sein, aber nehmen Sie nicht an, dass sie für Sie bestimmt ist, weil sie existiert.
Viel, viel länger antworten
HINWEIS: Für die Zwecke dieser Antwort verwende ich das Wort "Disziplin", um Selbstverleugnung für einen gewissen Nutzen zu bedeuten.
Dies ähnelt in seiner Form einer anderen Frage: "Soll ich Typescript verwenden? Warum sind Typen in JavaScript so wichtig?". Es hat auch eine ähnliche Antwort. Stellen Sie sich das folgende Szenario vor:
Nun haben Sie die Wahl, wechseln Sie zu Typescript?
Typescript bietet einige überzeugende Vorteile: Intellisense, frühzeitiges Erkennen von Fehlern, Festlegen Ihrer APIs im Voraus, einfache Behebung von Problemen, wenn das Refactoring sie unterbricht, weniger Tests. Typoskript hat auch einige Kosten: Bestimmte sehr natürliche und korrekte JavaScript-Redewendungen können in seinem nicht besonders leistungsfähigen Typsystem schwierig zu modellieren sein, Anmerkungen erhöhen die LoC, Zeit und Aufwand für das Umschreiben der vorhandenen Codebasis, zusätzliche Schritte in der Build-Pipeline usw. Grundsätzlich wird eine Teilmenge möglicher korrekter JavaScript-Programme herausgearbeitet, um das Versprechen zu erhalten, dass Ihr Code mit größerer Wahrscheinlichkeit korrekt ist. Es ist willkürlich restriktiv. Das ist der springende Punkt: Sie erzwingen eine Disziplin, die Sie einschränkt (hoffentlich, indem Sie sich in den Fuß schießen).
Zurück zu der Frage, die im Zusammenhang mit dem obigen Absatz umformuliert wurde: Lohnt es sich ?
In dem beschriebenen Szenario würde ich behaupten, dass die Wahl der Verwendung von Typescript eher ästhetisch als praktisch ist, wenn Sie mit einer kleinen bis mittelmäßigen JS-Codebasis sehr vertraut sind. Und das ist in Ordnung , an der Ästhetik ist nichts auszusetzen , sie ist einfach nicht unbedingt überzeugend.
Szenario B:
Damals, als du ein 5k LoC-Typ / Mädchen warst, war es einfach nicht so wichtig. Sogar die Dokumentation war keine so große Sache, selbst wenn man nach 6 Monaten auf einen bestimmten Teil des Codes zurückkam, konnte man es leicht genug herausfinden. Aber jetzt ist Disziplin nicht nur nett, sondern notwendig . Dass Disziplin nicht beinhalten Typoskript, sondern wird wahrscheinlich irgendeine Form von statischen Analyse sowie alle anderen Formen der Codierung Disziplin (Dokumentation, Styleguide, Build - Skripte, Regressionstests, CI) beinhalten. Disziplin ist kein Luxus mehr , sondern eine Notwendigkeit .
All dies galt
GOTO
1978: Ihr versautes kleines Blackjack-Spiel in C konnteGOTO
s- und Spaghetti-Logik verwenden, und es war einfach keine so große Sache, Ihr eigenes Abenteuer zu wählen, aber als die Programme größer wurden und Ein ehrgeizigerer, undisziplinierterer EinsatzGOTO
konnte nicht aufrechterhalten werden. Und das alles gilt heute für die Unveränderlichkeit.Genau wie bei statischen Typen ist die Wahl der Unveränderlichkeit eher ästhetisch als praktisch, wenn Sie nicht an einer großen Codebasis mit einem Team von Ingenieuren arbeiten, die diese warten / erweitern. Die Vorteile sind immer noch vorhanden, überwiegen jedoch möglicherweise noch nicht die Kosten.
Aber wie bei allen nützlichen Disziplinen kommt ein Punkt, an dem dies nicht mehr optional ist. Wenn ich ein gesundes Gewicht halten möchte, kann Disziplin mit Eis optional sein. Wenn ich jedoch ein Leistungssportler sein möchte, hängt meine Entscheidung, ob ich Eis essen möchte oder nicht, von meiner Wahl der Ziele ab. Wenn Sie die Welt mit Software verändern möchten, ist Unveränderlichkeit möglicherweise Teil dessen, was Sie benötigen, um zu verhindern, dass sie unter ihrem eigenen Gewicht zusammenbricht.
quelle
Ich habe eine Framework-agnostische Open-Source-Bibliothek (MIT) für den veränderlichen (oder unveränderlichen) Zustand erstellt, die alle unveränderlichen Speicher wie Bibliotheken (Redux, Vuex usw.) ersetzen kann.
Unveränderliche Zustände waren für mich hässlich, weil zu viel Arbeit zu erledigen war (viele Aktionen für einfache Lese- / Schreibvorgänge), Code weniger lesbar war und die Leistung für große Datenmengen nicht akzeptabel war (erneutes Rendern der gesamten Komponente: /).
Mit Deep-State-Observer kann ich nur einen Knoten mit Punktnotation aktualisieren und Platzhalter verwenden. Ich kann auch einen Statusverlauf erstellen (Rückgängig / Wiederherstellen / Zeitreise), wobei nur die konkreten Werte beibehalten werden, die geändert wurden
{path:value}
= weniger Speicherbedarf.Mit Deep-State-Observer kann ich Feinabstimmungen vornehmen und habe die Kontrolle über das Verhalten der Komponenten, sodass die Leistung drastisch verbessert werden kann. Code ist besser lesbar und Refactoring ist viel einfacher - suchen und ersetzen Sie einfach Pfadzeichenfolgen (Code / Logik müssen nicht geändert werden).
quelle
Ich denke, der Hauptgrund für unveränderliche Objekte ist, den Zustand des Objekts gültig zu halten.
Angenommen, wir haben ein Objekt namens
arr
. Dieses Objekt ist gültig, wenn alle Elemente denselben Buchstaben haben.Wenn es sich
arr
um ein unveränderliches Objekt handelt, sind wir sicher, dass arr immer in einem gültigen Zustand ist.quelle
arr
wird jedes Mal mutiert, wenn Sie anrufenfillWithZ