为什么不可变性在 JavaScript 中如此重要(或需要)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34385243/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:15:02  来源:igfitidea点击:

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

javascriptreactjsfunctional-programmingimmutabilityimmutable.js

提问by bozzmob

I am currently working on React JSand React Nativeframeworks. On the half way road I came across Immutability or the Immutable-JS library, when I was reading about Facebook's Flux and Redux implementation.

我目前正在研究React JSReact Native框架。在半路上,当我阅读 Facebook 的 Flux 和 Redux 实现时,我遇到了 Immutability 或Immutable-JS 库

The question is, why is immutability so important? What is wrong in mutating objects? Doesn't it make things simple?

问题是,为什么不变性如此重要?改变对象有什么问题?这不是让事情变得简单吗?

Giving an example, let us consider a simple News readerapp with the opening screen being a list view of news headlines.

举个例子,让我们考虑一个简单的新闻阅读器应用程序,它的打开屏幕是新闻标题的列表视图。

If I set say an array of objectswith a value initiallyI can't manipulate it. That's what immutability principle says, right? (Correct me if I am wrong.) But, what if I have a new News object that has to be updated? In usual case, I could have just added the object to the array. How do I achieve in this case? Delete the store and recreate it? Isn't adding an object to the array a less expensive operation?

如果我设置了一个初始的对象数组,我将无法操作它。这就是不变性原则所说的,对吧?(如果我错了,请纠正我。)但是,如果我有一个新的 News 对象需要更新怎么办?通常情况下,我可以将对象添加到数组中。在这种情况下我如何实现?删除商店并重新创建它?将一个对象添加到数组中不是一个成本较低的操作吗?

采纳答案by danillouz

I have recently been researching the same topic. I'll do my best to answer your question(s) and try to share what I have learned so far.

我最近一直在研究同一主题。我会尽我所能回答你的问题,并尝试分享我到目前为止学到的东西。

The question is, why is immutability so important? What is wrong in mutating objects? Doesn't it make things simple?

问题是,为什么不变性如此重要?改变对象有什么问题?这不是让事情变得简单吗?

Basically it comes down to the fact that immutability increases predictability, performance (indirectly) and allows for mutation tracking.

基本上它归结为不变性增加了可预测性、性能(间接)并允许突变跟踪的事实。

Predictability

可预测性

Mutation hides change, which create (unexpected) side effects, which can cause nasty bugs. When you enforce immutability you can keep your application architecture and mental model simple, which makes it easier to reason about your application.

突变隐藏了变化,这会产生(意外的)副作用,这可能会导致令人讨厌的错误。当您强制实施不变性时,您可以保持应用程序架构和心理模型简单,从而更容易推理您的应用程序。

Performance

表现

Even though adding values to an immutable Object means that a new instance needs to be created where existing values need to be copied and new values need to be added to the new Object which cost memory, immutable Objects can make use of structural sharing to reduce memory overhead.

尽管向不可变对象添加值意味着需要在需要复制现有值的地方创建一个新实例,并且需要将新值添加到新对象中,这会消耗内存,但不可变对象可以利用结构共享来减少内存高架。

All updates return new values, but internally structures are shared to drastically reduce memory usage (and GC thrashing). This means that if you append to a vector with 1000 elements, it does not actually create a new vector 1001-elements long. Most likely, internally only a few small objects are allocated.

所有更新都返回新值,但共享内部结构以显着减少内存使用(和 GC 抖动)。这意味着如果你附加到一个有 1000 个元素的向量,它实际上不会创建一个新的 1001 个元素长的向量。最有可能的是,内部只分配了几个小对象。

You can read more about this here.

您可以在此处阅读更多相关信息。

Mutation Tracking

突变追踪

Besides reduced memory usage, immutability allows you to optimize your application by making use of reference- and value equality. This makes it really easy to see if anything has changed. For example a state change in a react component. You can use shouldComponentUpdateto check if the state is identical by comparing state Objects and prevent unnecessary rendering. You can read more about this here.

除了减少内存使用之外,不变性还允许您通过使用引用和值相等来优化您的应用程序。这使得查看是否有任何更改变得非常容易。例如,反应组件中的状态变化。您可以shouldComponentUpdate通过比较状态对象来检查状态是否相同,并防止不必要的渲染。您可以在此处阅读更多相关信息。

Additional resources:

其他资源:

If I set say an array of objects with a value initially. I can't manipulate it. That's what immutability principle says, right?(Correct me if I am wrong). But, what if I have a new News object that has to be updated? In usual case, I could have just added the object to the array. How do I achieve in this case? Delete the store & recreate it? Isn't adding an object to the array a less expensive operation?

如果我设置了一个初始值的对象数组。我无法操纵它。这就是不变性原则所说的,对吗?(如果我错了,请纠正我)。但是,如果我有一个新的 News 对象需要更新怎么办?通常情况下,我可以将对象添加到数组中。在这种情况下我如何实现?删除商店并重新创建它?将一个对象添加到数组中不是一个成本较低的操作吗?

Yes this is correct. If you're confused on how to implement this in your application I would recommend you to look at how reduxdoes this to get familiar with the core concepts, it helped me a lot.

是的,这是正确的。如果您对如何在应用程序中实现这一点感到困惑,我建议您查看redux如何做到这一点以熟悉核心概念,它对我有很大帮助。

I like to use Redux as an example because it embraces immutability. It has a single immutable state tree (referred to as store) where all state changes are explicit by dispatching actions which are processed by a reducer that accepts the previous state together with said actions (one at a time) and returns the next state of your application. You can read more about it's core principles here.

我喜欢用 Redux 作为例子,因为它包含不变性。它有一个单一的不可变状态树(称为store),其中所有状态更改都是通过分派由 reducer 处理的操作来显式更改的,reducer 接受前一个状态和所述操作(一次一个)并返回应用程序的下一个状态. 您可以在此处阅读有关其核心原则的更多信息。

There is an excellent redux course on egghead.iowhere Dan Abramov, the author of redux, explains these principles as follows (I modified the code a bit to better fit the scenario):

egghead.io上有一个很棒的 redux 课程,其中redux 的作者Dan Abramov解释了这些原则如下(我稍微修改了代码以更好地适应场景):

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();

Also, these videos demonstrate in further detail how to achieve immutability for:

此外,这些视频更详细地演示了如何实现以下方面的不变性:

回答by Steven de Salas

A Contrarian View of Immutability

不变性的逆向观点

TL/DR: Immutability is more a fashion trend than a necessity in JavaScript. If you are using React it does provide a neat work-around to some confusing design choicesin state management. However in most other situations it wont add enough value over the complexity it introduces, serving more to pad up a resumethan to fulfill an actual client need.

TL/DR:在 JavaScript 中,不变性与其说是必需品,不如说是一种时尚趋势。如果您正在使用 React,它确实为状态管理中一些令人困惑的设计选择提供了一种巧妙的解决方法。然而,在大多数其他情况下,它不会为其引入的复杂性增加足够的价值,更多地是为了充实简历而不是满足实际的客户需求。

Long answer: read below.

长答案:阅读下文。

Why is immutability so important(or needed) in javascript?

为什么不可变性在 javascript 中如此重要(或需要)?

Well, I'm glad you asked!

好吧,我很高兴你问了!

Some time ago a very talented guy called Dan Abramovwrote a javascript state management library called Reduxwhich uses pure functions and immutability. He also made some really cool videosthat made the idea really easy to understand (and sell).

前段时间,一个非常有才华的人Dan Abramov写了一个名为Redux的 JavaScript 状态管理库,它使用纯函数和不变性。他还制作了一些非常酷的视频,使这个想法非常容易理解(和销售)。

The timing was perfect. The novelty of Angularwas fading, and JavaScript world was ready to fixate on the latest thing that had the right degree of cool, and this library was not only innovative but slotted in perfectly with Reactwhich was being peddled by another Silicon Valley powerhouse.

时机是完美的。Angular的新鲜感正在消退,JavaScript 世界已经准备好关注具有适当酷炫程度的最新事物,而这个库不仅具有创新性,而且与被另一家硅谷巨头兜售的React完美契合。

Sad as it may be, fashions rule in the world of JavaScript. Now Abramov is being hailed as a demigod and all us mere mortals have to subject ourselves to the Dao of Immutability... Wether it makes sense or not.

尽管很可悲,但时尚统治着 JavaScript 的世界。现在,阿布拉莫夫被誉为半神,我们凡人都必须服从不变之……这是否有意义。

What is wrong in mutating objects?

改变对象有什么问题?

Nothing!

没有!

In fact programmers have been mutating objects for er... as long as there has been objects to mutate. 50+ yearsof application development in other words.

事实上,程序员一直在改变对象……只要有要改变的对象。换句话说,50 多年的应用程序开发经验。

And why complicate things? When you have object catand it dies, do you really need a second catto track the change? Most people would just say cat.isDead = trueand be done with it.

为什么要把事情复杂化?当您有对象cat并且它死了时,您真的需要一秒钟cat来跟踪更改吗?大多数人只会说cat.isDead = true并完成它。

Doesn't (mutating objects) make things simple?

(变异对象)不会让事情变得简单吗?

YES! .. Of course it does!

是的!..当然可以!

Specially in JavaScript, which in practice is most useful used for rendering a view of some state that is maintained elsewhere (like in a database).

特别是在 JavaScript 中,它在实践中最有用的是呈现在其他地方(例如在数据库中)维护的某些状态的视图。

What if I have a new News object that has to be updated? ... How do I achieve in this case? Delete the store & recreate it? Isn't adding an object to the array a less expensive operation?

如果我有一个新的 News 对象需要更新怎么办?...在这种情况下我如何实现?删除商店并重新创建它?将一个对象添加到数组中不是一个成本较低的操作吗?

Well, you can go the traditional approach and update the Newsobject, so your in-memory representation of that object changes (and the view displayed to the user, or so one would hope)...

好吧,您可以采用传统方法并更新News对象,因此该对象的内存表示会发生变化(以及向用户显示的视图,或者人们希望如此)......

Or alternatively...

或者替代...

You can try the sexy FP/Immutability approach and add your changes to the Newsobject to an array tracking every historical changeso you can then iterate through the array and figure out what the correct state representation should be (phew!).

您可以尝试性感的 FP/Immutability 方法,并将您对News对象的更改添加到跟踪每个历史更改的数组中,这样您就可以遍历该数组并找出正确的状态表示应该是什么(呸!)。

I am trying to learn what's right here. Please do enlighten me :)

我正在尝试了解这里的内容。请赐教我:)

Fashions come and go buddy. There are many ways to skin a cat.

时尚来来去去,伙计。有很多方法可以给猫剥皮。

I am sorry that you have to bear the confusion of a constantly changing set of programming paradigms. But hey, WELCOME TO THE CLUB!!

很抱歉,您不得不忍受一组不断变化的编程范式的混乱。但是,嘿,欢迎来到俱乐部!!

Now a couple of important points to remember with regards to Immutability, and you'll get these thrown at you with the feverish intensity that only naivety can muster.

现在有几个重要的点需要记住,关于不变性,你会以只有天真才能聚集的狂热强度向你抛出这些。

1) Immutability is awesome for avoiding race conditions in multi-threaded environments.

1) 不变性对于避免多线程环境中的竞争条件非常有用

Multi-threaded environments (like C++, Java and C#) are guilty of the practice of locking objects when more than one thread wants to change them. This is bad for performance, but better than the alternative of data corruption. And yet not as good as making everything immutable (Lord praise Haskell!).

当多个线程想要更​​改对象时,多线程环境(如 C++、Java 和 C#)会犯锁定对象的做法。这对性能不利,但比数据损坏的替代方案要好。但还不如让一切都一成不变(上帝赞美 Haskell!)。

BUT ALAS! In JavaScript you always operate on a single thread. Even web workers (each runs inside a separate context). So since you can't have a thread relatedrace condition inside your execution context (all those lovely global variables and closures), the main point in favour of Immutability goes out the window.

可惜!在 JavaScript 中,您总是在单线程上操作。甚至网络工作者(每个都在单独的上下文中运行)。因此,由于您的执行上下文中不能有与线程相关的竞争条件(所有那些可爱的全局变量和闭包),支持不变性的要点就消失了。

(Having said that, there isan advantage to using pure functions in web workers, which is that you'll have no expectations about fiddling with objects on the main thread.)

(话虽如此,有一个优势,在网络工作者,这是,你有没有关于与主线程上的对象摆弄的预期使用纯函数。)

2) Immutability can (somehow) avoid race conditions in the state of your app.

2)不变性可以(以某种方式)避免应用程序状态下的竞争条件。

And here is the real crux of the matter, most (React) developers will tell you that Immutability and FP can somehow work this magic that allows the state of your application to become predictable.

这就是问题的真正症结所在,大多数(React)开发人员会告诉你,Immutability 和 FP 可以以某种方式发挥这种魔法,让你的应用程序的状态变得可预测。

Of course this doesn't mean that you can avoid race conditions in the database, to pull that one off you'd have to coordinate all users in all browsers, and for that you'd need a back-end push technology like WebSockets(more on this below) that will broadcast changes to everyone running the app.

当然,这并不意味着您可以避免数据库中的竞争条件,要解决这个问题,您必须协调所有浏览器中的所有用户,为此您需要一种后端推送技术,如WebSockets(更多关于此的内容),它将向运行该应用程序的每个人广播更改。

Nor does it mean that there is some inherent problem in JavaScript where your application state needs immutability in order to become predictable, any developer that has been coding front-end applications before React would tell you this.

也不意味着 JavaScript 中存在一些固有问题,即您的应用程序状态需要不变性才能变得可预测,任何在 React 之前编写前端应用程序的开发人员都会告诉您这一点。

This rather confusing claim simply means that with React your application state will become more prone to race conditions, but that immutability allows you to take that pain away. Why? Because React is special.. its been designed as a highly optimised rendering librarywith coherent state management taking second place, and thus component state is managed via an asynchronous chain of events(aka "one-way data binding") that you have no control over and rely on you remembering not to mutate state directly...

这个相当令人困惑的声明只是意味着使用 React 你的应用程序状态将变得更容易出现竞争条件,但这种不变性让你可以消除这种痛苦。为什么?因为 React 是特殊的......它被设计为高度优化的渲染库,其一致性状态管理位于第二位,因此组件状态是通过您无法控制的异步事件链(又名“单向数据绑定”)管理的靠你记住不要直接改变状态......

Given this context, its easy to see how the need for immutability has little to do with JavaScript and a lot to do with race conditions in React: if have a bunch of inter-dependent changes in your application and no easy way to figure out what your state is currently at, you are going to get confused, and thus it makes perfect sense to use immutability to track every historical change.

鉴于这种情况,很容易看出对不变性的需求与 JavaScript 几乎没有关系,而与 React 中的竞争条件有很大关系:如果您的应用程序中有一堆相互依赖的变化,并且没有简单的方法来弄清楚是什么您的状态目前处于 ,您会感到困惑,因此使用不变性来跟踪每个历史变化是非常有意义的

3) Race conditions are categorically bad.

3) 竞争条件绝对糟糕。

Well, they might be if you are using React. But they are rare if you pick up a different framework.

好吧,如果您使用 React,它们可能是。但是如果你选择不同的框架,它们就很少见了。

Besides, you normally have far bigger problemsto deal with… Problems like dependency hell. Like a bloated code-base. Like your CSS not getting loaded. Like a slow build process or being stuck to a monolithic back-end that makes iterating almost impossible. Like inexperienced devs not understanding whats going on and making a mess of things.

此外,你通常有更大的问题要处理......像依赖地狱这样的问题。就像一个臃肿的代码库。就像你的 CSS 没有被加载一样。就像一个缓慢的构建过程或被困在一个单一的后端,这使得迭代几乎不可能。就像没有经验的开发人员不了解正在发生的事情并把事情弄得一团糟一样。

You know. Reality. But hey, who cares about that?

你懂。现实。但是,嘿,谁在乎呢?

4) Immutability makes use of Reference Typesto reduce the performance impact of tracking every state change.

4) 不变性利用引用类型来减少跟踪每个状态变化对性能的影响。

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

因为说真的,如果你每次状态改变时都要复制东西,你最好确保你很聪明。

5) Immutability allows you to UNDO stuff.

5) 不变性允许你撤销东西

Because er.. this is the number one feature your project manager is going to ask for, right?

因为呃……这是你的项目经理会要求的第一项功能,对吧?

6) Immutable state has lots of cool potential in combination with WebSockets

6) 不可变状态与 WebSockets 结合有很多很酷的潜力

Last but not least, the accumulation of state deltas makes a pretty compelling case in combination with WebSockets, which allows for an easy consumption of state as a flow of immutable events...

最后但并非最不重要的一点是,状态增量的积累与 WebSockets 相结合是一个非常引人注目的案例,它允许作为不可变事件流轻松消耗状态......

Once the penny drops on this concept (state being a flow of events-- rather than a crude set of records representing the latest view), the immutable world becomes a magical place to inhabit. A land of event-sourcedwonder and possibility that transcends time itself. And when done right this can definitely make real-time apps easierto accomplish, you just broadcast the flow of events to everyone interested so they can build their own representationof the present and write back their own changes into the communal flow.

一旦在这个概念上花一分钱(状态是一个事件流——而不是代表最新观点的一组粗糙的记录),不变的世界就会变成一个神奇的栖息地。一个超越时间本身事件来源的奇迹和可能性的土地。并在完成后右这个绝对可以让实时应用程式EASI来完成,你只播事件给大家感兴趣,所以他们的流动建立自己表示本和自己的变化写回社区流动。

But at some point you wake up and realise that all that wonder and magic do not come for free. Unlike your eager colleagues, your stakeholders (yea, the people who pay you) care little about philosophy or fashion and a lot about the money they pay to build a product they can sell. And the bottom line is that its harder to write immutable code and easier to break it, plus there is little point having an immutable front-end if you don't have a back-end to support it. When (and if!) you finally convince your stakeholders that you should publish and consume events via a push techologylike WebSockets, you find out what a pain it is to scale in production.

但在某些时候,你醒来并意识到所有的奇迹和魔法都不是免费的。与你热切的同事不同,你的利益相关者(是的,付钱给你的人)很少关心哲学或时尚,而是很关心他们为打造可以销售的产品而支付的钱。最重要的是,编写不可变代码更难,更容易破坏它,而且如果您没有后端来支持它,那么拥有一个不可变的前端也没什么意义。当(并且如果!)您最终说服您的利益相关者您应该通过像 WebSockets 这样的推送技术发布和使用事件时,您会发现在生产中扩展是多么痛苦



Now for some advice, should you choose to accept it.

现在给一些建议,你是否应该选择接受它。

A choice to write JavaScript using FP/Immutability is also a choice to make your application code-base larger, more complex and harder to manage. I would strongly argue for limiting this approach to your Redux reducers, unless you know what you are doing... And IF you are going to go ahead and use immutability regardless, then apply immutable state to your whole application stack, and not just the client-side, as you're missing the real value of it otherwise.

使用 FP/Immutability 编写 JavaScript 的选择也是使您的应用程序代码库更大、更复杂和更难管理的选择。我强烈主张将这种方法限制在您的 Redux 减速器上,除非您知道自己在做什么……而且如果您无论如何都要继续使用不变性,那么将不可变状态应用于整个应用程序堆栈,而不仅仅是应用程序堆栈客户端,否则您将错过它的真正价值。

Now, if you are fortunate enough to be able to make choices in your work, then try and use your wisdom (or not) and do what's right by the person who is paying you. You can base this on your experience, on your gut, or whats going on around you (admittedly if everyone is using React/Redux then there a valid argument that it will be easier to find a resource to continue your work).. Alternatively, you can try either Resume Driven Developmentor Hype Driven Developmentapproaches. They might be more your sort of thing.

现在,如果你有幸能够在你的工作中做出选择,那么试着运用你的智慧(或不),做支付给你钱的人应该做的事情。你可以根据你的经验、直觉或你周围发生的事情(诚然,如果每个人都在使用 React/Redux,那么有一个有效的论点是,找到一个资源来继续你的工作会更容易)。或者,您可以尝试恢复驱动开发炒作驱动开发方法。他们可能更适合你。

In short, the thing to be said for immutability is that it willmake you fashionable with your peers, at least until the next craze comes around, by which point you'll be glad to move on.

总之,可以说对于不变性的事情是,它令你的时尚与您同行,至少直到下一个热潮来临时,由此时你会很高兴地前进。



Now after this session of self-therapy I'd like to point out that I've added this as an article in my blog => Immutability in JavaScript: A Contrarian View. Feel free to reply in there if you have strong feelings you'd like to get off your chest too ;).

现在,在本次自我治疗之后,我想指出我已将此作为文章添加到我的博客中 => JavaScript 中的不变性:逆势观点。如果您有强烈的感觉,也想摆脱胸膛,请随时在那里回复;)。

回答by TwoStraws

The question is, why is immutability so important? What is wrong in mutating objects? Doesn't it make things simple?

问题是,为什么不变性如此重要?改变对象有什么问题?这不是让事情变得简单吗?

Actually, the opposite is true: mutability makes things more complicated, at least in the long run. Yes, it makes your initial coding easier because you can just change things wherever you want, but when your program goes bigger it becomes a problem –?if a value changed, what changed it?

实际上,情况恰恰相反:可变性使事情变得更加复杂,至少从长远来看是这样。是的,它使您的初始编码更容易,因为您可以随心所欲地更改内容,但是当您的程序变大时,它就会成为一个问题——如果一个值改变了,是什么改变了它?

When you make everything immutable, it means data can't be changed by surprise any more. You know for certain that if you pass a value into a function, it can't be changed in that function.

当你让一切都变得不可变时,这意味着数据不能再意外地改变了。您肯定知道,如果将值传递给函数,则无法在该函数中更改该值。

Put simply: if you use immutable values, it makes it very easy to reason about your code: everyone gets a unique* copy of your data, so it can't futz with it and break other parts of your code. Imagine how much easier this makes working in a multi-threaded environment!

简而言之:如果您使用不可变值,则可以很容易地对您的代码进行推理:每个人都会获得您的数据的唯一*副本,因此它不会对其进行干扰并破坏代码的其他部分。想象一下这让在多线程环境中工作变得多么容易!

Note 1: There is a potential performance cost to immutability depending on what you're doing, but things like Immutable.js optimise as best they can.

注 1:根据您正在做的事情,不变性有潜在的性能成本,但是像 Immutable.js 这样的东西会尽可能地优化。

Note 2: In the unlikely event you weren't sure, Immutable.js and ES6 constmean very different things.

注 2:万一您不确定,Immutable.js 和 ES6 的const含义非常不同。

In usual case, I could have just added the object to the array. How do I achieve in this case? Delete the store & recreate it? Isn't adding an object to the array a less expensive operation? PS: If the example is not the right way to explain immutability, please do let me know what's the right practical example.

通常情况下,我可以将对象添加到数组中。在这种情况下我如何实现?删除商店并重新创建它?将一个对象添加到数组中不是一个成本较低的操作吗?PS:如果这个例子不是解释不变性的正确方法,请让我知道什么是正确的实际例子。

Yes, your news example is perfectly good, and your reasoning is exactly right: you can't just amend your 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);

回答by Jared Smith

Although the other answers are fine, to address your question about a practical use case (from the comments on the other answers) lets step outside your running code for a minute and look at the ubiquitous answer right under your nose: git. What would happen if every time you pushed a commit you overwrotethe data in the repository?

尽管其他答案很好,但要解决您关于实际用例的问题(来自对其他答案的评论),让我们暂时离开正在运行的代码一分钟,看看您眼皮底下无处不在的答案:git。如果每次推送提交时都覆盖存储库中的数据会发生什么?

Now we're in to one of the problems that immutable collections face: memory bloat. Git is smart enough to not simply make new copies of files every time you make a change, it simply keeps track of the diffs.

现在我们遇到了不可变集合面临的问题之一:内存膨胀。Git 足够聪明,不会在每次更改时简单地制作文件的新副本,它只是跟踪差异

While I don't know much about the inner workings of git, I can only assume it uses a similar strategy to that of libraries you reference: structural sharing. Under the hood the libraries use triesor other trees to only track the nodes that are different.

虽然我不太了解 git 的内部工作原理,但我只能假设它使用与您引用的库类似的策略:结构共享。在幕后,库使用尝试或其他树来仅跟踪不同的节点。

This strategy is also reasonably performant for in-memory data structures as there are well-knowntree-operation algorithms that operate in logarithmic time.

这种策略对于内存数据结构也具有合理的性能,因为存在以对数时间运行的众所周知的树操作算法。

Another use case: say you want an undo button on your webapp. With immutable representations of your data, implementing such is relatively trivial. But if you rely on mutation, that means you have to worry about caching the state of the world and making atomic updates.

另一个用例:假设您想要在您的 web 应用程序上有一个撤消按钮。对于数据的不可变表示,实现这样的表示相对微不足道。但是如果你依赖变异,那就意味着你必须担心缓存世界的状态和进行原子更新。

In short, there's a price to pay for immutability in runtime performance and the learning curve. But any experienced programmer will tell you that debugging time outweighs code-writing time by an order of magnitude. And the slight hit on runtime performance is likely outweighed by the state-related bugs your users don't have to endure.

简而言之,要为运行时性能和学习曲线的不变性付出代价。但是任何有经验的程序员都会告诉你调试时间比编写代码时间长一个数量级。您的用户不必忍受与状态相关的错误可能会抵消对运行时性能的轻微影响。

回答by Maciej Sikora

The question is, why is immutability so important? What is wrong in mutating objects? Doesn't it make things simple?

问题是,为什么不变性如此重要?改变对象有什么问题?这不是让事情变得简单吗?

About mutability

关于可变性

Nothing is wrong in mutability from technical point of view. It is fast, it is re-using the memory. Developers are use to it from the beginning (as I remember it). Problem exists in the use of mutability and troubles which this use can bring.

从技术角度来看,可变性没有错。它很快,它正在重新使用内存。开发人员从一开始就习惯了它(我记得它)。使用可变性存在问题,并且这种使用可能带来麻烦。

If object is not shared with anything, for example exists in the scope of the function and is not exposed to the outside, then it is hard to see benefits in immutability. Really in this case it is no sense to be immutable. The sense of immutability starts when something is shared.

如果 object 不与任何东西共享,例如存在于函数的范围内并且不暴露给外部,那么很难看到不变性的好处。真的在这种情况下,保持不变是没有意义的。当某些东西被共享时,不变性就开始了。

Mutability headache

可变性头痛

Mutable shared structure can easily create many pitfalls. Any change in any part of the code with access to the reference has impact to other parts with visibility of this reference. Such impact connects all parts together, even when they should not be aware of different modules. Mutation in one function can crash totally different part of the app. Such thing is a bad side effect.

可变的共享结构很容易造成许多陷阱。可以访问引用的任何代码部分的任何更改都会影响具有此引用可见性的其他部分。这种影响将所有部分连接在一起,即使它们不应该知道不同的模块。一个函数中的突变可能会导致应用程序的完全不同部分崩溃。这样的事情是一个不好的副作用。

Next often problem with mutation is corrupted state. Corrupted state can happen when mutation procedure fails in the middle, and some fields were modified and some not.

突变的下一个常见问题是状态损坏。当变异过程在中间失败时,可能会发生损坏状态,有些字段被修改,有些没有。

What's more, with mutation it is hard to track the change. Simple reference check will not show the difference, to know what changed some deep check needs to be done. Also to monitor the change some observable pattern needs to be introduced.

更重要的是,使用突变很难跟踪变化。简单的参考检查不会显示差异,要知道发生了什么变化,需要进行一些深入的检查。此外,为了监控变化,需要引入一些可观察的模式。

Finally, mutation is reason of the trust deficit. How you can be sure that some structure has wanted value, if it can be mutated.

最后,变异是信任赤字的原因。如果可以变异,您如何确定某些结构具有所需的价值。

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

As above example shows, passing mutable structure always can finish by having different structure. Function doSomething is mutating the attribute given from outside. No trust for the code, you really don't know what you have and what you will have. All these problems take place because: Mutable structures are representing pointers to the memory.

如上例所示,传递可变结构总是可以通过具有不同的结构来完成。函数 doSomething 正在改变从外部给出的属性。对代码不信任,你真的不知道你拥有什么,你将拥有什么。所有这些问题的发生都是因为:可变结构表示指向内存的指针。

Immutability is about values

不变性是关于价值的

Immutability means that change is not done on the same object,structure, but change is represented in new one. And this is because reference represents value not only memory pointer. Every change creates new value and doesn't touch the old one. Such clear rules gives back the trust and code predictability. Functions are safe to use because instead of mutation, they deal with own versions with own values.

不变性意味着变化不是在同一个对象、结构上完成的,而是用新的对象来表示的。这是因为引用不仅代表内存指针,还代表值。每一次变化都会创造新的价值,并且不会触及旧的价值。如此清晰的规则还给了信任和代码可预测性。函数可以安全使用,因为它们不是变异,而是用自己的值处理自己的版本。

Using values instead of memory containers gives certainty that every object represents specific unchangeable value and it is safe to use it.

使用值而不是内存容器可以确定每个对象都代表特定的不可更改的值,并且使用它是安全的。

Immutable structures are representing values.

不可变结构代表值。

I am diving even more into the subject in medium article - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310

我在中篇文章中更深入地探讨了这个主题 - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310

回答by prosti

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

为什么不可变性在 JavaScript 中如此重要(或需要)?

Immutability can be tracked in different contexts, but most important would be to track it against the application state and against the application UI.

可以在不同的上下文中跟踪不变性,但最重要的是根据应用程序状态和应用程序 UI 跟踪它。

I will consider the JavaScript Redux pattern as very trendy and modern approach and because you mentioned that.

我将 JavaScript Redux 模式视为非常流行和现代的方法,因为你提到了这一点。

For the UI we need to make it predictable. It will be predictable if UI = f(application state).

对于 UI,我们需要使其可预测。如果UI = f(application state).

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

应用程序(在 JavaScript 中)确实通过使用reducer 函数实现的操作来更改状态。

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

reducer 函数简单地执行动作和旧状态并返回新状态,保持旧状态不变。

new state  = r(current state, action)

enter image description here

在此处输入图片说明

The benefit is: you time-travel the states since all the state objects are saved, and you can render the app in any state since UI = f(state)

好处是:由于所有状态对象都已保存,因此您可以时间旅行状态,并且您可以在任何状态下渲染应用程序 UI = f(state)

So you can undo/redo easily.

因此,您可以轻松撤消/重做。



Happens to be creating all these states can still be memory efficient, an analogy with Git is great, and we have the similar analogy in Linux OS with symbolic links (based on the inodes).

碰巧创建所有这些状态仍然可以节省内存,与 Git 的类比很好,我们在 Linux 操作系统中也有类似的类比,带有符号链接(基于 inode)。

回答by ghostypants

Another benefit of Immutability in Javascript is that it reduces Temporal Coupling, which has substantial benefits for design generally. Consider the interface of an object with two methods:

Javascript 中不变性的另一个好处是它减少了时间耦合,这通常对设计有很大的好处。考虑具有两种方法的对象的接口:

class Foo {

      baz() {
          // .... 
      }

      bar() {
          // ....
      }

}

const f = new Foo();

It may be the case that a call to baz()is required to get the object in a valid state for a call to bar()to work correctly. But how do you know this?

可能需要调用baz()才能使对象处于有效状态,才能使调用bar()正确工作。但是你怎么知道这个?

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

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

To figure it out you need to scrutinise the class internals because it is not immediately apparent from examining the public interface. This problem can explode in a large codebase with lots of mutable state and classes.

要弄清楚它,您需要仔细检查类的内部结构,因为检查公共接口并不能立即看出这一点。这个问题可能会在包含大量可变状态和类的大型代码库中爆炸。

If Foois immutable then this is no longer a problem. It is safe to assume we can call bazor barin any order because the inner state of the class cannot change.

如果Foo是不可变的,那么这不再是问题。可以安全地假设我们可以调用bazbar以任何顺序,因为类的内部状态不能改变。

回答by puchu

Once upon a time, there was a problem with data synchronization between threads. This problem was a great pain, there were 10+ solutions. Some people tried to solve it radically. It was a place where functional programming was born. It is just like Marxism. I couldn't understand how Dan Abramov sold this idea into JS, because it is single threaded. He is a genius.

曾几何时,线程之间的数据同步存在问题。这个问题很痛苦,有10多个解决方案。有些人试图从根本上解决它。它是函数式编程诞生的地方。就像马克思主义一样。我无法理解 Dan Abramov 是如何将这个想法卖给 JS 的,因为它是单线程的。他是个天才。

I can give a small example. There is an attribute __attribute__((pure))in gcc. Compilers tries to solve whether your function is pure or not if you won't declear it specially. Your function can be pure even your state is mutable. Immutability is just a one of 100+ ways to guarantee that you function will be pure. Actually 95% of your functions will be pure.

我可以举一个小例子。__attribute__((pure))gcc 中有一个属性。如果您不特别声明它,编译器会尝试解决您的函数是否是纯函数。即使您的状态是可变的,您的函数也可以是纯函数。不变性只是 100 多种保证函数纯粹的方法之一。实际上 95% 的函数都是纯函数。

You shouldn't use any limitations (like immutability) if you actually don't have a serious reason. If you want to "Undo" some state, you can create transactions. If you want to simplify communications you can send events with immutable data. It is up to you.

如果您实际上没有严重的理由,则不应使用任何限制(如不变性)。如果您想“撤消”某些状态,您可以创建交易。如果您想简化通信,您可以使用不可变数据发送事件。它是由你决定。

I am writing this message from post marxism republic. I am sure that radicalization of any idea is a wrong way.

我正在写这篇来自后马克思主义共和国的信息。我确信任何想法的激进化都是错误的。

回答by Jared Smith

A Different Take...

不同的看法...

My other answer addresses the question from a very practical standpoint, and I still like it. I've decided to add this as another answer rather than an addendum to that one because it is a boring philosophical rant which hopefully also answers the question, but doesn't really fit with my existing answer.

我的另一个答案是从非常实际的角度解决了这个问题,我仍然喜欢它。我决定将其添加为另一个答案而不是该答案的附录,因为这是一个无聊的哲学咆哮,希望也能回答这个问题,但并不真正符合我现有的答案。

TL;DR

TL; 博士

Even in small projects immutability can be useful, but don't assume that because it exists it's meant for you.

即使在小项目中,不变性也很有用,但不要认为它存在就适合你。

Much, much longer answer

很多,更长的答案

NOTE:for the purpose of this answer I'm using the word 'discipline' to mean self-denial for some benefit.

注意:为了这个答案的目的,我使用“纪律”这个词来表示为了某些利益而自我否定。

This is similar in form to another question: "Should I use Typescript? Why are types so important in JavaScript?". It has a similar answer too. Consider the following scenario:

这在形式上类似于另一个问题:“我应该使用 Typescript?为什么类型在 JavaScript 中如此重要?”。它也有类似的答案。考虑以下场景:

You are the sole author and maintainer of a JavaScript/CSS/HTML codebase of some 5000 lines. Your semi-technical boss reads something about Typescript-as-the-new-hotness and suggests that we may want to move to it but leaves the decision to you. So you read about it, play with it, etc.

您是大约 5000 行 JavaScript/CSS/HTML 代码库的唯一作者和维护者。你的半技术老板读了一些关于 Typescript-as-the-new-hotness 的内容,并建议我们可能想转向它,但把决定权留给你。所以你阅读它,玩它,等等。

So now you have a choice to make, do you move to Typescript?

所以现在你有一个选择,你会转向打字稿吗?

Typescript has some compelling advantages: intellisense, catching errors early, specifying your APIs upfront, ease of fixing things when refactoring breaks them, fewer tests. Typescript also has some costs: certain very natural and correct JavaScript idioms can be tricky to model in it's not-especially-powerful type system, annotations grow the LoC, time and effort of rewriting existing codebase, extra step in the build pipeline, etc. More fundamentally, it carves out a subsetof possible correct JavaScript programs in exchange for the promise that your code is more likelyto be correct. It's arbitrarily restrictive. That's the whole point: you impose some discipline that limits you (hopefully from shooting yourself in the foot).

Typescript 有一些引人注目的优势:智能感知、及早发现错误、预先指定 API、在重构破坏时易于修复、更少的测试。Typescript 也有一些成本:某些非常自然和正确的 JavaScript 习语在它不是特别强大的类型系统中建模可能很棘手,注释增加了 LoC,重写现有代码库的时间和精力,构建管道中的额外步骤等。更根本的是,它开辟了一个可能正确的 JavaScript 程序的子集,以换取你的代码更有可能是正确的承诺。它是任意限制的。这就是重点:你强加了一些限制你的纪律(希望不要用脚射击自己)。

Back to the question, rephrased in the context of the above paragraph: is it worth it?

回到问题,在上一段的上下文中重新表述:值得吗?

In the scenario described, I would contend that if you are very familiar with a small-to-middling JS codebase, that the choice to use Typescript is more aesthetic than practical. And that's fine, there's nothing wrongwith aesthetics, they just aren't necessarily compelling.

在所描述的场景中,我认为如果您非常熟悉中小型 JS 代码库,那么选择使用 Typescript 比实际更美观。这很好,美学没有任何问题,只是它们不一定引人注目。

Scenario B:

场景B:

You change jobs and are now a line-of-business programmer at Foo Corp. You're working with a team of 10 on a 90000 LoC (and counting) JavaScript/HTML/CSS codebase with a fairly complicated build pipeline involving babel, webpack, a suite of polyfills, react with various plugins, a state management system, ~20 third-party libraries, ~10 internal libraries, editor plugins like a linter with rules for in-house style guide, etc. etc.

你换了工作,现在是 Foo Corp 的一名业务线程序员。你和一个 10 人的团队一起工作在 90000 LoC(和计数)JavaScript/HTML/CSS 代码库中,使用相当复杂的构建管道,包括 babel、webpack ,一套 polyfill,与各种插件、状态管理系统、~20 个第三方库、~10 个内部库、编辑器插件(如带有内部样式指南规则的 linter)等做出反应。

Back when you were 5k LoC guy/girl, it just didn't matter that much. Even documentation wasn't thatbig a deal, even coming back to a particular portion of the code after 6 months you could figure it out easily enough. But now discipline isn't just nice but necessary. That discipline may not involve Typescript, but willlikely involve some form of static analysis as well as all the other forms of coding discipline (documentation, style guide, build scripts, regression testing, CI). Discipline is no longer a luxury, it is a necessity.

回到当你是 5k LoC 的男人/女孩时,这并不重要。甚至文档也不什么大问题,即使在 6 个月后回到代码的特定部分,您也可以很容易地弄清楚它。但是现在纪律不仅是好的而且是必要的。这门学科可能不涉及打字稿,但可能涉及某种形式的静态分析,以及所有其他形式的编码规则(文档,风格指南,构建脚本,回归测试,CI)的。纪律不再是一种奢侈品,而是一种必需品

All of this applied to GOTOin 1978: your dinky little blackHyman game in C could use GOTOs and spaghetti logic and it just wasn't that big a deal to choose-your-own-adventure your way through it, but as programs got bigger and more ambitious, well, undisciplineduse of GOTOcould not be sustained. And all of this applies to immutability today.

所有这些都适用GOTO于 1978 年:你用 C 语言编写的小巧的二十一点游戏可以使用GOTOs 和意大利面条式逻辑,而且选择你自己的冒险方式并不是什么大不了的事,但是随着程序变得越来越大和更雄心勃勃的,好吧,无纪律的使用GOTO无法持续。所有这些都适用于今天的不变性。

Just like static types, if you are not working on a large codebase with a team of engineers maintaining/extending it, the choice to use immutability is more aesthetic than practical: it's benefits are still there but may not outweigh the costs yet.

就像静态类型一样,如果您不是在由工程师团队维护/扩展它的大型代码库上工作,那么选择使用不变性比实际更美观:它的好处仍然存在,但可能不会超过成本。

But as with all useful disciplines, there comes a point at which it is no longer optional. If I want to maintain a healthy weight, then discipline involving ice cream may be optional. But if I want to be a competitive athlete, my choice of whether or not to eat ice cream is subsumed by my choice of goals. If you want to change the world with software, immutability might be part of what you need to avoid it collapsing under it's own weight.

但与所有有用的学科一样,有一点不再是可选的。如果我想保持健康的体重,那么涉及冰淇淋的纪律可能是可选的。但如果我想成为一名竞技运动员,我对是否吃冰淇淋的选择就包含在我对目标的选择中。如果你想用软件改变世界,不变性可能是你需要避免它在它自身的重量下崩溃的一部分。

回答by neuronet

I've created a framework agnostic open source (MIT) lib for mutable (or immutable) state which can replace all those immutable storage like libs (redux, vuex etc...).

我为可变(或不可变)状态创建了一个框架不可知的开源(MIT)库,它可以替换所有那些不可变的存储,如库(redux、vuex 等)。

Immutable states was ugly for me because there was too much work to do (a lot of actions for simple read/write operations), code was less readable and performance for big datasets was not acceptable (whole component re-render :/ ).

不可变状态对我来说很难看,因为有太多的工作要做(简单的读/写操作有很多操作),代码可读性较差,大数据集的性能不可接受(整个组件重新渲染:/)。

With deep-state-observerI can update only one node with dot notation and use wildcards. I can also create history of the state (undo/redo/time travel) keeping just those concrete values that have been changed {path:value}= less memory usage.

使用deep-state-observer,我只能用点表示法更新一个节点并使用通配符。我还可以创建状态历史(撤消/重做/时间旅行),只保留那些已更改的具体值{path:value}= 更少的内存使用。

With deep-state-observerI can fine-tune things and I have grain control over component behavior so performance can be drastically improved. Code is more readable and refactoring is a lot easier - just search and replace path strings (no need to change code/logic).

使用深度状态观察器,我可以对事物进行微调,并且可以对组件行为进行粒度控制,因此可以显着提高性能。代码更具可读性,重构也更容易——只需搜索和替换路径字符串(无需更改代码/逻辑)。