¿Cómo borrar rápidamente un objeto JavaScript?

170

Con una matriz de JavaScript, puedo restablecerlo a un estado vacío con una sola asignación:

array.length = 0;

Esto hace que la matriz "parezca" vacía y lista para reutilizarse, y hasta donde yo entiendo es una sola "operación", es decir, tiempo constante.

¿Hay una forma similar de borrar un objeto JS? Sé que puedo iterar sus campos eliminándolos:

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

Pero esto tiene una complejidad lineal.

También puedo tirar el objeto y crear uno nuevo:

obj = {};

Pero la creación "promiscua" de nuevos objetos genera problemas con Garbage Collection en IE6. ( Como se describe aquí )

levik
fuente
2
"array.length == 0 ... es una única 'operación', es decir, tiempo constante", lo dudo.
Miles
1
No creo que elimine ninguno de los contenidos, solo hace que cosas como push () funcionen como si la matriz estuviera vacía. ¿Tienes una referencia a que lo contrario es verdad?
levik
2
@derobert: Eso es un poco presuntuoso. El problema de la recolección de basura IE6 está bien documentado.
levik
El código en la publicación original es incorrecto. El bucle interno debe ser: delete obj [prop]. Vea mi publicación stackoverflow.com/questions/6780315/…
stackoverflowuser2010
66
para cualquiera que use ese fragmento, usefor (var prop in obj)
bendytree

Respuestas:

54

La respuesta corta a su pregunta, creo, es no (simplemente puede crear un nuevo objeto).

  1. En este ejemplo, creo que establecer la longitud en 0 todavía deja todos los elementos para la recolección de basura.

  2. Puede agregar esto a Object.prototype si es algo que usaría con frecuencia. Sí, es lineal en complejidad, pero cualquier cosa que no haga la recolección de basura más adelante lo será.

  3. Esta es la mejor solución. Sé que no está relacionado con su pregunta, pero ¿por cuánto tiempo debemos continuar admitiendo IE6? Hay muchas campañas para descontinuar su uso.

Siéntase libre de corregirme si hay algo incorrecto arriba.

jthompson
fuente
44
Algunas compañías tienen que apoyar IE6 como una cuestión de política, y lo harán mientras disfruten de una participación de mercado de dos dígitos. El problema de IE GC no es que las cosas no se recojan, es que la colección ejecuta cada asignación X y lleva más tiempo cada vez. De ahí la necesidad de reutilizar objetos.
levik
Sí, hay muchos casos en los que todavía se usa debido a la política de la compañía / etc. Estaba fuera de la clasificación de temas por despecho :) Entonces, ¿cómo se elimina obj.prop; realizar cuando la propiedad es en sí misma un objeto? No sé si ganas mucha eficiencia allí.
jthompson
2
GC pobre significa que IE6 funcionará más lentamente, lo que significa que hay aún más incentivos para actualizar. Todavía lo estás apoyando, es solo que funcionará lentamente.
nickf
1
Significa que su aplicación funciona mal para una parte de su público objetivo. Algunas personas no tienen la opción de actualizar porque se encuentran en un entorno de TI controlado. Quizás su compañía usa un control Active-X que solo funciona con IE6.
levik
2
@levik simplemente no funciona para una empresa que te obliga a apoyar IE6. Esto no puede ser una buena compañía de todos modos.
low_rents
121

Bueno, a riesgo de hacer las cosas demasiado fáciles ...

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

... parece ser bastante efectivo para limpiar el objeto en una línea de código con un mínimo de paréntesis de miedo. Todos los miembros se eliminarán realmente en lugar de dejarse como basura.

Obviamente, si desea eliminar el objeto en sí, aún tendrá que hacer un delete () por separado para eso.

Wytze
fuente
2
La eliminación solo rompe la referencia, por ejemplo, hace que myObject [member] evalúe como indefinido, y técnicamente deja el objeto como basura a menos que exista otra referencia al objeto.
Joey Carson el
Esta respuesta es muy responsable. Si se aplica a todos los objetos administrados en una fase de eliminación, generalmente se ocupará de muchas de las cadenas de objetos accidentales. rock on Wytze
deepelement
1
El OP sugiere usar hasOwnPropertyen este bucle. He leído los documentos, pero todavía estoy tratando de entender cuáles son los riesgos, si los hay, si omitimos el hasOwnProperty()cheque.
logidelic
2
Pensé que era peligroso eliminar propiedades mientras se iteraba sobre el objeto, en la mayoría de los idiomas esto invalidaría el iterador y rompería las cosas, sutil o catastróficamente, pero aparentemente esto está bien en JavaScript por MDN: "Una propiedad que se elimina antes ha sido visitado no será visitado más tarde ". developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Piotr
46

ES5

La solución ES5 puede ser:

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

ES6

Y la solución ES6 puede ser:

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

Actuación

Independientemente de las especificaciones, las soluciones más rápidas generalmente serán:

// 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 razón por la for..inque se debe realizar solo en objetos poco profundos o simples es que atraviesa las propiedades que se heredan prototípicamente, no solo las propiedades propias que se pueden eliminar. En caso de que no se sabe con certeza que un objeto es liso y propiedades son enumerables, forcon Object.getOwnPropertyNameses una mejor opción.

Frasco Estus
fuente
7

Puedes probar esto. La siguiente función establece todos los valores de las propiedades del objeto en indefinido. Funciona también con objetos anidados.

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
fuente
Tenga en cuenta que los campos solo se configuran como indefinidos, esto provocará pérdidas de memoria con el tiempo ya que el recolector de basura no borrará los campos.
Alexis Tyler
7

Para recapitular su pregunta: desea evitar, en la medida de lo posible, problemas con el error IE6 GC. Ese error tiene dos causas:

  1. La recolección de basura ocurre una vez cada tantas asignaciones ; por lo tanto, cuantas más asignaciones realice, más a menudo se ejecutará GC;
  2. Cuantos más objetos tenga 'en el aire', más tiempo llevará cada ejecución de Recolección de basura (ya que rastreará toda la lista de objetos para ver cuáles están marcados como basura).

La solución para causar 1 parece ser: mantener baja la cantidad de asignaciones; Asigne nuevos objetos y cadenas lo menos posible.

La solución para causar 2 parece ser: mantener baja la cantidad de objetos 'vivos'; elimine sus cadenas y objetos tan pronto como ya no los necesite, y créelos nuevamente cuando sea necesario.

Hasta cierto punto, estas soluciones son contradictorias: mantener baja la cantidad de objetos en la memoria implicará más asignaciones y desasignaciones. Por el contrario, reutilizar constantemente los mismos objetos podría significar mantener más objetos en la memoria de los estrictamente necesarios.


Ahora para tu pregunta. Ya sea que restablezca un objeto creando uno nuevo o eliminando todas sus propiedades: eso dependerá de lo que quiera hacer con él después.

Probablemente quieras asignarle nuevas propiedades:

  • Si lo hace de inmediato, le sugiero que asigne las nuevas propiedades de inmediato y omita eliminar o borrar primero. (¡Sin embargo, asegúrese de que todas las propiedades se sobrescriban o se eliminen!)
  • Si el objeto no se usará de inmediato, pero se repoblará en una etapa posterior, sugiero eliminarlo o asignarlo como nulo, y crear uno nuevo más adelante.

No hay una forma rápida y fácil de usar para borrar un objeto JScript para reutilizarlo como si fuera un objeto nuevo, sin crear uno nuevo. Lo que significa que la respuesta corta a su pregunta es 'No', como dice jthompson.

Martijn
fuente
4

Algo nuevo en lo que pensar acerca de esperar a Object.observe en ES7 y con el enlace de datos en general. Considerar:

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

Entonces, bajo esa circunstancia, # 2 puede ser la mejor opción.

Terry Thorsen
fuente
Si utiliza marcos como AngularJS que pueden enlazar objetos a vistas (enlace de una o dos vías), al borrar el objeto con el obj = {}marco no se sabrá ningún cambio adicional al objeto, por lo tanto, sus plantillas no se representarán correctamente. Sin embargo, la opción n. ° 2 funcionará correctamente.
Ivan Hušnjak
Buen punto. Necesito mantener el mismo objeto de montón, y esto demuestra otra razón por la que querrías mantener la misma referencia.
Cody
1

Puede eliminar los accesorios, pero no elimine las variables. delete abc;es inválido en ES5 (y lanza con uso estricto).

Puede asignarlo a nulo para configurarlo para su eliminación al GC (no lo hará si tiene otras referencias a propiedades)

Establecer la lengthpropiedad en un objeto no cambia nada. (solo, bueno, establece la propiedad)

Ven
fuente
Para ser claros, cuando se establece lengthen 0 en una matriz (que es un tipo específico de objeto; si no me cree, intente ejecutar typeof []), no solo establecerá la propiedad, de hecho borrará el contenido de la matriz . Ver stackoverflow.com/questions/1232040/…
Sean the Bean el
Bueno, puedes decir eliminar window.abcporque es un accesorio en ese caso.
shaedrich
0

Esto me molestó durante años, así que aquí está mi versión, ya que no quería un objeto vacío, quería uno con todas las propiedades pero restablecer algunos valores predeterminados. Algo así como una nueva instanciación de una clase.

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
fuente