web-dev-qa-db-fra.com

Comment obtenir des références d'objet javascript ou un nombre de références?

Comment obtenir le nombre de références pour un objet

  • Est-il possible de déterminer si un objet javascript contient plusieurs références?
  • Ou s'il a des références en plus de celle avec laquelle j'y accède?
  • Ou même juste pour obtenir le nombre de références lui-même?
  • Puis-je trouver ces informations à partir de javascript lui-même, ou dois-je garder une trace de mes propres compteurs de référence.

Évidemment, il doit y avoir au moins une référence pour que mon code accède à l'objet. Mais ce que je veux savoir, c'est s'il y a d'autres références ou si mon code est le seul endroit auquel il est accessible. Je voudrais pouvoir supprimer l'objet si rien d'autre ne le référence.

Si vous connaissez la réponse, il n'est pas nécessaire de lire le reste de cette question. Voici un exemple pour clarifier les choses.


Cas d'utilisation

Dans mon application, j'ai une instance d'objet Repository appelée contacts qui contient un tableau de TOUS mes contacts. Il existe également plusieurs instances d'objet Collection, telles que la collection friends et une collection coworkers. Chaque collection contient un tableau avec un ensemble différent d'éléments de contactsRepository.

Exemple de code

Pour rendre ce concept plus concret, considérez le code ci-dessous. Chaque instance de l'objet Repository contient une liste de tous les éléments d'un type particulier. Vous pourriez avoir un référentiel de Contacts et un référentiel séparé de Events. Pour rester simple, vous pouvez simplement obtenir, ajouter et supprimer des éléments et en ajouter plusieurs via le constructeur.

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;
    }
  }
}  

Notez la ligne dans remove avec le commentaire. Je souhaite uniquement supprimer l'élément de mon référentiel d'objets principal si aucun autre objet ne fait référence à l'élément. Voici 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;
    }
  }
}

Et puis ce code utilise Repository et Collection:

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

Remarquez comment coworkers.remove(2) n'a pas supprimé id 2 de contactRepo? En effet, il était toujours référencé à partir de friends.items. Cependant, friends.remove(4) entraîne la suppression de l'id 4 de contactRepo, car aucune autre collection n'y fait référence.

Résumé

Ce qui précède est ce que je veux faire. Je suis sûr qu'il existe des moyens de le faire en gardant une trace de mes propres compteurs de référence et autres. Mais s'il existe un moyen de le faire en utilisant la gestion de référence intégrée de javascript, j'aimerais savoir comment l'utiliser.

57
Tauren

Non Non Non Non; et oui, si vous avez vraiment besoin de compter les références, vous devrez le faire manuellement. JS n'a pas d'interface avec cela, GC ou des références faibles.

Alors que vous pourriez implémenter une liste d'objets comptés par référence manuelle, il est douteux que tous les frais supplémentaires (en termes de performances, mais surtout la complexité du code) en valent la peine.

Dans votre exemple de code, il semblerait plus simple d'oublier le Repository, d'utiliser un simple Array pour vos listes et de laisser le garbage collection standard se charger de supprimer les personnes inutilisées. Si vous aviez besoin d'obtenir une liste de toutes les personnes utilisées, il vous suffit de concat les listes friends et coworkers (et de les trier/unifier si vous en avez besoin).

16
bobince

Vous pouvez être intéressé par les fonctions de réduction et les fonctions array.map. la carte pourrait être utilisée pour aider à identifier où vos collections se croisent, ou s'il y a une intersection. Une fonction de réduction définie par l'utilisateur peut être utilisée comme une fusion (un peu comme remplacer l'opérateur d'addition pour que vous puissiez appliquer une opération aux objets, ou fusionner toutes les collections sur "id" si c'est ainsi que vous définissez votre fonction de réduction - puis attribuez le résultat à votre tableau de référence principal, je vous recommande de conserver un tableau d'ombres qui contient tous les objets/valeurs racine au cas où vous voudriez REWIND ou quelque chose). Remarque: il faut faire attention aux chaînes de prototypes lors de la réduction d'un objet ou d'un tableau. La fonction de carte sera très utile dans ce cas.

Je suggérerais de ne pas supprimer l’objet ou l’enregistrement qui se trouve dans votre référentiel car vous voudrez peut-être le référencer plus tard. Mon approche serait de créer un ShadowRepository qui refléterait tous les enregistrements/objets qui ont au moins une "référence". D'après votre description et votre code présentés ici, il semble que vous initialisiez toutes les données et stockiez la référence à 1,2,4,5 comme indiqué dans votre code.

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

À partir de l'initialisation du référentiel et des collections, ce que vous demandez "Supprimer l'élément du référentiel s'il n'y a aucune référence" doit être supprimé immédiatement. Vous pouvez cependant suivre les références de différentes manières.

J'ai envisagé d'utiliser Object.observe pour une situation similaire. Cependant, Object.observe ne fonctionne pas dans tous les navigateurs. je me suis récemment tourné vers WatchJS

Je travaille sur la compréhension du code derrière Watch.JS pour permettre la création dynamique d'une liste d'observateurs sur un objet, cela permettrait également de supprimer un élément qui n'est plus surveillé, bien que je suggère de supprimer la référence au point de départ. access - Ce que je veux dire est une variable qui partage la portée lexicale immédiate avec un objet qui a donné un point de référence unique à son frère peut être supprimée, ce qui la rend plus accessible en dehors de l'objet qui a exposé l'enregistrement/l'élément/la propriété/objet de son frère. Avec la référence que toutes vos autres références dépendaient de l'accès supprimé aux données sous-jacentes est arrêté. Je génère un identifiant unique pour les références d'origine pour éviter de réutiliser accidentellement le même.

Merci d'avoir partagé votre question et la structure que vous utilisez, cela m'a aidé à considérer l'un de mes propres cas spécifiques où je générais des références identifiées de manière unique à un frère lexical, ces identifiants uniques ont été conservés sur le seul objet qui avait une portée, après la lecture ici, j'ai reconsidéré et décidé d'exposer une seule référence, puis d'affecter cette référence à un nom de variable partout où cela est nécessaire, comme dans la création d'un observateur ou d'un observateur ou d'une autre collection.

1
alignedfibers