循環参照を使用したデータ構造の例を次に示します。

function makeToolshed(){
var nut = {name: 'nut'}, bolt = {name: 'bolt'};
nut.needs = bolt; bolt.needs = nut;
return { nut: nut, bolt: bolt };
}
循環参照を保持したい場合(逆シリアル化するときに、それらを「ナック」するのではなく復元する)、2つの選択肢があります。ここで比較します。1つ目はDouglasCrockfordのcycle.jsで、2つ目は私のsiberiaパッケージです。どちらも、最初にオブジェクトを「逆循環」することによって機能します。つまり、「同じ情報を含む」別のオブジェクトを(循環参照なしで)構築します。
Crockford氏が最初に行きます:
JSON.decycle(makeToolshed())

ご覧のとおり、JSONのネストされた構造は保持されていますが、新しいものがあります。それは、特別な$ref
プロパティを持つオブジェクトです。それがどのように機能するか見てみましょう。
root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]
ドル記号はルートを表します。これは「すでに見た」オブジェクトであり、その特別なプロパティの値(ここでは、文字列$ ["nut"] ["needs"])がどこにあるかを示しています。上記を最初に参照して.bolt
ください。上記の2番目と2番目についても同様です。$ref
.bolt
===
$ref
===
適切なディープイコリティテスト(つまり、この質問deepGraphEqual
に対する受け入れられた回答からのAnders Kaseorgの関数)を使用して、クローン作成が機能するかどうかを確認しましょう。
root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true
さて、シベリア:
JSON.Siberia.forestify(makeToolshed())

Siberiaは、ネストされた構造ではなく、「クラシック」JSONを模倣しようとはしません。オブジェクトグラフは「フラット」な方法で記述されます。オブジェクトグラフの各ノードはフラットツリー(整数のみの値を持つプレーンキー値ペアリスト)に変換されます。これは、.forest.
インデックス0でルートオブジェクトを見つけ、より高いインデックスで他のノードを見つけます。オブジェクトグラフと(フォレストのツリーのキーの)負の値は、atoms
配列を指します(types配列を介して入力されますが、ここでは入力の詳細をスキップします)。すべての終端ノードはアトムテーブルにあり、すべての非終端ノードはフォレストテーブルにあり、オブジェクトグラフにあるノードの数、つまり、をすぐに確認できますforest.length
。それが機能するかどうかをテストしてみましょう:
root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true
比較
後でセクションを追加します。
ノート
現在、パッケージをリファクタリングしています。中心的なアイデアとアルゴリズムは同じままですが、新しいバージョンは使いやすくなり、トップレベルのAPIは異なります。間もなくシベリアをアーカイブし、リファクタリングされたバージョンを提示します。これをオブジェクトグラフと呼びます。今月(2020年8月)に開催されますので、ご期待ください。
ああ、そして比較のための超短バージョン。「ポインタ」の場合、「すでに表示されているノードへのポインタ」(実際には、すでに表示されているかどうかに関係なく、すべてのノードへのポインタ)は単なる整数であるため、整数と同じくらいのスペースが必要です。Crockford氏のバージョンでは、「ポインタ」を格納するために必要な量は、オブジェクトグラフのサイズによってのみ制限されます。それは、クロックフォード氏のバージョンの最悪の場合の複雑さを非常に恐ろしいものにします。クロックフォード氏は私たちに「別のバブルソート」をくれました。私はあなたをからかっていません。それは悪いです。信じられない場合は、テストがあります。パッケージのreadmeから開始してテストを見つけることができます(2020年8月の今月もbenchmark.jsに準拠するように変換されます)