Wie lösche ich schnell ein JavaScript-Objekt?

170

Mit einem JavaScript-Array kann ich es mit einer einzigen Zuweisung auf einen leeren Zustand zurücksetzen:

array.length = 0;

Dadurch wird das Array "leer" und kann wiederverwendet werden. Soweit ich weiß, handelt es sich um eine einzelne "Operation", dh eine konstante Zeit.

Gibt es eine ähnliche Möglichkeit, ein JS-Objekt zu löschen? Ich weiß, dass ich seine Felder iterieren kann, um sie zu löschen:

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

Dies hat jedoch eine lineare Komplexität.

Ich kann das Objekt auch einfach wegwerfen und ein neues erstellen:

obj = {};

Die "promiskuitive" Erstellung neuer Objekte führt jedoch zu Problemen mit der Garbage Collection im IE6. ( Wie hier beschrieben )

Levik
quelle
2
"array.length == 0 ... ist eine einzelne 'Operation' - das heißt konstante Zeit" - das bezweifle ich.
Meilen
1
Ich glaube nicht, dass es irgendeinen Inhalt löscht - nur Dinge wie push () funktionieren so, als ob das Array leer wäre. Haben Sie einen Hinweis darauf, dass das Gegenteil der Fall ist?
Levik
2
@derobert: Das ist ein bisschen anmaßend. Das IE6 Garbage Collection-Problem ist gut dokumentiert.
Levik
Der Code im ursprünglichen Beitrag ist falsch. Die innere Schleife sollte sein: delete obj [prop]. Siehe meinen Beitrag stackoverflow.com/questions/6780315/…
stackoverflowuser2010
6
Für alle, die dieses Snippet verwenden, verwenden Siefor (var prop in obj)
Bendytree

Antworten:

54

Ich denke, die kurze Antwort auf Ihre Frage lautet Nein (Sie können einfach ein neues Objekt erstellen).

  1. In diesem Beispiel, glaube ich, bleiben beim Setzen der Länge auf 0 immer noch alle Elemente für die Speicherbereinigung übrig.

  2. Sie können dies zu Object.prototype hinzufügen, wenn Sie es häufig verwenden. Ja, die Komplexität ist linear, aber alles, was später keine Speicherbereinigung durchführt, wird es sein.

  3. Dies ist die beste Lösung. Ich weiß, dass es nicht mit Ihrer Frage zusammenhängt - aber wie lange müssen wir IE6 weiterhin unterstützen? Es gibt viele Kampagnen, um die Nutzung einzustellen.

Fühlen Sie sich frei, mich zu korrigieren, wenn oben etwas nicht stimmt.

jthompson
quelle
4
Einige Unternehmen müssen IE6 aus politischen Gründen unterstützen - und werden dies tun, während es einen zweistelligen Marktanteil hat. Das IE GC-Problem ist nicht, dass Dinge nicht erfasst werden, sondern dass die Erfassung alle X-Zuordnungen ausführt und jedes Mal länger dauert. Daher die Notwendigkeit, Objekte wiederzuverwenden.
Levik
Ja, es gibt viele Fälle, in denen es aufgrund von Unternehmensrichtlinien / etc. Ich war aus Trotz vom Thema entfernt :) Wie löscht obj.prop? durchführen, wenn die Eigenschaft selbst ein Objekt ist? Ich weiß nicht, ob Sie dort viel Effizienz gewinnen.
Thompson
2
Eine schlechte GC bedeutet, dass IE6 langsamer läuft, was bedeutet, dass es noch mehr Anreize für ein Upgrade gibt. Sie unterstützen es immer noch, es ist nur so, dass es langsam läuft.
Nickf
1
Dies bedeutet, dass Ihre App für einen Teil Ihrer Zielgruppe eine schlechte Leistung erbringt. Einige Benutzer haben keine Möglichkeit zum Aktualisieren, da sie sich in einer kontrollierten IT-Umgebung befinden. Möglicherweise verwendet ihre Firma ein Active-X-Steuerelement, das nur mit IE6 funktioniert.
Levik
2
@levik funktioniert einfach nicht für ein Unternehmen, das Sie zwingt, IE6 zu unterstützen. Das kann sowieso keine gute Gesellschaft sein.
low_rents
121

Nun, auf die Gefahr hin, die Dinge zu einfach zu machen ...

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

... scheint ziemlich effektiv zu sein, um das Objekt in einer Codezeile mit einem Minimum an gruseligen Klammern zu bereinigen. Alle Mitglieder werden wirklich gelöscht, anstatt als Müll zurückgelassen zu werden.

Wenn Sie das Objekt selbst löschen möchten, müssen Sie natürlich noch ein separates delete () dafür ausführen.

Wytze
quelle
2
Durch das Löschen wird nur die Referenz unterbrochen, z. B. wird myObject [member] als undefiniert ausgewertet, und das Objekt bleibt technisch als Müll übrig, sofern keine andere Referenz auf das Objekt vorhanden ist.
Joey Carson
Diese Antwort ist sehr verantwortlich. Wenn es in einer Entsorgungsphase auf alle verwalteten Objekte angewendet wird, werden im Allgemeinen viele der zufälligen Objektketten behandelt. Rock auf Wytze
Deepelement
1
Das OP schlägt vor, hasOwnPropertyin dieser Schleife zu verwenden. Ich habe die Dokumente gelesen, aber ich versuche immer noch, mich mit den Risiken zu befassen, wenn überhaupt, wenn wir die hasOwnProperty()Prüfung weglassen .
Logidelic
2
Ich dachte, es sei gefährlich, Eigenschaften zu löschen, während über das Objekt iteriert wird - in den meisten Sprachen würde dies den Iterator ungültig machen und die Dinge entweder subtil oder katastrophal zerstören -, aber anscheinend ist dies in JavaScript pro MDN in Ordnung: "Eine Eigenschaft, die zuvor gelöscht wurde es wurde besucht wird später nicht mehr besucht. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Piotr
46

ES5

ES5-Lösung kann sein:

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

ES6

Und ES6-Lösung kann sein:

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

Performance

Unabhängig von den Spezifikationen sind die schnellsten Lösungen im Allgemeinen:

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

Der Grund, warum for..innur für flache oder einfache Objekte ausgeführt werden sollte, besteht darin, dass die prototypisch vererbten Eigenschaften durchlaufen werden und nicht nur eigene Eigenschaften, die gelöscht werden können. Falls es nicht sicher bekannt , dass ein Objekt schlicht und Eigenschaften sind zählbare, formit Object.getOwnPropertyNameseiner besseren Wahl ist.

Estus Flask
quelle
7

Sie können dies versuchen. Die folgende Funktion setzt alle Werte der Objekteigenschaften auf undefiniert. Funktioniert auch mit verschachtelten Objekten.

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
quelle
Beachten Sie, dass die Felder nur als undefiniert festgelegt werden. Dies führt im Laufe der Zeit zu Speicherverlusten, da die Felder vom Garbage Collector nicht gelöscht werden.
Alexis Tyler
7

Um Ihre Frage noch einmal zusammenzufassen: Sie möchten Probleme mit dem IE6 GC-Fehler so weit wie möglich vermeiden. Dieser Fehler hat zwei Ursachen:

  1. Die Speicherbereinigung erfolgt einmal bei so vielen Zuordnungen . Je mehr Zuweisungen Sie vornehmen, desto häufiger wird der GC ausgeführt.
  2. Je mehr Objekte Sie in der Luft haben, desto länger dauert jeder Garbage Collection-Lauf (da er die gesamte Liste der Objekte durchsucht, um zu sehen, welche als Müll markiert sind).

Die Lösung für Ursache 1 scheint zu sein: Halten Sie die Anzahl der Zuweisungen niedrig; Weisen Sie so wenig wie möglich neue Objekte und Zeichenfolgen zu.

Die Lösung für Ursache 2 scheint zu sein: Halten Sie die Anzahl der "lebenden" Objekte niedrig; Löschen Sie Ihre Zeichenfolgen und Objekte, sobald Sie sie nicht mehr benötigen, und erstellen Sie sie bei Bedarf erneut.

Bis zu einem gewissen Grad sind diese Lösungen widersprüchlich: Um die Anzahl der Objekte im Speicher niedrig zu halten, sind mehr Zuweisungen und Aufhebungen erforderlich. Umgekehrt kann die ständige Wiederverwendung derselben Objekte bedeuten, dass mehr Objekte im Speicher bleiben als unbedingt erforderlich.


Nun zu Ihrer Frage. Ob Sie ein Objekt zurücksetzen, indem Sie ein neues erstellen oder alle seine Eigenschaften löschen: Dies hängt davon ab, was Sie anschließend damit tun möchten.

Sie möchten ihm wahrscheinlich neue Eigenschaften zuweisen:

  • Wenn Sie dies sofort tun, empfehle ich, die neuen Eigenschaften sofort zuzuweisen und das Löschen oder Löschen zuerst zu überspringen. (Stellen Sie jedoch sicher, dass alle Eigenschaften entweder überschrieben oder gelöscht werden!)
  • Wenn das Objekt nicht sofort verwendet wird, sondern zu einem späteren Zeitpunkt wieder aufgefüllt wird, empfehle ich, es zu löschen oder null zuzuweisen und später ein neues zu erstellen.

Es gibt keine schnelle und benutzerfreundliche Möglichkeit, ein JScript-Objekt für die Wiederverwendung zu löschen, als wäre es ein neues Objekt - ohne ein neues zu erstellen. Was bedeutet, dass die kurze Antwort auf Ihre Frage "Nein" lautet, wie jthompson sagt.

Martijn
quelle
4

Es ist etwas Neues, sich auf Object.observe in ES7 und auf die Datenbindung im Allgemeinen zu freuen. Erwägen:

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()

Unter diesen Umständen kann # 2 die beste Wahl sein.

Terry Thorsen
quelle
Wenn Sie Frameworks wie AngularJS verwenden, mit denen Objekte an Ansichten gebunden werden können (Ein- oder Zwei-Wege-Bindung), wird durch das Löschen von Objekten mit obj = {}Framework das Framework nicht über weitere Änderungen am Objekt informiert, sodass Ihre Vorlagen nicht ordnungsgemäß gerendert werden. Option 2 funktioniert jedoch ordnungsgemäß.
Ivan Hušnjak
Guter Punkt. Ich muss dasselbe Heap-Objekt behalten, und dies zeigt einen weiteren Grund, warum Sie dieselbe Referenz behalten möchten.
Cody
1

Sie können die Requisiten löschen, aber keine Variablen. delete abc;ist in ES5 ungültig (und wirft bei strikter Verwendung).

Sie können es null zuweisen, um es für das Löschen im GC festzulegen (dies ist nicht der Fall, wenn Sie andere Verweise auf Eigenschaften haben).

Das Festlegen der lengthEigenschaft für ein Objekt ändert nichts. (es setzt nur die Eigenschaft fest)

Ven
quelle
Um es klar lengthauszudrücken: Wenn Sie in einem Array (bei dem es sich um einen bestimmten Objekttyp handelt - wenn Sie mir nicht glauben, versuchen Sie es auszuführen typeof []) den Wert 0 festlegen, wird nicht nur die Eigenschaft festgelegt, sondern auch der Inhalt des Arrays gelöscht . Siehe stackoverflow.com/questions/1232040/…
Sean the Bean
Nun, Sie können Löschen sagen, window.abcweil es in diesem Fall eine Requisite ist.
Shaedrich
0

Das hat mich ewig nervt, also hier ist meine Version, da ich kein leeres Objekt wollte, sondern eines mit allen Eigenschaften, aber auf einen Standardwert zurückgesetzt. Ein bisschen wie eine neue Instanziierung einer Klasse.

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
quelle