web-dev-qa-db-ja.com

JavaScriptオブジェクト参照または参照カウントを取得するにはどうすればよいですか?

オブジェクトの参照カウントを取得する方法

  • Javascriptオブジェクトに複数参照があるかどうかを判断することはできますか?
  • または、参照がある場合私がそれにアクセスしているもの以外
  • または参照カウント自体を取得するだけですか?
  • この情報をjavascript自体から見つけることはできますか、それとも自分の参照カウンターを追跡する必要がありますか?.

明らかに、コードがオブジェクトにアクセスするには、少なくとも1つの参照が必要です。しかし、私が知りたいのは、他の参照があるかどうか、またはコードがアクセスされる唯一の場所かどうかです。他に何も参照していないオブジェクトを削除できるようにしたいと思います。

答えがわかっている場合は、この質問の残りを読む必要はありません。以下は、より明確にするための例です。


使用事例

私のアプリケーションでは、_[〜#〜] all [〜#〜]連絡先の配列を含むRepositoryというcontactsオブジェクトインスタンスがあります。 Collectionコレクションやfriendsコレクションなど、複数のcoworkersオブジェクトインスタンスもあります。各コレクションには、contactsRepositoryの異なるアイテムのセットを含む配列が含まれています。

サンプルコード

この概念をより具体的にするには、以下のコードを検討してください。 Repositoryオブジェクトの各インスタンスには、特定のタイプのすべてのアイテムのリストが含まれています。 ContactsのリポジトリとEventsの別のリポジトリがあるとします。シンプルにするために、項目を取得、追加、削除し、コンストラクターを介して多くを追加できます。

_var Repository = function(items) {
  this.items = items || [];
}
Repository.prototype.get = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      return this.items[i];
    }
  }
}
Repository.prototype.add = function(item) {
  if (toString.call(item) === "[object Array]") {
    this.items.concat(item);
  }
  else {
    this.items.Push(item);
  }
}
Repository.prototype.remove = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      this.removeIndex(i);
    }
  }
}
Repository.prototype.removeIndex = function(index) {
  if (items[index]) {
    if (/* items[i] has more than 1 reference to it */) {
      // Only remove item from repository if nothing else references it
      this.items.splice(index,1);
      return;
    }
  }
}  
_

コメントのあるremoveの行に注意してください。他のオブジェクトがアイテムへの参照を持たない場合にのみ、オブジェクトのマスターリポジトリからアイテムを削除します。 Collectionは次のとおりです。

_var Collection = function(repo,items) {
  this.repo = repo;
  this.items = items || [];
}
Collection.prototype.remove = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      // Remove object from this collection
      this.items.splice(i,1);
      // Tell repo to remove it (only if no other references to it)
      repo.removeIndxe(i);
      return;
    }
  }
}
_

そして、このコードはRepositoryCollectionを使用します:

_var contactRepo = new Repository([
    {id: 1, name: "Joe"},
    {id: 2, name: "Jane"},
    {id: 3, name: "Tom"},
    {id: 4, name: "Jack"},
    {id: 5, name: "Sue"}
  ]);

var friends = new Collection(
  contactRepo,
  [
    contactRepo.get(2),
    contactRepo.get(4)
  ]
);

var coworkers = new Collection(
  contactRepo,
  [
    contactRepo.get(1),
    contactRepo.get(2),
    contactRepo.get(5)
  ]
);

contactRepo.items; // contains item ids 1, 2, 3, 4, 5 
friends.items;  // contains item ids 2, 4
coworkers.items;  // contains item ids 1, 2, 5

coworkers.remove(2);

contactRepo.items; // contains item ids 1, 2, 3, 4, 5 
friends.items;  // contains item ids 2, 4
coworkers.items;  // contains item ids 1, 5

friends.remove(4);

contactRepo.items; // contains item ids 1, 2, 3, 5 
friends.items;  // contains item ids 2
coworkers.items;  // contains item ids 1, 5
_

coworkers.remove(2)がID 2をcontactRepoから削除しなかったことに注意してください。これは、_friends.items_からまだ参照されているためです。ただし、friends.remove(4)を使用すると、id 4はcontactRepoから削除されます。これは、他のコレクションがそれを参照していないためです。

概要

以上が私がやりたいことです。私は自分の参照カウンターなどを追跡することでこれを行うことができる方法があると確信しています。しかし、JavaScriptの組み込み参照管理を使用してそれを行う方法がある場合は、それを使用する方法について聞きたいです。

57
Tauren

いいえ、いいえ、いいえ、いいえ;はい、参照をカウントする必要がある場合は手動で行う必要があります。 JSには、これ、GC、または弱参照へのインターフェースがありません。

あなたがcould手動で参照カウントされるオブジェクトリストを実装している間、(パフォーマンスの観点ではより重要なのはコードの複雑さの)余分なオーバーヘッドがそれだけの価値があるかどうかは疑問です。

コード例では、Repositoryを忘れて、リストにプレーンなArrayを使用し、標準のガベージコレクションで未使用のユーザーの削除を処理する方が簡単に思えます。使用中のすべてのユーザーのリストを取得する必要がある場合は、concatfriendsおよびcoworkersリストを(必要に応じて並べ替え/一意化して)ください。

16
bobince

リデュース関数とarray.map関数を調べてみてください。 mapは、コレクションが交差する場所、または交差があるかどうかを識別するのに役立ちます。ユーザー定義のリデュース関数は、マージのように使用できます(オブジェクトに操作を適用できるように加算演算子をオーバーライドするようなものです)、または、それがリデュース関数を定義する方法である場合は「id」ですべてのコレクションをマージし、結果をマスター参照配列。REWINDまたは何かをしたい場合に備えて、すべてのルートオブジェクト/値を保持するシャドウ配列を保持することをお勧めします。注:オブジェクトまたは配列を減らすときは、プロトタイプチェーンに注意する必要があります。この場合、マップ機能は非常に役立ちます。

しないでください後で参照するために、リポジトリにあるオブジェクトまたはレコードを削除することをお勧めします。私のアプローチは、少なくとも1つの「参照」を持つすべてのレコード/オブジェクトを反映するShadowRepositoryを作成することです。ここに提示された説明とコードから、すべてのデータを初期化し、コードに表示されている1、2、4、5への参照を格納しているように見えます。

var contactRepo = new Repository([
    {id: 1, name: "Joe"},
    {id: 2, name: "Jane"},
    {id: 3, name: "Tom"},
    {id: 4, name: "Jack"},
    {id: 5, name: "Sue"}
]);
var friends = new Collection(contactRepo,[
    contactRepo.get(2),
    contactRepo.get(4)
]);

var coworkers = new Collection(contactRepo,[
    contactRepo.get(1),
    contactRepo.get(2),
    contactRepo.get(5)
]);

リポジトリとコレクションの初期化から、「参照がない場合はリポジトリからアイテムを削除する」という項目3をすぐに削除する必要があります。ただし、いくつかの方法で参照を追跡できます。

同様の状況でObject.observeを使用することを検討しました。ただし、Object.observeはすべてのブラウザで機能するわけではありません。 最近WatchJSに切り替えました

オブジェクトのオブザーバーのリストを動的に作成できるように、Watch.JSの背後にあるコードの理解に取り組んでいます。これにより、監視されなくなったアイテムも削除できるようになりますが、ポイントの参照を削除することをお勧めしますアクセス-つまり、兄弟を参照する単一のポイントを与えたオブジェクトと直接レキシカルスコープを共有する変数を削除して、レコード/アイテム/プロパティ/を公開したオブジェクトの外部からアクセスできないようにすることができますその兄弟のオブジェクト。他のすべての参照が基になるデータへの削除されたアクセスに依存していたという参照により、停止されます。誤って同じものを再利用しないように、Origin参照の一意のIDを生成しています。

質問と使用している構造を共有していただき、ありがとうございます。私が語彙の兄弟への一意に識別された参照を生成していた特定のケースの1つを検討するのに役立ちました。これらの一意のIDは、スコープを持つONEオブジェクトに保持されていました。ここで私は再考し、参照を1つだけ公開し、ウォッチャーまたはオブザーバーまたは他のコレクションを作成する場合など、必要に応じてその参照を変数名に割り当てることにしました。

1
alignedfibers