小编典典

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

javascript

我目前正在研究React JS和ReactNative框架。在阅读关于Facebook的Flux和Redux实现的文章时,我遇到了Immutability或Immutable-JS库。

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

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

如果我设置说 最初 具有值 的对象数组,我将无法对其进行操作。这就是不变性原则的意思,对吗?(如果我错了,请纠正我。)但是,如果我有一个新的News对象必须更新怎么办?在通常情况下,我可以将对象添加到数组中。在这种情况下我该如何实现?删除商店并重新创建?是不是将对象添加到数组中的开销较小?


阅读 215

收藏
2020-05-01

共1个答案

小编典典

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

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

基本上可以归结为不变性(间接地)提高了可预测性,性能和允许进行突变跟踪的事实。

可预测性

突变会隐藏变化,从而产生(意外的)副作用,这可能会导致讨厌的错误。当您强制执行不变性时,可以使您的应用程序体系结构和思维模型保持简单,这使得对应用程序进行推理变得更加容易。

性能

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

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

变异追踪

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

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

我喜欢以Redux为例,因为它具有不变性。它具有一个不变的状态树(称为store),其中所有状态更改都是通过调度动作来显式显示的,该动作由化简器处理,该化简器接受前一个状态以及所述动作(一次一个)并返回应用程序的下一个状态。

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();
2020-05-01