222

With a JavaScript Array, I can reset it to an empty state with a single assignment:

array.length = 0;

This makes the Array "appear" empty and ready to reuse, and as far as I understand is a single "operation" - that is, constant time.

Is there a similar way to clear a JS Object? I know I can iterate its fields deleting them:

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

but this has linear complexity.

I can also just throw the object away and create a new one:

obj = {};

But "promiscuous" creation of new objects leads to problems with Garbage Collection on IE6. (As described here)

tanguy_k
  • 11,307
  • 6
  • 54
  • 58
levik
  • 114,835
  • 27
  • 73
  • 90
  • 2
    "array.length == 0 ... is a single 'operation' - that is, constant time" — I doubt that. – Miles Mar 26 '09 at 05:39
  • 1
    I don't believe it deletes any of the contents - just makes things like push() work as though the array were empty. Do you have a reference to the opposite being true? – levik Mar 26 '09 at 05:44
  • 2
    @derobert: That's a bit presumptuous. The IE6 Garbage Collection problem is well documented. – levik Mar 26 '09 at 05:46
  • The code in the original post is incorrect. The inner loop should be: delete obj[prop]. See my posting http://stackoverflow.com/questions/6780315/javascript-delete-doesnt-work-inside-iteration-loop – stackoverflowuser2010 Jul 22 '11 at 20:00
  • 6
    for anyone using that snippet, use `for (var prop in obj)` – bendytree Mar 28 '13 at 17:07
  • @levik See "Method 2" section: https://stackoverflow.com/a/1232046/1952991 – ceremcem Oct 30 '18 at 17:30
  • Wouldn't `array.splice(0, array.length)` be better? – Orwellophile Mar 12 '23 at 07:16

11 Answers11

149

Well, at the risk of making things too easy...

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

...would seem to be pretty effective in cleaning the object in one line of code with a minimum of scary brackets. All members will be truly deleted instead of left as garbage.

Obviously if you want to delete the object itself, you'll still have to do a separate delete() for that.

Wytze
  • 7,844
  • 8
  • 49
  • 62
  • 3
    Deletion only breaks the reference, e.g. it makes myObject[member] evaluate to undefined, and technically does leave the object as garbage unless another reference to the object exists. – Joey Carson Apr 26 '15 at 03:58
  • 1
    This answer is very responsible. If applied to all managed objects in a dispose phase, it will generally take care of many of the accidental object chains. rock on Wytze – deepelement Aug 25 '15 at 19:50
  • 1
    The OP suggests using `hasOwnProperty` in this loop. I've read the docs, but I'm still trying to wrap my head around what, if any, are the risks if we omit the `hasOwnProperty()` check? – logidelic Nov 23 '16 at 17:17
  • 3
    I thought it was dangerous to delete properties while iterating over the object -- in most languages this would invalidate the iterator and break things, either subtly or catastrophically -- but apparently this is OK in JavaScript per MDN: "A property that is deleted before it has been visited will not be visited later." https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in – Piotr Feb 27 '17 at 23:07
77

ES5

ES5 solution can be:

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

ES6

And ES6 solution can be:

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

Performance

Regardless of the specs, the quickest solutions will generally be:

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

The reason why for..in should be performed only on shallow or plain object is that it traverses the properties that are prototypically inherited, not just own properties that can be deleted. In case it isn't known for sure that an object is plain and properties are enumerable, for with Object.getOwnPropertyNames is a better choice.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
61

The short answer to your question, I think, is no (you can just create a new object).

  1. In this example, I believe setting the length to 0 still leaves all of the elements for garbage collection.

  2. You could add this to Object.prototype if it's something you'd frequently use. Yes it's linear in complexity, but anything that doesn't do garbage collection later will be.

  3. This is the best solution. I know it's not related to your question - but for how long do we need to continue supporting IE6? There are many campaigns to discontinue the usage of it.

Feel free to correct me if there's anything incorrect above.

Seb
  • 959
  • 16
  • 29
jthompson
  • 7,178
  • 2
  • 34
  • 33
  • 4
    Some companies have to support IE6 as a matter of policy - and will while it enjoys double-digit market share. The IE GC issue isn't that things go uncollected, it's that collection runs every X allocations, and takes longer each time. Thus the need to reuse objects. – levik Mar 26 '09 at 05:21
  • Yeah, there are many cases where it's still used due to company policy/etc. I was off topic ranking out of spite :) So how does delete obj.prop; perform when the property is itself an object? I don't know if you gain much efficiency there. – jthompson Mar 26 '09 at 05:25
  • 2
    poor GC means that IE6 will run slower, meaning that there's even more incentive to upgrade. You're still supporting it, it's just that it'll run slow. – nickf Mar 26 '09 at 05:47
  • 1
    It means your app performs poorly for a portion of your target audience. Some people don't have the option of upgrading because they are in a controlled IT environment. Perhaps their company uses an Active-X control that only works with IE6. – levik Mar 26 '09 at 06:00
  • 2
    @levik just don't work for a company that forces you to support IE6. this can't be a good company anyways. – low_rents Jul 10 '15 at 08:11
  • The IE6 issue is not the only problem with `myArr = []` (instead of `myArr.length = 0`). Since Arrays are Objects and objects are passed around by reference in JS; if you have a service that provides a reference to an array to other class, and later tries to reset the array by creating a new one, a reference to the old (uncleared) array is still alive in the subclass. This will couse data inconsistance issues. Therefore alwayes use myArr.length = 0 (and not the 2nd method) – Gil Epshtain May 14 '18 at 12:22
14

Easiest way:

Object.keys(object).forEach(key => {
  delete object[key];
})

Object.keys will return each key of the object as an array, for example:

const user = {
  name: 'John Doe',
  age: 25,
};

Object.keys(user) // ['name', 'age']

forEach will run the function in first parameter for each element, and the delete keyword deletes a key from an object. Therefore, the code will delete a key of the object for each key.

Siegh
  • 161
  • 2
  • 5
  • Please provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Tyler2P May 01 '21 at 15:11
  • thanks for the help <3 this is my first time answering a question in stack overflow. – Siegh May 03 '21 at 13:18
  • 1
    You could reduce this to a single line by removing the curly braces – ThirstyCamel May 06 '21 at 10:50
10

You can try this. Function below sets all values of object's properties to undefined. Works as well with nested objects.

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
  • 145
  • 1
  • 7
  • Note as the fields are only being set as undefined this will cause memory leaks over time as the fields won't be cleared by the garbage collector. – Alexis Tyler Nov 06 '19 at 07:09
7

So to recap your question: you want to avoid, as much as possible, trouble with the IE6 GC bug. That bug has two causes:

  1. Garbage Collection occurs once every so many allocations; therefore, the more allocations you make, the oftener GC will run;
  2. The more objects you've got ‘in the air’, the more time each Garbage Collection run takes (since it'll crawl through the entire list of objects to see which are marked as garbage).

The solution to cause 1 seems to be: keep the number of allocations down; assign new objects and strings as little as possible.

The solution to cause 2 seems to be: keep the number of 'live' objects down; delete your strings and objects as soon as you don't need them anymore, and create them afresh when necessary.

To a certain extent, these solutions are contradictory: to keep the number of objects in memory low will entail more allocations and de-allocations. Conversely, constantly reusing the same objects could mean keeping more objects in memory than strictly necessary.


Now for your question. Whether you'll reset an object by creating a new one, or by deleting all its properties: that will depend on what you want to do with it afterwards.

You’ll probably want to assign new properties to it:

  • If you do so immediately, then I suggest assigning the new properties straightaway, and skip deleting or clearing first. (Make sure that all properties are either overwritten or deleted, though!)
  • If the object won't be used immediately, but will be repopulated at some later stage, then I suggest deleting it or assigning it null, and create a new one later on.

There's no fast, easy to use way to clear a JScript object for reuse as if it were a new object — without creating a new one. Which means the short answer to your question is ‘No’, like jthompson says.

Martijn
  • 13,225
  • 3
  • 48
  • 58
4

Something new to think about looking forward to Object.observe in ES7 and with data-binding in general. Consider:

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

So under that circumstance #2 can be the best choice.

Terry Thorsen
  • 241
  • 2
  • 4
  • If using frameworks such as AngularJS which can bind objects to views (one or two way binding), clearing object with `obj = {}` will make framework unaware of any further change to object therefore your templates will not get properly rendered. Option #2 however, will work properly. – Ivan Hušnjak Mar 02 '16 at 14:08
  • Good point. I need to keep the same heap object, and this demonstrates another reason you'd want to keep the same reference. – Cody Mar 28 '17 at 21:02
3

This bugged me for ages so here is my version as I didn't want an empty object, I wanted one with all the properties but reset to some default value. Kind of like a new instantiation of a class.

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 }
Andy
  • 2,124
  • 1
  • 26
  • 29
1

You can delete the props, but don't delete variables. delete abc; is invalid in ES5 (and throws with use strict).

You can assign it to null to set it for deletion to the GC (it won't if you have other references to properties)

Setting length property on an object does not change anything. (it only, well, sets the property)

Ven
  • 19,015
  • 2
  • 41
  • 61
  • To be clear, when setting `length` to 0 on an array (which is a specific type of object - if you don't believe me, try running `typeof []`), it will not only set the property, it will in fact clear the contents of the array. See http://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript#comment7533900_1234337 – Sean the Bean Nov 13 '15 at 19:10
  • Well, you can say delete `window.abc` because it's a prop in that case. – shaedrich Jul 31 '18 at 07:50
0

const obj = {
  second: 'hasValue',
  third: false,
  first: null,
  fourth: true
}

const translator = {
  null: '-',
  true: 'OK',
  false: 'NO'
}

const cleanObject = Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, translator[value] || value]));
console.log(cleanObject); //{ second: 'hasValue', third: 'NO', first: '-', fourth: 'OK' }
-7

Assuming your object name is BASKET, simply set BASKET = "";

user670265
  • 483
  • 4
  • 3