Comment effacer rapidement un objet JavaScript?

170

Avec un tableau JavaScript, je peux le réinitialiser à un état vide avec une seule affectation:

array.length = 0;

Cela fait «apparaître» le tableau vide et prêt à être réutilisé, et pour autant que je sache, il s'agit d'une seule «opération» - c'est-à-dire du temps constant.

Existe-t-il une méthode similaire pour effacer un objet JS? Je sais que je peux itérer ses champs en les supprimant:

for (var prop in obj) { if (obj.hasOwnProperty(prop)) { delete obj[prop]; } }

mais cela a une complexité linéaire.

Je peux aussi simplement jeter l'objet et en créer un nouveau:

obj = {};

Mais la création "promiscuous" de nouveaux objets conduit à des problèmes avec Garbage Collection sur IE6. ( Comme décrit ici )

Levik
la source
2
"array.length == 0 ... est une seule 'opération' - c'est-à-dire un temps constant" - j'en doute.
Miles
1
Je ne crois pas que cela supprime le contenu - fait simplement fonctionner des choses comme push () comme si le tableau était vide. Avez-vous une référence au contraire qui est vrai?
levik
2
@derobert: C'est un peu présomptueux. Le problème de Garbage Collection d'IE6 est bien documenté.
levik
Le code dans le message d'origine est incorrect. La boucle interne doit être: delete obj [prop]. Voir ma publication stackoverflow.com/questions/6780315/…
stackoverflowuser2010
6
pour tous ceux qui utilisent cet extrait, utilisezfor (var prop in obj)
bendytree

Réponses:

54

La réponse courte à votre question, je pense, est non (vous pouvez simplement créer un nouvel objet).

  1. Dans cet exemple, je pense que la définition de la longueur à 0 laisse toujours tous les éléments pour le ramasse-miettes.

  2. Vous pouvez l'ajouter à Object.prototype si c'est quelque chose que vous utilisez fréquemment. Oui, sa complexité est linéaire, mais tout ce qui ne fera pas de ramasse-miettes plus tard le sera.

  3. C'est la meilleure solution. Je sais que ce n'est pas lié à votre question - mais pendant combien de temps devons-nous continuer à soutenir IE6? Il existe de nombreuses campagnes pour arrêter son utilisation.

N'hésitez pas à me corriger s'il y a quelque chose d'incorrect ci-dessus.

jthompson
la source
4
Certaines entreprises doivent soutenir IE6 comme une question de politique - et le feront tant qu'elle jouit d'une part de marché à deux chiffres. Le problème du GC IE n'est pas que les choses ne sont pas collectées, c'est que la collection exécute toutes les allocations X et prend plus de temps à chaque fois. D'où la nécessité de réutiliser des objets.
levik
Oui, il y a de nombreux cas où il est encore utilisé en raison de la politique de l'entreprise / etc. J'étais hors du classement du sujet par dépit :) Alors, comment supprimer obj.prop; effectuer lorsque la propriété est elle-même un objet? Je ne sais pas si vous y gagnez beaucoup d'efficacité.
jthompson
2
Un GC médiocre signifie qu'IE6 fonctionnera plus lentement, ce qui signifie qu'il y a encore plus d'incitation à mettre à niveau. Vous le soutenez toujours, c'est juste que ça va tourner lentement.
nickf
1
Cela signifie que votre application fonctionne mal pour une partie de votre public cible. Certaines personnes n'ont pas la possibilité de procéder à une mise à niveau car elles se trouvent dans un environnement informatique contrôlé. Peut-être que leur entreprise utilise un contrôle Active-X qui ne fonctionne qu'avec IE6.
levik
2
@levik ne fonctionne tout simplement pas pour une entreprise qui vous oblige à soutenir IE6. cela ne peut pas être une bonne entreprise de toute façon.
low_rents
121

Eh bien, au risque de rendre les choses trop faciles ...

for (var member in myObject) delete myObject[member];

... semble être assez efficace pour nettoyer l'objet dans une ligne de code avec un minimum de crochets effrayants. Tous les membres seront vraiment supprimés au lieu d'être laissés à la poubelle.

Évidemment, si vous souhaitez supprimer l'objet lui-même, vous devrez toujours faire un delete () séparé pour cela.

Wytze
la source
2
La suppression ne fait que casser la référence, par exemple, elle rend monObjet [membre] évalué comme indéfini, et laisse techniquement l'objet comme une poubelle à moins qu'une autre référence à l'objet n'existe.
Joey Carson
Cette réponse est très responsable. S'il est appliqué à tous les objets gérés lors d'une phase d'élimination, il s'occupera généralement de la plupart des chaînes d'objets accidentelles. rock on Wytze
deepelement
1
L'OP suggère d'utiliser hasOwnPropertydans cette boucle. J'ai lu la documentation, mais j'essaie toujours de comprendre quels sont les risques, le cas échéant, si nous omettons le hasOwnProperty()chèque?
logidelic
2
Je pensais qu'il était dangereux de supprimer des propriétés lors de l'itération sur l'objet - dans la plupart des langues, cela invaliderait l'itérateur et casserait les choses, de manière subtile ou catastrophique - mais apparemment, c'est OK en JavaScript par MDN: "Une propriété qui est supprimée avant il a été visité ne sera pas visité plus tard. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Piotr
46

ES5

La solution ES5 peut être:

// for enumerable and non-enumerable properties
Object.getOwnPropertyNames(obj).forEach(function (prop) {
  delete obj[prop];
});

ES6

Et la solution ES6 peut être:

// for enumerable and non-enumerable properties
for (const prop of Object.getOwnPropertyNames(obj)) {
  delete obj[prop];
}

Performance

Quelles que soient les spécifications, les solutions les plus rapides seront généralement:

// for enumerable and non-enumerable of an object with proto chain
var props = Object.getOwnPropertyNames(obj);
for (var i = 0; i < props.length; i++) {
  delete obj[props[i]];
}

// for enumerable properties of shallow/plain object
for (var key in obj) {
  // this check can be safely omitted in modern JS engines
  // if (obj.hasOwnProperty(key))
    delete obj[key];
}

La raison pour laquelle for..inne doit être effectué que sur un objet peu profond ou simple est qu'il traverse les propriétés qui sont héritées de manière prototypique, pas seulement les propriétés propres qui peuvent être supprimées. Dans le cas où l'on ne sait pas avec certitude qu'un objet est simple et que les propriétés sont énumérables, foravec Object.getOwnPropertyNamesest un meilleur choix.

Fiole d'Estus
la source
7

Vous pouvez essayer ceci. La fonction ci-dessous définit toutes les valeurs des propriétés de l'objet sur indéfinies. Fonctionne également avec les objets imbriqués.

var clearObjectValues = (objToClear) => {
    Object.keys(objToClear).forEach((param) => {
        if ( (objToClear[param]).toString() === "[object Object]" ) {
            clearObjectValues(objToClear[param]);
        } else {
            objToClear[param] = undefined;
        }
    })
    return objToClear;
};
Alina Poluykova
la source
Notez que les champs ne sont définis que comme non définis, cela entraînera des fuites de mémoire au fil du temps car les champs ne seront pas effacés par le garbage collector.
Alexis Tyler
7

Donc, pour récapituler votre question: vous voulez éviter, autant que possible, les problèmes avec le bogue IE6 GC. Ce bug a deux causes:

  1. Le nettoyage de la mémoire se produit une fois toutes les allocations ; par conséquent, plus vous effectuez d'allocations, le GC fonctionnera souvent;
  2. Plus vous avez d'objets `` en l'air '', plus chaque exécution de Garbage Collection prend de temps (car elle parcourt toute la liste des objets pour voir lesquels sont marqués comme des déchets).

La solution à la cause 1 semble être: réduire le nombre d'allocations; attribuez le moins possible de nouveaux objets et chaînes.

La solution à la cause 2 semble être: réduire le nombre d'objets «vivants»; supprimez vos chaînes et objets dès que vous n'en avez plus besoin et créez-les à nouveau si nécessaire.

Dans une certaine mesure, ces solutions sont contradictoires: maintenir le nombre d'objets en mémoire bas entraînera davantage d'allocations et de désallocations. À l'inverse, réutiliser constamment les mêmes objets pourrait signifier garder plus d'objets en mémoire que strictement nécessaire.


Maintenant pour votre question. Que vous réinitialisiez un objet en créant un nouveau, ou en supprimant toutes ses propriétés: cela dépendra de ce que vous voulez en faire par la suite.

Vous voudrez probablement lui attribuer de nouvelles propriétés:

  • Si vous le faites immédiatement, je vous suggère d'attribuer immédiatement les nouvelles propriétés et d'ignorer d'abord la suppression ou l'effacement. (Assurez-vous cependant que toutes les propriétés sont écrasées ou supprimées!)
  • Si l'objet ne sera pas utilisé immédiatement, mais sera repeuplé ultérieurement, je suggère de le supprimer ou de lui attribuer une valeur nulle et d'en créer un nouveau plus tard.

Il n'y a pas de moyen rapide et facile à utiliser pour effacer un objet JScript pour le réutiliser comme s'il s'agissait d'un nouvel objet - sans en créer un nouveau. Ce qui signifie que la réponse courte à votre question est «Non», comme le dit jthompson.

Martijn
la source
4

Quelque chose de nouveau à penser dans l'attente d'Object.observe dans ES7 et avec la liaison de données en général. Considérer:

var foo={
   name: "hello"
};

Object.observe(foo, function(){alert('modified');}); // bind to foo

foo={}; // You are no longer bound to foo but to an orphaned version of it
foo.name="there"; // This change will be missed by Object.observe()

Donc, dans cette circonstance, le n ° 2 peut être le meilleur choix.

Terry Thorsen
la source
Si vous utilisez des frameworks tels que AngularJS qui peuvent lier des objets à des vues (liaison un ou deux sens), effacer l'objet avec obj = {}rendra le framework inconscient de toute modification ultérieure de l'objet, donc vos modèles ne seront pas correctement rendus. L'option n ° 2 fonctionnera cependant correctement.
Ivan Hušnjak
Bon point. J'ai besoin de conserver le même objet de tas, et cela démontre une autre raison pour laquelle vous voudriez conserver la même référence.
Cody
1

Vous pouvez supprimer les accessoires, mais ne supprimez pas les variables. delete abc;n'est pas valide dans ES5 (et lance avec use strict).

Vous pouvez l'attribuer à null pour le définir pour la suppression dans le GC (ce ne sera pas le cas si vous avez d'autres références à des propriétés)

Définir une lengthpropriété sur un objet ne change rien. (il ne définit que la propriété)

Ven
la source
Pour être clair, lors de la mise lengthà 0 sur un tableau (qui est un type d'objet spécifique - si vous ne me croyez pas, essayez de courir typeof []), cela ne définira pas seulement la propriété, cela effacera en fait le contenu du tableau . Voir stackoverflow.com/questions/1232040/…
Sean the Bean
Eh bien, vous pouvez dire supprimer window.abccar c'est un accessoire dans ce cas.
shaedrich
0

Cela m'a dérangé pendant des siècles alors voici ma version car je ne voulais pas d'objet vide, j'en voulais un avec toutes les propriétés mais réinitialisé à une valeur par défaut. Un peu comme une nouvelle instanciation d'une classe.

let object1 = {
  a: 'somestring',
  b: 42,
  c: true,
  d:{
    e:1,
    f:2,
    g:true,
    h:{
      i:"hello"
    }
  },
  j: [1,2,3],
  k: ["foo", "bar"],
  l:["foo",1,true],
  m:[{n:10, o:"food", p:true }, {n:11, o:"foog", p:true }],
  q:null,
  r:undefined
};

let boolDefault = false;
let stringDefault = "";
let numberDefault = 0;

console.log(object1);
//document.write("<pre>");
//document.write(JSON.stringify(object1))
//document.write("<hr />");
cleanObject(object1);
console.log(object1);
//document.write(JSON.stringify(object1));
//document.write("</pre>");

function cleanObject(o) {
  for (let [key, value] of Object.entries(o)) {
    let propType = typeof(o[key]);

    //console.log(key, value, propType);

    switch (propType) {
      case "number" :
        o[key] = numberDefault;
        break;

      case "string":
        o[key] = stringDefault;
        break;

      case "boolean":
        o[key] = boolDefault;    
        break;

      case "undefined":
        o[key] = undefined;   
        break;

      default:
        if(value === null) {
            continue;
        }

        cleanObject(o[key]);
        break;
    }
  }
}

// EXPECTED OUTPUT
// Object { a: "somestring", b: 42, c: true, d: Object { e: 1, f: 2, g: true, h: Object { i: "hello" } }, j: Array [1, 2, 3], k: Array ["foo", "bar"], l: Array ["foo", 1, true], m: Array [Object { n: 10, o: "food", p: true }, Object { n: 11, o: "foog", p: true }], q: null, r: undefined }
// Object { a: "", b: 0, c: undefined, d: Object { e: 0, f: 0, g: undefined, h: Object { i: "" } }, j: Array [0, 0, 0], k: Array ["", ""], l: Array ["", 0, undefined], m: Array [Object { n: 0, o: "", p: undefined }, Object { n: 0, o: "", p: undefined }], q: null, r: undefined }

ozzy432836
la source