Why is immutability so important (or necessary) in JavaScript?

I am currently working on the React JS and React Native platforms. Halfway I stumbled upon Immutable or the Immutable-JS library when I read about the Facebook Flux and Redux implementation.

The question is, why is immutability so important? What is wrong with mutating objects? Does this not simplify the situation?

As an example, let's look at a simple news reader application in which the home screen is a list of news headlines.

If I install, say, an array of objects with a value initially, I cannot manipulate it. This is what the principle of immutability says, right? (Correct me if I'm wrong.) But what if I have a new news item that needs to be updated? In the normal case, I could just add an object to the array. How can I achieve in this case? Delete the store and recreate it? Is adding an object to an array a less expensive operation?

+172
javascript immutability functional-programming reactjs
Dec 20 '15 at 20:00
source share
10 answers

I recently explored the same topic. I will do my best to answer your questions and try to share what I have learned so far.

The question is, why is immutability so important? What is wrong with mutating objects? Does this not simplify the situation?

Basically, it boils down to the fact that immutability increases predictability, performance (indirectly) and allows you to track mutations.

Predictability

A mutation hides changes that create (unexpected) side effects that can cause unpleasant errors. When you apply immutability, you can keep your application architecture and mental model simple, making it easy to analyze your application.

Performance

Even if adding values ​​to an immutable object means that you need to create a new instance into which you want to copy the existing values ​​and add new values ​​to a new object that is worth the memory, immutable objects can use structural sharing to reduce memory overhead.

All updates return new values, but internal structures are used together to significantly reduce memory usage (and GC overkill). This means that if you add a vector with 1000 elements, it does not actually create a new vector with a length of 1001 elements. Most likely, only a few small objects stand out inside.

You can read more about it here .

Mutation tracking

In addition to reducing memory usage, immutability allows you to optimize your application using reference- and equality of values. This makes it easy to see if anything has changed. For example, a state change in a reaction component. You can use shouldComponentUpdate to verify state identity by comparing state objects and preventing unnecessary rendering. You can read more about it here .

Additional resources:

If I install, say, an array of objects with a value initially. I can not manipulate this. This is what the principle of immutability says, right? (Correct me if I am wrong). But what if I have a new news item that needs to be updated? In the normal case, I could just add an object to the array. How can I achieve in this case? Delete the store and recreate it? Is adding an object to an array a less expensive operation?

Yes, it's right. If you don’t understand how to implement this in your application, I would recommend you see how Redux does it to familiarize yourself with the basic concepts, it helped me a lot.

I like using Redux as an example because it includes immutability. It has a single immutable state tree (called store ), where all state changes are explicit by sending actions that are processed by the reducer, which takes the previous state along with the mentioned actions (one at a time) and returns the next state of your application. You can read more about the basic principles here .

There is an excellent reduction course on egghead.io, where Dan Abramov , the author of the reduction, explains these principles as follows (I changed the code a bit to better fit the script):

 import React from 'react'; import ReactDOM from 'react-dom'; // Reducer. const news = (state=[], action) => { switch(action.type) { case 'ADD_NEWS_ITEM': { return [ ...state, action.newsItem ]; } default: { return state; } } }; // Store. const createStore = (reducer) => { let state; let listeners = []; const subscribe = (listener) => { listeners.push(listener); return () => { listeners = listeners.filter(cb => cb !== listener); }; }; const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach( cb => cb() ); }; dispatch({}); return { subscribe, getState, dispatch }; }; // Initialize store with reducer. const store = createStore(news); // Component. const News = React.createClass({ onAddNewsItem() { const { newsTitle } = this.refs; store.dispatch({ type: 'ADD_NEWS_ITEM', newsItem: { title: newsTitle.value } }); }, render() { const { news } = this.props; return ( <div> <input ref="newsTitle" /> <button onClick={ this.onAddNewsItem }>add</button> <ul> { news.map( ({ title }) => <li>{ title }</li>) } </ul> </div> ); } }); // Handler that will execute when the store dispatches. const render = () => { ReactDOM.render( <News news={ store.getState() } />, document.getElementById('news') ); }; // Entry point. store.subscribe(render); render(); 

In addition, these videos demonstrate in more detail how to achieve consistency for:

+171
Dec 20 '15 at 20:48
source share

The opposite view of immutability

Short answer: immutability is more a fashion trend than a necessity for JavaScript. There are a few narrow cases where this becomes useful (mostly React / Redux), although usually for the wrong reasons.

Long answer: read below.

Why is immutability so important (or necessary) in JavaScript?

Well, I'm glad you asked!

Some time ago, a very talented guy named Dan Abramov wrote a javascript state management library called Redux that uses pure functions and immutability. He also made some really cool videos that made the idea very easy to understand (and sell).

The time was perfect. The Angular novelty was disappearing, and the JavaScript world was ready to fixate on the latest innovations with the proper degree of steepness, and this library was not only innovative, but also perfect for React, which is being sold by another power station in Silicon Valley .

Sadly, fashion rules the world of JavaScript. Now Abramov is being proclaimed a demigod, and all of us, mere mortals, are forced to obey the Tao of Invariability ... does this make sense or not.

What is wrong with mutating objects?

Nothing!

In fact, programmers mutated objects forever ... as long as there were objects that needed to be mutated. 50+ years of application development, in other words.

And why complicate things? When you have a cat object and it dies, do you really need a second cat to track the changes? Most people will just say cat.isDead = true and cat.isDead = true with this.

Don't (mutating objects) make things simple?

YES! .. Of course it is!

Especially in JavaScript, which in practice is most useful for displaying some state that is supported elsewhere (for example, in a database).

What if I have a new news item that needs to be updated? ... How do I achieve this? Delete the store and recreate it? Is adding an object to an array a less expensive operation?

Well, you can follow the traditional approach and update the News object, so that your representation of this object in memory will change (and the representation displayed to the user, or one might hope so) ...

Or alternatively ...

You can try the "FP / Immutability" approach and add your changes to the News object in an array that tracks each historical change, so you can then iterate over the array and figure out what the correct state view should be (yuck!).

I'm trying to find out what is right here. Please enlighten me :)

Fashion comes and goes, buddy. There are many ways to skin a cat.

I'm sorry that you have to confuse the ever-changing set of programming paradigms. But hello, welcome to the club!

Now there are a few important points to remember regarding immutability, and you will get these throws at you with a feverish intensity that only naivety can show.

1) Consistency is exceptional because it avoids race conditions in multi-threaded environments .

Multithreaded environments (such as C ++, Java, and C #) are guilty of the practice of locking objects when more than one thread wants to change them. This has a bad effect on performance, but is better than an alternative to data corruption. And still not as good as making everything unchanged (praise to Lord Haskell!).

BUT Alas! In JavaScript, you always work with a single thread . Even web workers (each working in a separate context ). Since you cannot have a race condition associated with a thread inside the execution context (all these nice global variables and closures), the main thing in favor of immutability goes beyond.

(Having said that, there is the advantage of using pure functions in web workers, which is that you will not have any expectations about manipulating objects in the main thread.)

2) Consistency can (somehow) escape the race condition in the state of your application.

And this is the essence of the matter, most developers (React) will tell you that immutability and FP can somehow use this magic, which allows the state of your application to become predictable.

Of course, this does not mean that you can avoid competition in the database , to cope with this, you need to coordinate the actions of all users in all browsers, and for this you will need internal push technology, such as WebSockets (more on this below), which will Broadcast changes to everyone who runs the application.

This rather confusing statement simply means that the last values ​​assigned to the state object (defined by one user working in its browser) become predictable. Which is actually not progress at all. Because you could use the old mutated variable to track your state, and you would know that you are dealing with the latest information every time you access it, and this will work with anyone other than React / Redux.

What for? Since React is special ... and the state of the component is controlled through a chain of events that you cannot control, and you rely on not changing the state directly . This was remarkably handled in terms of PR, as React ads turned the flaw into a sexy fashion. In addition to fashion, I would prefer to see the immutability of what it is, that is, a tool to eliminate the gap when the framework you choose does not handle the state intuitively.

3) Racing conditions are categorically bad.

Well, they can be if you use React. But they are rare if you take other frames.

In addition, you usually have much bigger problems that you have to deal with ... Problems such as addiction hell. Like a bloated codebase. How your CSS is not loading. Like a slow build process or binding to a monolithic backend, which makes iteration almost impossible. Like inexperienced developers who do not understand what is happening and make a mess.

You know. Reality. But hey, who cares?

4) Immutability uses reference types to reduce the performance impact of tracking each state change.

Because, seriously, if you are going to copy things every time your state changes, you better make sure that you are smart about it.

5) Consistency allows you to undo things .

Because ... this is the number one feature the project manager will ask for, right?

6) Fixed state has many interesting features in conjunction with WebSockets

And last but not least, the accumulation of state deltas leads to compelling arguments in conjunction with WebSockets, which makes it easy to use state as a stream of immutable events ...

As soon as a penny falls on this concept (a state is a stream of events, not a rough set of records representing the latest point of view), an unchanging world becomes a magical place to live. A country of wonderful events and opportunities that go beyond time itself. And if everything is done correctly, this can definitely simplify the execution of real-time applications, you just broadcast the stream of events to everyone interested so that they can create their own idea of ​​the present and write their changes to the general stream.

But at some point you wake up and realize that all this miracle and magic do not come for free. Unlike your impatient colleagues, your stakeholders (yes, the people who pay you) care little about fashion and much about the money they pay to create a product that they can sell. And the bottom line is that it's harder to write immutable code and easier to crack, plus it makes little sense to have an immutable external interface if you do not have an internal interface to support it. When (and if!) You finally convince your stakeholders that you should publish and use events using push technology such as WebSockets, you discover how difficult it is to scale in production .




Now for some advice if you decide to accept it.

Choosing to write JavaScript using FP / Immutability is also a choice to make the codebase of your application bigger, harder and harder to manage. I would strongly advise limiting this approach to your Redux reducers ... if you don't know what you are doing. In other words: just keep it simple ™. In most cases, you will feel better. And wherever you are, I would focus on getting the benefits of immutable data passing through your (whole) application, rather than creating a purely functional interface, and that was it.

If you paid someone to draw you a horse, which one would you rather?

Now, if you are lucky enough that you can make choices in your work, then try to use your wisdom (or not) and do what is right, the person who pays you . You can base this on your own experience, your intuition, or what is happening around you (admittedly, if everyone uses React / Redux, then there is a good argument that it will be easier to find a resource to continue your work). Alternatively, you can try the Resume Driven Development or Hype Driven Development approaches. They may be more of your kind.

In short, I must say that for immutability it will make you fashionable with your peers, at least until the next hobby comes, and by this moment you will be happy to move on.




Now I added this as an article on my blog => Invariance in JavaScript: the opposite view . Feel free to answer there if you have strong feelings that you would like to take off your chest too;).

+102
Apr 10 '17 at 9:02 on
source share
Question: Why is immutability important? What is wrong with mutating objects? Doesn't that make things simple?

In fact, the opposite is true: variability complicates the situation, at least in the long run. Yes, it simplifies your initial coding, because you can just change things wherever you want, but when your program gets bigger, it becomes a problem - if the value has changed, what changed it?

When you make everything unchanged, it means that data can no longer be changed by surprise. You know for sure that if you pass a value to a function, it cannot be changed in that function.

Simply put: if you use immutable values, it’s very simple to talk about your code: everyone gets a unique * copy of your data, so he can’t work with it and break other parts of your code. Imagine how much easier it works in a multi-threaded environment!

Note 1: Depending on what you are doing, there is a potential execution cost for immutability, but things like Immutable.js are optimized as much as possible.

Note 2: In the unlikely event you were not sure that Immutable.js and ES6 const mean very different things.

In the normal case, I could just add an object to the array. How can I achieve in this case? Delete repository and recreate it? Does an object add an cheaper operation to the array? PS: If this example is not the right way to explain immutability, please let me know which is the right practical example.

Yes, your news example is great, and your reasoning is correct: you cannot just modify an existing list, so you need to create a new one:

 var originalItems = Immutable.List.of(1, 2, 3); var newItems = originalItems.push(4, 5, 6); 
+51
Dec 20 '15 at 20:13
source share

While the other answers are good to answer your question about a practical use case (from comments on other answers), let's go outside your working code for a moment and look at the ubiquitous answer right under your nose: git . What happens if every time you click commit, you overwrite the data in the repository?

Now we are faced with one of the problems that invariable collections face: bloating memory. Git is smart enough to not only make new copies of files every time you make changes, it just keeps track of the differences.

Although I know little about the internal workings of git, I can only assume that it uses a strategy similar to the library strategy you are referring to: structural sharing. Libraries use attempts or other trees under the hood to track only different nodes.

This strategy is also quite effective for data structures in memory, since there are well-known algorithms for working with trees that work in logarithmic time.

Another use case: let's say you want to cancel a button in your web application. With immutable representations of your data, implementing them is relatively trivial. But if you rely on a mutation, that means you need to worry about caching the state of the world and making atomic updates.

In short, there is a price to pay for invariable performance at runtime and a learning curve. But any experienced programmer will tell you that the debugging time is an order of magnitude longer than the time it takes to write the code. And a small performance hit at runtime is probably outweighed by state errors that users don't have to endure.

+35
Dec 21 '15 at 1:58
source share

Why is immutability so important (or necessary) in JavaScript?

Consistency can be tracked in different contexts, but the most important thing is to track it depending on the state of the application and the user interface of the application.

I will consider the Redux JavaScript template as a very fashionable and modern approach, and because you mentioned it.

For the user interface, we must make it predictable . This will be predictable if UI = f(application state) .

Applications (in JavaScript) change state using actions implemented using the reducer function .

The reducer function simply takes the action and the old state and returns the new state, keeping the old state unchanged.

 new state = r(current state, action) 

enter image description here

The advantage is that you move in time through states, since all state objects are saved, and you can visualize the application in any state, since UI = f(state)

This way you can undo / redo easily.




, , Git , Linux ( inode).

+6
02 . '17 11:01
source share

, ? ? ?

. , . ( ). , .

-, , , . . , - .

. . , . . .

- . , , , .

, . , , , . , .

, . , - , .

 const car = { brand: 'Ferrari' }; doSomething(car); console.log(car); // { brand: 'Fiat' } 

, , . doSomething , . , , . , : .

-

, , , . , . . . , .

, .

.

- https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310

+6
01 . '18 23:08
source share

Javascript , , . :

 class Foo { baz() { // .... } bar() { // .... } } const f = new Foo(); 

, baz() , bar() . ?

 f.baz(); f.bar(); // this is ok f.bar(); f.baz(); // this blows up 

, , . .

Foo , . , baz bar , .

+4
11 '17 14:35
source share

- . , 10+. . , . . , JS, . .

. gcc __attribute__((pure)) . , , . , . - 100+, , . 95% .

- (, ), . - , . , . .

. , - .

+3
17 . '18 9:54
source share

...

, . , , , , , , .

TL; DR

, , , , .

: "", .

: " Typescript? JavaScript?". . Consider the following scenario:

JavaScript/CSS/HTML, 5000 . - - Typescript-as-new-hotness , , . , , ..

, , Typescript?

Typescript : , , API-, , . Typescript : JavaScript , LoC, , .. , JavaScript , , , . . : , (, ).

, : ?

, JS- JS , Typescript , . , , .

:

- Foo Corp. 10 90000 LoC ( ) JavaScript/HTML/CSS , babel, webpack , , , ~ 20 , ~ 10 , , .. etc. etc.

, 5k LoC /, . , 6 , . , . Typescript, , , , (, , , , CI). , .

GOTO 1978 : C GOTO , , , , GOTO . .

, , , / , , : , .

, , , . , . , , , . , , , .

+3
12 . '18 13:49
source share

, pro - .

, arr . , .

 // this function will change the letter in all the array function fillWithZ(arr) { for (var i = 0; i < arr.length; ++i) { if (i === 4) // rare condition return arr; // some error here arr[i] = "Z"; } return arr; } console.log(fillWithZ(["A","A","A"])) // ok, valid state console.log(fillWithZ(["A","A","A","A","A","A"])) // bad, invalid state 

arr , , arr .

-one
22 . '17 16:57
source share



All Articles