web-dev-qa-db-ja.com

object.freezeではなくimmutablejsを使用する必要があるのはなぜですか?

Object.freeze()に対するimmutablejsの利点についてネットで調査しましたが、満足できるものは見つかりませんでした!

私の質問は、なぜこのライブラリを使用し、プレーンな古いjavascriptオブジェクトをフリーズできるときに非ネイティブのデータ構造を操作する必要があるのか​​ということです。

28
alisabzevari

Immutablejsが提供するものを理解していないと思います。オブジェクトを不変にするだけのライブラリではなく、不変の値を扱うライブラリです。

単に docsmission statement を繰り返すことなく、次の2つのことを説明します。

  1. タイプ。彼らは(不変の)無限の範囲、スタック、順序付きセット、リストなどを実装しました...

  2. すべてのタイプは Persistent Data Structures として実装されます。

私は嘘をついた、ここに彼らの使命声明の引用がある:

不変データは一度作成すると変更できないため、アプリケーション開発が大幅に簡素化され、防御的なコピーが不要になり、単純なロジックで高度なメモ化と変更検出技術が可能になります。永続データは、データをその場で更新するのではなく、常に新しい更新されたデータを生成する、変異型APIを提供します。

彼らがリンクしている記事やビデオ、および永続データ構造についての詳細を読むことをお勧めします(それらはimmutablejsのことなので) '文などで要約します:

ゲームを書いていて、2D飛行機に座っているプレイヤーがいると想像してください。たとえば、ボブは次のとおりです。

var player = {
  name: 'Bob',
  favouriteColor: 'moldy mustard',

  x: 4,
  y: 10
};

FP koolaidを飲んだので、プレイヤーをフリーズしたいです(brrr!ボブがセーターを手に入れたことを願っています):

var player = Object.freeze({
    name: 'Bob',
    ...
});

そして、ゲームループに入ります。ティックごとに、プレーヤーの位置が変更されます。凍結されているため、プレーヤーオブジェクトを更新することはできないため、コピーします。

function movePlayer(player, newX, newY) {
    return Object.freeze(Object.assign({}, player, { x: newX, y: newY }));
}

それは素晴らしいことですが、どれだけ無駄なコピーを作成しているかに注目してください。ティックごとに、新しいオブジェクトを作成し、オブジェクトの1つを反復処理して、その上に新しい値を割り当てます。ティックごと、オブジェクトごとに。それはかなり一口です。

不変はあなたのためにこれをラップします:

var player = Immutable.Map({
    name: 'Bob',
    ...
});

function movePlayer(player, newX, newY) {
    return player.set('x', newX).set('y', newY);
}

そして、永続的なデータ構造のノ * magic ゚マジック✧゚ *ヽにより、可能な限りleastの操作量を約束します。

考え方の違いもあります。 「普通の[凍結] javascriptオブジェクト」で作業する場合、everythingの部分のデフォルトのアクションは、可変性を想定することであり、意味のある不変性(つまり、状態が存在することを認める不変性)を達成するための余分なマイル。それがfreezeが存在する理由の一部です。そうしないと、物事がパニックに陥ります。 Immutablejsでは、もちろん、不変性はデフォルトの前提であり、その上にNice APIがあります。

それはすべてがピンクでバラ色で、上にチェリーが付いていると言っているわけではありません。もちろん、すべてに欠点があり、できるからといって不変を詰め込むべきではありません。オブジェクトをfreezeingするだけで十分な場合もあります。ちなみに、ほとんどの場合は十分以上です。ニッチな便利なライブラリですが、誇大広告に夢中にならないでください。

50
Zirak

私の benchmarks によると、immutable.jsは書き込み操作用に最適化されています、Object.assign()より高速ですが、読み取り操作の場合は遅くなります。そのため、決定はアプリケーションのタイプとその読み取り/書き込み比率に依存します。ベンチマーク結果の概要は次のとおりです。

-- Mutable
Total elapsed = 103 ms = 50 ms (read) + 53 ms (write).

-- Immutable (Object.assign)
Total elapsed = 2199 ms = 50 ms (read) + 2149 ms (write).

-- Immutable (immutable.js)
Total elapsed = 1690 ms = 638 ms (read) + 1052 ms (write).

-- Immutable (seamless-immutable)
Total elapsed = 91333 ms = 31 ms (read) + 91302 ms (write).

-- Immutable (immutable-assign (created by me))
Total elapsed = 2223 ms = 50 ms (read) + 2173 ms (write).

理想的には、パフォーマンスの最適化を導入する前にアプリケーションのプロファイルを作成する必要がありますが、不変性は設計上の決定の1つであり、早期に決定する必要があります。 immutable.js の使用を開始する場合、fromJS()およびtoJS()を使用するプレーンJSオブジェクトとの相互運用は非常にコストがかかるため、アプリケーション全体で使用してパフォーマンス上のメリットを得る必要があります。

PS:ディープフリーズしたアレイ(1000要素)の更新が非常に遅くなり、約50倍遅くなることがわかったため、開発モードでのみディープフリーズを使用する必要があります。ベンチマーク結果:

-- Immutable (Object.assign) + deep freeze
Total elapsed = 45903 ms = 96 ms (read) + 45807 ms (write).
6
engineforce

どちらもオブジェクトを深く不変にしません。

ただし、Object.freeze自分でオブジェクト/配列の新しいインスタンスを作成する必要があり、それらは構造的な共有を持ちません。したがって、すべてを深くコピーする必要があるすべての変更、および古いコレクションはガベージコレクションされます。

一方、immutablejsはコレクションを管理します。何かが変更されると、新しいインスタンスは変更されていない古いインスタンスの部分を使用するため、コピーとガベージコレクションが少なくなります。

4
Ori Drori

心に浮かぶ最大の理由-不変の更新を支援する機能的なAPIを持っていること以外に、Immutable.jsで利用される構造共有です。強制的な不変性が必要なアプリケーションがある場合(つまり、Reduxを使用している場合)、Object.freezeのみを使用している場合は、すべての「突然変異」のコピーを作成します。これは、GCの処理につながるため、時間の経過とともに実際には効率的ではありません。 Immutable.jsを使用すると、immutableから返されるデータ構造がTriesであるため、構造共有がベイクインされます(オブジェクトプール/独自の構造共有モデルを実装する必要はありません)。これは、すべての突然変異がデータ構造内で引き続き参照されるため、GCスラッシングが最小限に抑えられることを意味します。これについての詳細は、Immutable.jsのドキュメント(および作成者のLee Byronにより詳細に説明されている素晴らしいビデオ)にあります。

https://facebook.github.io/immutable-js/

1

Object.freezeはネイティブでディープフリーズを行いません。immutable.jsが行うと考えています。

どのライブラリでも同じです-アンダースコア、jqueryなどを使用する理由.

他の人が作った車輪を再利用するのが好きな人:-)

1
Tuvia

Object.freeze()とimmutable.jsにはいくつかの大きな違いがあります。

最初にパフォーマンスコストに対処しましょう。 Object.freeze()は浅いです。オブジェクトは不変になりますが、そのオブジェクト内のネストされたプロパティとメソッドは変更できます。 Object.freeze()documentation はこれに対処し、パフォーマンスの点でさらにコストのかかる「deepFreeze」機能を提供します。一方、Immutable.jsは、オブジェクト全体(ネストされたプロパティ、メソッドなど)を低コストで不変にします。

さらに、不変変数のクローンを作成する必要がある場合、Object.freeze()は完全に新しい変数を作成するように強制しますが、Immutable.jsは既存の不変変数を再利用してクローンをより効率的に作成できます。 この記事 からのこれに関する興味深い引用です:

「.set()のような不変のメソッドは、新しいオブジェクトが古いオブジェクトのデータを参照できるようにするため、クローニングよりも効率的です。変更されたプロパティのみが異なります。

一言で言えば、Immutable.jsは、古い不変変数と新しい不変変数の間に論理的な接続を作成するため、クローン作成のパフォーマンスが向上し、フリーズした変数がメモリを占有します。 Object.freeze()は悲しいことにそうではありません-凍結したオブジェクトから新しい変数を複製するたびに、基本的にすべてのデータを書き直し、(何らかの奇妙な理由で)それらが同一であってもデータ。

したがって、パフォーマンスの観点から、特にプログラムで不変の変数を常に使用する場合、Immutable.jsが最適です。ただし、パフォーマンスだけがすべてではなく、Immutable.jsを使用する上で大きな注意事項があります。 Immutable.jsは独自のデータ構造を使用しているため、デバッグを行うだけでなく、コンソールにデータを記録するだけでも大変な苦労があります。また、JavaScriptの基本的な機能が失われる可能性があります(たとえば、 ES6の構造化解除は使用できません ) Facebook内でのみ)、簡単な問題が発生した場合でも多くのウェブ検索が必要です。

これが両方のアプローチの最も重要な側面をカバーし、どちらが最適かを判断するのに役立つことを願っています。

1
Nadav