1. ホーム
  2. ジャバスクリプト

[解決済み】なぜJavaScriptでは不変性が重要(または必要)なのですか?

2022-04-01 06:45:32

質問

現在、私は リアクトJS リアクトネイティブ フレームワークです。途中から、Immutabilityや、そのための Immutable-JSライブラリ FacebookのFluxとReduxの実装について読んでいたときです。

問題は、なぜ不変性がそれほど重要なのか、ということです。オブジェクトを変異させることの何が問題なのでしょうか?物事を単純にするものではないのでしょうか?

例として、単純な ニュースリーダー アプリのオープニング画面は、ニュースヘッドラインのリストビューです。

例えば オブジェクトの配列 という値で 最初は 操作できないんです。不変性原理はそう言っていますよね?(間違っていたら訂正してください) しかし、新しいNewsオブジェクトを更新しなければならない場合はどうでしょうか?通常の場合、そのオブジェクトを配列に追加するだけでよかったのです。 この場合、どうすればいいのでしょうか?ストアを削除して、再作成しますか? オブジェクトを配列に追加する方が、より安価な処理ではないでしょうか?

どのように解決するのですか?

最近、同じテーマで研究しています。私はあなたの質問(複数可)に答えるために最善を尽くし、私がこれまでに学んだことを共有しようとします。

<ブロッククオート

問題は、なぜ不変性がそれほど重要なのか、ということです。何が問題で オブジェクトを変異させる?物事をシンプルにするのではないのでしょうか?

基本的には、不変性が予測可能性を高め、パフォーマンスを(間接的に)向上させ、突然変異の追跡を可能にするという事実に帰結します。

予測可能性

突然変異は変化を隠すので、(予期しない)副作用を生み出し、厄介なバグの原因となる。不変性を強制すると、アプリケーションのアーキテクチャとメンタルモデルをシンプルに保つことができ、アプリケーションに関する推論が容易になります。

パフォーマンス

イミュータブルオブジェクトに値を追加する場合、新しいインスタンスを作成し、既存の値をコピーして、新しい値を新しいオブジェクトに追加する必要があり、メモリコストがかかりますが、イミュータブルオブジェクトは構造共有を利用してメモリのオーバーヘッドを削減することができます。

<ブロッククオート

すべての更新は新しい値を返しますが、内部で構造体を共有することで メモリ使用量(とGCスラッシング)を劇的に削減します。これは、もし 1000 要素のベクトルに追加しても、実際に作成されるのは は1001要素の長さの新しいベクトルです。ほとんどの場合、内部ではいくつかの の小さなオブジェクトが割り当てられます。

詳しくはこちらをご覧ください こちら .

ミューテーショントラッキング

メモリ使用量の削減のほか、参照と値の等価性を利用したアプリケーションの最適化も可能です。これにより、何かが変更されたかどうかを実に簡単に確認することができます。例えば、reactコンポーネントの状態が変化した場合。この場合 shouldComponentUpdate を使用すると、状態オブジェクトの比較によって状態が同一かどうかを確認し、不要なレンダリングを防止することができます。 詳しくはこちら ここで .

追加資料です。

オブジェクトの配列に値を初期設定する場合。このままでは それを操作する。不変性原理はそう言っていますよね? 間違っていたら教えてください)。しかし、新しいNewsオブジェクトが必要な場合はどうでしょう。 更新されるのでしょうか?通常の場合、そのオブジェクトを 配列になります。この場合、どうすればいいのでしょうか?ストア&アンプを削除し、それを再作成しますか? オブジェクトを配列に追加する方が、より安価な操作ではないでしょうか?

はい、これは正しいです。もし、あなたのアプリケーションでこれをどのように実装するかで迷っているのであれば、次の方法を見ることをお勧めします。 リデュース を実行することで、核となる概念に精通することができます。

私がReduxを例に挙げるのが好きなのは、Reduxがイミュータビリティを受け入れているからです。Reduxは、単一のイミュータブルなステートツリー(以下、「ステートツリー」と呼ぶ)を持っています。 store このアクションはreducerによって処理され、reducerは前の状態とアクションを(一度に一つずつ)受け取り、アプリケーションの次の状態を返します。その基本的な原理については、以下の記事をご覧ください。 こちら .

には素晴らしいreduxのコースがあります。 egghead.io どこ ダン・アブラモフ reduxの作者である某氏は、この原則を以下のように説明しています(シナリオに合うようにコードを少し修正しました)。

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

また、これらのビデオでは、immutability forを実現する方法をさらに詳しく説明しています。