【问题标题】:How to quickly clear a JavaScript Object?如何快速清除 JavaScript 对象?
【发布时间】:2009-03-26 05:01:19
【问题描述】:

使用 JavaScript 数组,我可以通过一次赋值将其重置为空状态:

array.length = 0;

这使数组“看起来”为空并准备好重复使用,据我所知,这是一个单一的“操作”——即恒定时间。

有没有类似的清除 JS 对象的方法?我知道我可以迭代它的字段来删除它们:

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

但这具有线性复杂性。

我也可以把对象扔掉并创建一个新对象:

obj = {};

但是“混杂”创建新对象会导致 IE6 上的垃圾收集出现问题。 (As described here)

【问题讨论】:

  • "array.length == 0 ... 是一个单一的“操作”——即恒定时间”——我对此表示怀疑。
  • 我不相信它会删除任何内容——只是让 push() 之类的东西像数组为空一样工作。你有没有提到相反的事实?
  • @derobert:这有点冒昧。 IE6 垃圾收集问题有据可查。
  • 原帖中的代码不正确。内循环应该是:delete obj[prop]。查看我的帖子stackoverflow.com/questions/6780315/…
  • 对于使用该 sn-p 的任何人,请使用 for (var prop in obj)

标签: javascript performance


【解决方案1】:

好吧,冒着让事情变得太容易的风险......

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

...在一行代码中用最少的可怕括号清理对象似乎非常有效。所有成员将被真正删除,而不是作为垃圾。

显然,如果您想删除对象本身,您仍然需要为此执行单独的 delete()。

【讨论】:

  • 删除只会破坏引用,例如它使 myObject[member] 评估为未定义,并且在技术上确实将对象视为垃圾,除非存在对该对象的另一个引用。
  • 这个回答很负责。如果在 dispose 阶段应用于所有托管对象,它通常会处理许多意外的对象链。在 Wytze 上摇滚
  • OP 建议在此循环中使用hasOwnProperty。我已阅读文档,但我仍在努力思考如果我们省略 hasOwnProperty() 检查有什么风险(如果有的话)?
  • 我认为在迭代对象时删除属性很危险——在大多数语言中,这会使迭代器无效并破坏事物,无论是微妙的还是灾难性的——但显然这在每个 MDN 的 JavaScript 中是可以的: "一个在被访问之前被删除的属性以后不会被访问。" developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【解决方案2】:

ES5

ES5解决方案可以是:

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

ES6

而ES6的解决方案可以是:

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

性能

无论规格如何,最快的解决方案通常是:

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

for..in 应该只在浅层或普通对象上执行的原因是它遍历原型继承的属性,而不仅仅是可以删除的自己的属性。如果不确定一个对象是普通的并且属性是可枚举的,forObject.getOwnPropertyNames 是更好的选择。

【讨论】:

    【解决方案3】:

    我认为,对您的问题的简短回答是否定的(您可以创建一个新对象)。

    1. 在此示例中,我相信将长度设置为 0 仍会将所有元素留给垃圾回收。

    2. 如果您经常使用它,您可以将它添加到 Object.prototype 中。是的,它的复杂性是线性的,但以后不进行垃圾收集的任何东西都会。

    3. 这是最好的解决方案。我知道这与您的问题无关 - 但我们需要继续支持 IE6 多久?有许多活动要停止使用它。

    以上如有不妥之处,欢迎指正。

    【讨论】:

    • 一些公司必须支持 IE6 作为政策问题 - 并且会在它享有两位数的市场份额的同时。 IE GC 的问题不是没有收集,而是收集运行每个 X 分配,并且每次都需要更长的时间。因此需要重用对象。
    • 是的,由于公司政策等原因,在很多情况下仍在使用它。我出于恶意而偏离主题排名:) 那么如何删除 obj.prop;当属性本身是一个对象时执行?我不知道你是否在那里获得了很多效率。
    • 糟糕的 GC 意味着 IE6 运行速度会变慢,这意味着升级的动力更大。你还在支持它,只是它会运行缓慢。
    • 这意味着您的应用在部分目标受众中表现不佳。有些人没有升级选项,因为他们处于受控的 IT 环境中。也许他们的公司使用的 Active-X 控件仅适用于 IE6。
    • @levik 不适合强迫您支持 IE6 的公司。这无论如何都不是一家好公司。
    【解决方案4】:

    你可以试试这个。下面的函数将对象属性的所有值设置为未定义。也适用于嵌套对象。

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

    【讨论】:

    • 请注意,由于这些字段仅设置为未定义,这将导致内存泄漏,因为垃圾收集器不会清除这些字段。
    【解决方案5】:

    因此,回顾一下您的问题:您希望尽可能避免 IE6 GC 错误带来的麻烦。该错误有两个原因:

    1. 垃圾收集每隔这么多分配发生一次;因此,您进行的分配越多,运行的 GC 就越频繁;
    2. “空中”的对象越多,每次垃圾收集运行所花费的时间就越多(因为它会爬过整个对象列表以查看哪些对象被标记为垃圾)。

    导致 1 的解决方案似乎是:减少分配次数;尽可能少地分配新的对象和字符串。

    导致 2 的解决方案似乎是:减少“活动”对象的数量;不再需要时立即删除字符串和对象,并在必要时重新创建它们。

    在一定程度上,这些解决方案是矛盾的:保持内存中的对象数量较少将需要更多的分配和取消分配。相反,不断重用相同的对象可能意味着在内存中保留的对象数量超过了绝对必要的数量。


    现在回答你的问题。是通过创建一个新对象还是通过删除其所有属性来重置对象:这将取决于您以后要如何处理它。

    您可能想要为其分配新属性:

    • 如果您立即这样做,那么我建议直接分配新属性,然后先跳过删除或清除。 (不过,请确保 所有 属性都被覆盖或删除!)
    • 如果该对象不会立即使用,但会在稍后阶段重新填充,那么我建议将其删除或将其分配为 null,然后再创建一个新对象。

    没有一种快速、易于使用的方法可以清除 JScript 对象以供重用,就好像它是一个新对象一样 — 无需创建一个新对象。这意味着您的问题的简短回答是“不”,就像 jthompson 所说的那样。

    【讨论】:

      【解决方案6】:

      最简单的方法:

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

      Object.keys 会将对象的每个键作为数组返回,例如:

      const user = {
        name: 'John Doe',
        age: 25,
      };
      
      Object.keys(user) // ['name', 'age']
      

      forEach 将为每个元素运行第一个参数中的函数,delete 关键字从对象中删除一个键。 因此,代码会为每个键删除对象的一个​​键。

      【讨论】:

      • 请解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助、质量更好,并且更有可能吸引投票。
      • 感谢帮助
      • 您可以通过删除花括号将其减少为一行
      【解决方案7】:

      期待 ES7 中的 Object.observe 和一般数据绑定的新思考。考虑:

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

      所以在这种情况下,#2 可能是最好的选择。

      【讨论】:

      • 如果使用诸如 AngularJS 之类的框架可以将对象绑定到视图(一种或两种方式绑定),使用obj = {} 清除对象将使框架不知道对对象的任何进一步更改,因此您的模板将无法获取正确渲染。但是,选项 #2 将正常工作。
      • 好点。我需要保留相同的堆对象,这说明了您希望保留相同引用的另一个原因。
      【解决方案8】:

      这困扰了我很久,所以这是我的版本,因为我不想要一个空对象,我想要一个具有所有属性但重置为某个默认值的对象。有点像类的新实例化。

      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 }

      【讨论】:

      • 你成就了我的一天!这就是我要找的!
      【解决方案9】:

      您可以删除道具,但不要删除变量。 delete abc; 在 ES5 中无效(并使用 use strict 抛出)。

      您可以将其分配为 null 以将其设置为删除到 GC(如果您有其他对属性的引用,则不会)

      在对象上设置length 属性不会改变任何东西。 (它只是设置属性)

      【讨论】:

      • 需要明确的是,在数组上将length 设置为 0(这是一种特定类型的对象 - 如果您不相信我,请尝试运行 typeof []),它不仅设置属性,它实际上会清除数组的内容。见stackoverflow.com/questions/1232040/…
      • 好吧,你可以说删除window.abc,因为在这种情况下它是一个道具。
      【解决方案10】:

      假设您的对象名称是 BASKET,只需设置 BASKET = "";

      【讨论】:

        猜你喜欢
        • 2018-01-24
        • 1970-01-01
        • 2012-01-07
        • 1970-01-01
        • 2015-08-12
        • 2021-04-17
        • 2014-05-23
        • 2013-01-17
        • 2015-03-06
        相关资源
        最近更新 更多