小编典典

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

all

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

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

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

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


阅读 80

收藏
2022-05-22

共1个答案

小编典典

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

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

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

可预测性

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

表现

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

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

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

突变追踪

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

其他资源:

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

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

我喜欢以 Redux 为例,因为它包含不变性。store它有一个单一的不可变状态树(称为.
你可以在这里阅读更多关于它的核心原则。

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

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

2022-05-22