web-dev-qa-db-fra.com

ECMAScript 6: à quoi sert WeakSet?

Le WeakSet est supposé stocker les éléments par faible référence. Autrement dit, si un objet n'est référencé par rien d'autre, il doit être nettoyé du WeakSet.

J'ai écrit le test suivant:

var weakset = new WeakSet(),
    numbers = [1, 2, 3];

weakset.add(numbers);
weakset.add({name: "Charlie"});

console.log(weakset);

numbers = undefined;

console.log(weakset);

Bien que mon tableau [1, 2, 3] ne soit référencé par rien, il n'est pas supprimé du WeakSet. La console imprime:

WeakSet {[1, 2, 3], Object {name: "Charlie"}}
WeakSet {[1, 2, 3], Object {name: "Charlie"}}

Pourquoi donc?

De plus, j'ai une autre question. Quel est l'intérêt d'ajouter directement des objets aux WeakSets, comme ceci:

weakset.add({name: "Charlie"});

Est-ce que ces problèmes de Traceur ou est-ce que je manque quelque chose?

Et enfin, quelle est l'utilisation pratique de WeakSet si nous ne pouvons même pas le parcourir ni obtenir la taille actuelle?

45
Robo Robok

il n'est pas supprimé du WeakSet. Pourquoi donc?

Probablement parce que le ramasse-miettes n'a pas encore été exécuté. Cependant, vous dites que vous utilisez Traceur, il est donc possible qu’ils ne soient pas correctement pris en charge. Je me demande comment la console peut afficher le contenu d'une WeakSet de toute façon.

Quel est l'intérêt d'ajouter directement des objets aux WeakSets?

Il est absolument inutile d'ajouter des littéraux d'objet à WeakSets.

Quelle est l'utilisation pratique de WeakSet si nous ne pouvons même pas le parcourir ni obtenir la taille actuelle?

Tout ce que vous pouvez obtenir est un bit d’information: l’objet (ou la valeur de manière générique) est-il contenu dans l’ensemble?

Cela peut être utile dans les cas où vous souhaitez "baliser" des objets sans les transformer réellement (leur attribuer une propriété). Beaucoup d'algorithmes contiennent une sorte de condition "si x a déjà été vue" (une détection de cycle JSON.stringify pourrait être un bon exemple), et lorsque vous travaillez avec des valeurs fournies par l'utilisateur, il est conseillé d'utiliser une variable Set/WeakSet. L’avantage d’une WeakSet ici est que son contenu peut être nettoyé pendant que votre algorithme est toujours en cours d’exécution. Il est donc utile de réduire la consommation de mémoire (voire même d’éviter les fuites) lorsque vous traitez beaucoup de données paresseusement (voire même de manière asynchrone). ) produit.

46
Bergi

C'est une question vraiment difficile. Pour être tout à fait honnête, je ne savais pas du tout dans le contexte de JavaScript alors j’ai posé la question in esdiscuss et j’ai obtenu une réponse convaincante de Domenic .

Les WeakSets sont utiles pour sécurité _ et validation raisons. Si vous voulez pouvoir isoler un morceau de JavaScript. Ils vous permettent de taguer un objet pour indiquer qu'il appartient à un ensemble d'objets spécifique.

Disons que j'ai une classe ApiRequest:

class ApiRequest {
  constructor() {
    // bring object to a consistent state, use platform code you have no cirect access to
  }

  makeRequest() {
    // do work 
  }
}

Maintenant, j'écris une plate-forme JavaScript - ma plate-forme vous permet d'utiliser JavaScript pour passer des appels - pour passer ces appels dont vous avez besoin d'un ApiRequest - Je veux seulement que vous fassiez des ApiRequests avec les objets que je vous ai donnés pour que vous ne puissiez en contourner contraintes que j'ai en place.

Cependant, pour le moment rien ne vous empêche de faire:

ApiRequest.prototype.makeRequest.call(null, args); // make request as function
Object.create(ApiRequest.prototype).makeRequest(); // no initialization
function Foo(){}; Foo.prototype = ApiRequest.prototype; new Foo().makeRequest(); // no super

Et ainsi de suite, notez que vous ne pouvez pas conserver une liste ou un tableau normal d'objets ApiRequest car cela les empêcherait d'être récupérés. Autre qu'une fermeture, tout peut être réalisé avec des méthodes publiques comme Object.getOwnPropertyNames ou Object.getOwnSymbols. Alors tu me montes et fais:

const requests = new WeakSet();
class ApiRequest {
  constructor() {
    requests.add(this);
  }

  makeRequest() {
    if(!request.has(this)) throw new Error("Invalid access");
    // do work
  }
}

Maintenant, peu importe ce que je fais - je dois détenir un objet ApiRequest valide pour appeler la méthode makeRequest dessus. Ceci est impossible sans WeakMap/WeakSet.

En bref, WeakMaps sont utiles pour écrire des plates-formes dans JavaScirpt. Normalement, ce type de validation est effectué du côté C++, mais l’ajout de ces fonctionnalités permettra de déplacer et de créer des éléments en JavaScript.

(Bien sûr, tout ce que fait un WeakSet fait un WeakMap qui mappe des valeurs sur true peut également le faire, mais cela est vrai pour toute construction map/set)

(Comme le suggère la réponse de Bergi, il n'y a jamais de raison d'ajouter un objet littéral directement à un WeakMap ou à un WeakSet)

25

Par définition, WeakSet n'a que trois fonctionnalités clés

  • Lier faiblement un objet à l'ensemble
  • Supprimer un lien vers un objet de l'ensemble
  • Vérifier si un objet a déjà été lié à l'ensemble

Cela semble plus familier?

Dans certaines applications, les développeurs peuvent avoir besoin de mettre en place un moyen rapide pour parcourir une série de données polluées par de nombreuses redondances mais vous ne voulez choisir que celles qui n'ont pas encore été traitées (unique). WeakSet pourrait vous aider. Voir un exemple ci-dessous:

var processedBag = new WeakSet();
var nextObject = getNext();
while (nextObject !== null){
    // Check if already processed this similar object?
    if (!processedBag.has(nextObject)){
        // If not, process it and memorize 
        process(nextObject);
        processedBag.add(nextObject);
    }
    nextObject = getNext();
}

L'une des meilleures structures de données pour l'application ci-dessus est filtre de Bloom, ce qui est très bien pour une taille de données massive. Cependant, vous pouvez également utiliser WeakSet.

11
TaoPR

Un ensemble ou une carte "faible" est utile lorsque vous devez conserver une collection d'éléments arbitraire, mais que vous ne voulez pas que leur présence dans la collection empêche ces éléments d'être collectés de manière illisible si la mémoire est saturée. (Si garbage collection existe se produit, les objets "récoltés" disparaîtront en silence de la collection, ce qui vous permettra de savoir s'ils ont disparu.) 

Ils sont excellents, par exemple, pour une utilisation en tant que cache en regard: "ai-je déjà récupéré cet enregistrement, récemment?" Chaque fois que vous récupérez quelque chose, placez-le dans la carte, sachant que le garbage collector JavaScript sera celui qui sera chargé de "rogner la liste" pour vous, et qu'il le fera automatiquement en réponse à la mémoire en vigueur conditions (que vous ne pouvez pas raisonnablement anticiper).

Le seul inconvénient est que ces types ne sont pas "énumérables". Vous ne pouvez pas parcourir une liste d'entrées - probablement parce que cela toucherait probablement ces entrées et irait à l'encontre du but recherché. Mais, c’est un petit prix à payer (et vous pourriez , si besoin est, "code autour").

2
Mike Robinson

Votre console affichait probablement le contenu de manière incorrecte en raison du fait que la récupération de place n'avait pas encore eu lieu. Par conséquent, puisque l'objet n'a pas été récupéré, l'objet sera toujours dans un jeu faible.

Si vous voulez vraiment voir si un faible ensemble a toujours une référence à un certain objet, utilisez la méthode WeakSet.prototype.has(). Cette méthode, comme son nom l’indique, retourne une boolean indiquant si l’objet existe toujours dans le faible jeu.

Exemple:

var weakset = new WeakSet(),
    numbers = [1, 2, 3];

weakset.add(numbers);
weakset.add({name: "Charlie"});

console.log(weakset.has(numbers));

numbers = undefined;

console.log(weakset.has(numbers));

1

WeakSet est une simplification de WeakMap car votre valeur sera toujours booléenne. Il vous permet de baliser des objets JavaScript afin de ne les utiliser qu'une seule fois ou de conserver leur état par rapport à un processus donné. En théorie, comme il n'est pas nécessaire de conserver une valeur, il devrait utiliser un peu moins de mémoire et fonctionner légèrement plus vite que WeakMap.

var [touch, untouch] = (() => {
    var seen = new WeakSet();
    return [
        value => seen.has(value)) || (seen.add(value), !1),
        value => !seen.has(value) || (seen.delete(value), !1)
    ];
})();

function convert(object) {
    if(touch(object)) return;
    extend(object, yunoprototype); // Made up.
};

function unconvert(object) {
    if(untouch(object)) return;
    del_props(object, Object.keys(yunoprototype)); // Never do this IRL.
};
1
jgmjgm