JavaScriptオブジェクトをすばやくクリアする方法は?


170

JavaScript配列を使用すると、1つの割り当てでそれを空の状態にリセットできます。

array.length = 0;

これにより、配列が空になり、再利用の準備が整います。私が理解している限り、単一の「操作」、つまり一定の時間です。

JSオブジェクトをクリアする同様の方法はありますか?私はそれらを削除してフィールドを反復できることを知っています:

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

しかし、これは線形の複雑さを持っています。

オブジェクトを捨てて新しいオブジェクトを作成することもできます。

obj = {};

しかし、新しいオブジェクトを「無差別」に作成すると、IE6のガベージコレクションで問題が発生します。(ここに記載されているように


2
"array.length == 0 ...は単一の「操作」です。つまり、一定の時間です」—私はそれを疑います。
マイル

1
内容が削除されるとは思わない-配列が空であるかのようにpush()などを機能させるだけです。真実であるという反対への言及はありますか?
levik 2009年

2
@derobert:ちょっとおかしいです。IE6ガベージコレクションの問題は十分に文書化されています。
levik 2009年

元の投稿のコードは正しくありません。内部ループは次のようになります。deleteobj [prop]。私の投稿stackoverflow.com/questions/6780315/…を
stackoverflowuser2010

6
そのスニペットを使用する人は、次のように使用してくださいfor (var prop in obj)
bendytree '28

回答:


54

あなたの質問に対する短い答えは「いいえ」だと思います(新しいオブジェクトを作成することができます)。

  1. この例では、長さを0に設定しても、ガベージコレクションのすべての要素が残っています。

  2. 頻繁に使用するものであれば、Object.prototypeに追加できます。はい、複雑さは直線的ですが、後でガベージコレクションを行わないものはすべてそうなります。

  3. これが最良の解決策です。私はそれがあなたの質問に関連していないことを知っています-しかし、IE6をサポートし続ける必要がある期間はどれくらいですか?それの使用を中止する多くのキャンペーンがあります。

上記に誤りがある場合は、遠慮なく訂正してください。


4
一部の企業はポリシーの問題としてIE6をサポートする必要があり、2桁の市場シェアを享受している間もそうです。IE GCの問題は、物事が収集されないことではなく、収集がすべてのX割り当てを実行し、毎回時間がかかることです。したがって、オブジェクトを再利用する必要があります。
levik 2009年

ええ、会社の方針などでまだ使われているケースが多いです。私はトピックランキングから外れていました:)では、どのようにobj.propを削除しますか?プロパティ自体がオブジェクトであるときに実行しますか?あなたがそこで多くの効率を得るかどうか私は知りません。
jthompson 2009年

2
不十分なGCは、IE6の実行速度が遅くなることを意味します。つまり、アップグレードするインセンティブがさらに高まります。あなたはまだそれをサポートしています、それはそれが遅くなるというだけです。
nickf 2009年

1
これは、ターゲットユーザーの一部に対してアプリのパフォーマンスが低いことを意味します。管理されたIT環境にいるため、アップグレードするオプションがない人もいます。おそらく、同社はIE6でのみ機能するActive-Xコントロールを使用しています。
levik 2009年

2
@levikは、IE6のサポートを強制する会社では機能しません。とにかく、これは良い会社になることはできません。
low_rents 2015

121

まあ、物事を簡単にしてしまうリスクがあります...

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

...最低限の恐ろしいブラケットを使用して、1行のコードでオブジェクトをクリーンアップするのにかなり効果があるようです。すべてのメンバーは、ゴミとして残されるのではなく、本当に削除されます。

当然、オブジェクト自体を削除する場合でも、そのために個別のdelete()を実行する必要があります。


2
削除は参照を破壊するだけです。たとえば、myObject [member]を未定義と評価し、オブジェクトへの別の参照が存在しない限り、技術的にはオブジェクトをガベージとして残します。
Joey Carson、

この答えは非常に責任があります。破棄フェーズですべての管理対象オブジェクトに適用すると、通常、偶発的なオブジェクトチェーンの多くが処理されます。Wytzeのロック
deepelement

1
OPはhasOwnProperty、このループでの使用を提案しています。私はドキュメントを読みましたが、hasOwnProperty()チェックを省略した場合のリスクはあるとすれば、それでも頭を包み込もうとしていますか?
logidelic 2016年

2
オブジェクトを反復処理しているときにプロパティを削除するのは危険だと思いました。ほとんどの言語では、イテレータが無効になり、微妙にまたは破滅的に破壊されます。訪問されたことがあるので、後で訪問されることはありません。」 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
ピョートル・

46

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だけ浅いまたはプレーンオブジェクトで実行されなければならないが、それはprototypically継承されるプロパティ、削除することができるだけでなく独自のプロパティを横断ということです。ケースでは、オブジェクトが無地で、プロパティは、列挙されていることを確認するために知られていないforとのObject.getOwnPropertyNamesより良い選択です。


7

これを試すことができます。以下の関数は、オブジェクトのプロパティのすべての値を未定義に設定します。ネストされたオブジェクトでも機能します。

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

フィールドは未定義としてのみ設定されているため、ガベージコレクターによってフィールドがクリアされないため、時間の経過とともにメモリリークが発生することに注意してください。
Alexis Tyler

7

だからあなたの質問を要約すると:IE6 GCバグのトラブルをできるだけ避けたいです。そのバグには2つの原因があります。

  1. ガベージコレクションは、非常に多くの割り当てごとに1回発生します。したがって、割り当てる割り当てが多いほど、GCが頻繁に実行されます。
  2. 「空中」にあるオブジェクトが多いほど、ガベージコレクションの実行にかかる時間が長くなります(オブジェクトのリスト全体をクロールして、ガベージとしてマークされているオブジェクトを確認するため)。

1を発生させる解決策は次のようです。割り当ての数を抑えてください。新しいオブジェクトと文字列をできるだけ少なく割り当てます。

2の原因となる解決策は次のようです。「ライブ」オブジェクトの数を抑えます。文字列とオブジェクトが不要になったらすぐに削除し、必要に応じて新しく作成します。

これらの解決策はある程度矛盾しています。メモリ内のオブジェクトの数を少なく保つことは、より多くの割り当てと割り当て解除を必要とします。逆に、同じオブジェクトを常に再利用することは、必要以上に多くのオブジェクトをメモリに保持することを意味します。


さてあなたの質問のために。新しいオブジェクトを作成してオブジェクトをリセットするか、すべてのプロパティを削除してオブジェクトをリセットするかは、後でオブジェクトをどのように処理するかによって異なります。

おそらくそれに新しいプロパティを割り当てたいでしょう:

  • すぐにそうする場合は、新しいプロパティをすぐに割り当てて、最初に削除またはクリアをスキップすることをお勧めします。(ただし、すべてのプロパティが上書きまたは削除されていることを確認してください!)
  • オブジェクトがすぐには使用されないが、後の段階で再入力される場合は、削除するかnullを割り当てて、後で新しいオブジェクトを作成することをお勧めします。

新しいオブジェクトを作成することなく、JScriptオブジェクトをクリアして新しいオブジェクトであるかのように再利用するための高速で使いやすい方法はありません。つまり、jthompsonが言うように、質問に対する短い答えは「いいえ」です。


4

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は正しく機能します。
IvanHušnjak16年

いい視点ね。同じヒープオブジェクトを保持する必要があります。これは、同じ参照を保持したいもう1つの理由を示しています。
Cody

1

小道具は削除できますが、変数は削除しないでください。delete abc;ES5では無効です(use strictでスローします)。

これをnullに割り当てて、GCに削除するように設定できます(プロパティへの他の参照がない場合はそうではありません)。

lengthオブジェクトのプロパティを設定しても、何も変更されません。(それだけで、まあ、プロパティを設定します)


明確にするlengthために、配列(特定のタイプのオブジェクトです-信じられない場合は実行してみてくださいtypeof [])を0 に設定すると、プロパティが設定されるだけでなく、実際に配列の内容も消去されます。stackoverflow.com/questions/1232040/…を
Sean the Bean

まあ、window.abcその場合は小道具なので削除と言えます。
shaedrich

0

これは何年もの間私を悩ませたので、空のオブジェクトが欲しくなかったので、ここに私のバージョンがあります。クラスの新しいインスタンス化のようなものです。

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 }

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.