Come cancellare rapidamente un oggetto JavaScript?


170

Con un array JavaScript, posso ripristinarlo a uno stato vuoto con una singola assegnazione:

array.length = 0;

Questo fa sì che l'array "appaia" vuoto e pronto per essere riutilizzato, e per quanto ho capito è una singola "operazione", ovvero tempo costante.

Esiste un modo simile per cancellare un oggetto JS? So di poter iterare i suoi campi eliminandoli:

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

ma questo ha una complessità lineare.

Posso anche semplicemente buttare via l'oggetto e crearne uno nuovo:

obj = {};

Ma la creazione "promiscua" di nuovi oggetti porta a problemi con Garbage Collection su IE6. ( Come descritto qui )


2
"array.length == 0 ... è una singola" operazione ", ovvero tempo costante" - ne dubito.
Miglia,

1
Non credo che elimini alcun contenuto - fa solo funzionare cose come push () come se l'array fosse vuoto. Hai un riferimento al fatto che sia vero il contrario?
levik,

2
@derobert: è un po 'presuntuoso. Il problema IE6 Garbage Collection è ben documentato.
levik,

Il codice nel post originale non è corretto. Il ciclo interno dovrebbe essere: delete obj [prop]. Vedi i miei post stackoverflow.com/questions/6780315/…
stackoverflowuser2010

6
per chiunque usi quel frammento, usafor (var prop in obj)
bendytree

Risposte:


54

La risposta breve alla tua domanda, credo, è no (puoi semplicemente creare un nuovo oggetto).

  1. In questo esempio, credo che l'impostazione della lunghezza su 0 lasci ancora tutti gli elementi per la garbage collection.

  2. Puoi aggiungerlo a Object.prototype se è qualcosa che utilizzeresti frequentemente. Sì, è lineare nella complessità, ma tutto ciò che non farà la spazzatura in seguito lo sarà.

  3. Questa è la soluzione migliore So che non è correlato alla tua domanda, ma per quanto tempo dobbiamo continuare a supportare IE6? Esistono molte campagne per interrompere l'utilizzo.

Sentiti libero di correggermi se c'è qualcosa di sbagliato sopra.


4
Alcune aziende devono supportare IE6 come una questione di politica - e godranno mentre gode di quote di mercato a due cifre. Il problema con IE GC non è che le cose non vengono raccolte, è che la raccolta esegue ogni allocazione X e impiega più tempo ogni volta. Quindi la necessità di riutilizzare gli oggetti.
levik,

Sì, ci sono molti casi in cui è ancora utilizzato a causa della politica aziendale / ecc. Ero fuori tema per dispetto :) Quindi, come si cancella obj.prop; eseguire quando la proprietà è essa stessa un oggetto? Non so se guadagni molta efficienza lì.
jthompson,

2
GC scadente significa che IE6 funzionerà più lentamente, il che significa che c'è ancora più incentivo per l'aggiornamento. Lo stai ancora supportando, è solo che andrà piano.
Nickf

1
Significa che la tua app funziona male per una parte del tuo pubblico di destinazione. Alcune persone non hanno la possibilità di eseguire l'aggiornamento perché si trovano in un ambiente IT controllato. Forse la loro azienda utilizza un controllo Active-X che funziona solo con IE6.
levik,

2
@levik non lavora per un'azienda che ti obbliga a supportare IE6. questa non può essere comunque una buona compagnia.
low_rents,

121

Bene, a rischio di rendere le cose troppo facili ...

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

... sembrerebbe abbastanza efficace nel ripulire l'oggetto in una riga di codice con un minimo di parentesi spaventose. Tutti i membri verranno veramente eliminati invece di essere lasciati come spazzatura.

Ovviamente se vuoi cancellare l'oggetto stesso, dovrai comunque fare un delete () separato per quello.


2
La cancellazione interrompe solo il riferimento, ad esempio fa valutare myObject [membro] come indefinito e tecnicamente lascia l'oggetto come spazzatura a meno che non esista un altro riferimento all'oggetto.
Joey Carson,

Questa risposta è molto responsabile. Se applicato a tutti gli oggetti gestiti in una fase di smaltimento, si prenderà generalmente cura di molte catene di oggetti accidentali. rock on Wytze
deepelement

1
L'OP suggerisce l'uso hasOwnPropertyin questo ciclo. Ho letto i documenti, ma sto ancora cercando di avvolgere la mia testa su quali, se del caso, sono i rischi se omettiamo il hasOwnProperty()controllo?
logidelic

2
Ho pensato che fosse pericoloso eliminare le proprietà durante l'iterazione sull'oggetto - nella maggior parte delle lingue questo avrebbe invalidato l'iteratore e spezzato le cose, sottilmente o catastroficamente - ma a quanto pare questo è OK in JavaScript per MDN: "Una proprietà che è stata eliminata prima è stato visitato non verrà visitato più tardi. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Piotr

46

ES5

La soluzione ES5 può essere:

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

ES6

E la soluzione ES6 può essere:

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

Prestazione

Indipendentemente dalle specifiche, le soluzioni più veloci saranno generalmente:

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

Il motivo per cui for..indovrebbe essere eseguito solo su oggetti superficiali o semplici è che attraversa le proprietà ereditate prototipicamente, non solo le proprietà che possono essere eliminate. Nel caso non si sa con certezza che un oggetto è semplice e proprietà sono enumerabili, forcon Object.getOwnPropertyNamesè una scelta migliore.


7

Puoi provare questo. La funzione seguente imposta tutti i valori delle proprietà dell'oggetto su indefinito. Funziona anche con oggetti nidificati.

var clearObjectValues = (objToClear) => {
    Object.keys(objToClear).forEach((param) => {
        if ( (objToClear[param]).toString() === "[object Object]" ) {
            clearObjectValues(objToClear[param]);
        } else {
            objToClear[param] = undefined;
        }
    })
    return objToClear;
};

Nota: poiché i campi vengono impostati come non definiti, ciò causerà perdite di memoria nel tempo poiché i campi non verranno cancellati dal Garbage Collector.
Alexis Tyler,

7

Quindi, per ricapitolare la tua domanda: vuoi evitare, per quanto possibile, problemi con il bug GC IE6. Tale errore ha due cause:

  1. Garbage Collection si verifica una volta ogni tante allocazioni ; pertanto, più allocazioni vengono effettuate, più spesso verrà eseguito GC;
  2. Più oggetti hai "in aria", più tempo impiega ogni esecuzione di Garbage Collection (poiché scorrerà l'intero elenco di oggetti per vedere quali sono contrassegnati come spazzatura).

La soluzione alla causa 1 sembra essere: mantenere basso il numero di allocazioni; assegnare nuovi oggetti e stringhe il meno possibile.

La soluzione alla causa 2 sembra essere: mantenere basso il numero di oggetti "attivi"; elimina le stringhe e gli oggetti non appena non ti servono più e creali di nuovo quando necessario.

In una certa misura, queste soluzioni sono contraddittorie: mantenere basso il numero di oggetti in memoria comporterà più allocazioni e de-allocazioni. Al contrario, riutilizzare costantemente gli stessi oggetti potrebbe significare mantenere più oggetti in memoria di quanto strettamente necessario.


Ora per la tua domanda. Sia che ripristinerai un oggetto creandone uno nuovo o eliminando tutte le sue proprietà: ciò dipenderà da ciò che desideri farne successivamente.

Probabilmente vorrai assegnargli nuove proprietà:

  • Se lo fai immediatamente, ti suggerisco di assegnare subito le nuove proprietà e di saltare prima l'eliminazione o la cancellazione. (Assicurati però che tutte le proprietà siano sovrascritte o eliminate!)
  • Se l'oggetto non verrà utilizzato immediatamente, ma verrà ripopolato in un secondo momento, quindi suggerisco di eliminarlo o assegnarlo null e crearne uno nuovo in seguito.

Non esiste un modo rapido e facile da usare per cancellare un oggetto JScript da riutilizzare come se fosse un nuovo oggetto, senza crearne uno nuovo. Ciò significa che la risposta breve alla tua domanda è "No", come dice jthompson.


4

Qualcosa di nuovo da considerare per guardare avanti a Object.observe in ES7 e con l'associazione dei dati in generale. Tener conto di:

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

Quindi in quella circostanza # 2 può essere la scelta migliore.


Se si utilizzano framework come AngularJS che possono associare gli oggetti alle viste (associazione a una o due vie), la cancellazione dell'oggetto con obj = {}renderà il framework ignaro di ulteriori modifiche all'oggetto, pertanto i modelli non verranno visualizzati correttamente. L'opzione n. 2 tuttavia funzionerà correttamente.
Ivan Hušnjak

Buon punto. Devo mantenere lo stesso oggetto heap e questo dimostra un'altra ragione per cui vorresti mantenere lo stesso riferimento.
Cody,

1

Puoi eliminare gli oggetti di scena, ma non eliminare le variabili. delete abc;non è valido in ES5 (e genera con un uso rigoroso).

Puoi assegnarlo a null per impostarlo per l'eliminazione al GC (non lo sarà se hai altri riferimenti alle proprietà)

L'impostazione della lengthproprietà su un oggetto non cambia nulla. (solo, beh, imposta la proprietà)


Per essere chiari, quando si imposta lengthsu 0 su un array (che è un tipo specifico di oggetto - se non mi credi, prova a correre typeof []), non imposterà solo la proprietà, ma cancellerà di fatto il contenuto dell'array . Vedi stackoverflow.com/questions/1232040/…
Sean the Bean,

Bene, puoi dire di eliminare window.abcperché è un sostegno in quel caso.
shaedrich

0

Questo mi ha infastidito per anni, quindi ecco la mia versione in quanto non volevo un oggetto vuoto, ne volevo uno con tutte le proprietà ma ripristinato su un valore predefinito. Un po 'come una nuova istanza di una 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 }

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.