为什么不可变性在 JavaScript 中如此重要(或需要)? [英] Why is immutability so important (or needed) in JavaScript?

查看:36
本文介绍了为什么不可变性在 JavaScript 中如此重要(或需要)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在研究 React JSReact Native 框架.在中途我遇到了 Immutability 或 Immutable-JS 库,当时我正在阅读有关Facebook 的 Flux 和 Redux 实现.

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

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

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

解决方案

我最近一直在研究同一主题.我会尽力回答您的问题,并尝试分享我到目前为止所学到的知识.

<块引用>

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

基本上归结为不变性提高了可预测性、性能(间接)并允许突变跟踪.

可预测性

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

性能

即使向不可变对象添加值意味着需要创建一个新实例,其中需要复制现有值并将新值添加到需要内存的新对象中,但不可变对象可以利用结构共享以减少内存开销.

<块引用>

所有更新都返回新值,但内部结构共享给大幅减少内存使用(和 GC 抖动).这意味着如果你附加到一个有 1000 个元素的向量,它实际上并没有创建一个长度为 1001 个元素的新向量.很可能,内部只有少数分配小对象.

您可以在此处阅读有关此内容的更多信息.

突变追踪

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

其他资源:

<块引用>

如果我设置了一个初始值的对象数组.我不能操纵它.这就是不变性原则所说的,对吗?(正确如果我错了,我).但是,如果我有一个新的 News 对象必须被更新?通常情况下,我可以将对象添加到大批.在这种情况下我如何实现?删除商店 &重新创建它?向数组中添加对象不是成本更低的操作吗?

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

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

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

从'react'导入React;从 'react-dom' 导入 ReactDOM;//减速器.const news = (state=[], action) =>{开关(动作.类型){案例'ADD_NEWS_ITEM':{返回 [ ...状态, action.newsItem ];}默认: {返回状态;}}};//店铺.const createStore = (reducer) =>{让状态;让听众= [];const 订阅 = (监听器) =>{listeners.push(listener);返回 () =>{listeners = listeners.filter(cb => cb !== listener);};};const getState = () =>状态;const dispatch = (action) =>{状态 = 减速器(状态,动作);listeners.forEach( cb => cb() );};派遣({});返回{订阅,获取状态,调度};};//用 reducer 初始化 store.const store = createStore(news);//成分.const 新闻 = React.createClass({onAddNewsItem() {const { newsTitle } = this.refs;store.dispatch({类型:'ADD_NEWS_ITEM',新闻项目:{ 标题:新闻标题.值}});},使成为() {const { 新闻 } = this.props;返回 (<div><input ref="newsTitle"/><button onClick={ this.onAddNewsItem }>add</button><ul>{ news.map( ({ title }) => 
  • { title }
  • ) }

    );}});//将在商店调度时执行的处理程序.const render = () =>{ReactDOM.render(<News news={ store.getState() }/>,document.getElementById('新闻'));};//入口点.store.subscribe(render);使成为();

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

    I am currently working on React JS and React Native frameworks. 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.

    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 reader app with the opening screen being a list view of news headlines.

    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 and recreate it? Isn't adding an object to the array a less expensive operation?

    解决方案

    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.

    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 shouldComponentUpdate to check if the state is identical by comparing state Objects and prevent unnecessary rendering. You can read more about this here.

    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?

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

    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.

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

    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:

    这篇关于为什么不可变性在 JavaScript 中如此重要(或需要)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆