Bir JavaScript Nesnesi nasıl hızlı bir şekilde temizlenir?


170

Bir JavaScript Dizisi ile, tek bir atamayla boş bir duruma sıfırlayabilirim:

array.length = 0;

Bu, Diziyi "görünür" ve yeniden kullanıma hazır hale getirir ve anladığım kadarıyla tek bir "işlem" dir, yani sabit bir zaman.

JS Nesnesini temizlemenin benzer bir yolu var mı? Onları silerek alanlarını yineleyebileceğimi biliyorum:

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

ancak bu doğrusal karmaşıklığa sahiptir.

Ayrıca sadece nesneyi atıp yeni bir tane oluşturabilirim:

obj = {};

Ancak yeni nesnelerin "karışık" oluşturulması IE6'da Çöp Toplama ile ilgili sorunlara yol açar. ( Burada açıklandığı gibi )


2
"array.length == 0 ... tek bir 'işlem' - yani, sabit zaman" - Bundan şüpheliyim.
Miles

1
İçeriğin herhangi birini siler inanmıyorum - sadece dizi boş gibi push () çalışma gibi şeyler yapar. Bunun tersi olmanın bir referansı var mı?
levik

2
@derobert: Bu biraz küstahça. IE6 Çöp Toplama sorunu iyi belgelenmiştir.
levik

Orijinal gönderideki kod yanlış. İç döngü şöyle olmalıdır: delete obj [prop].
stackoverflowuser2010

6
bu pasajı kullanan herkes için şunu kullanınfor (var prop in obj)
bendytree

Yanıtlar:


55

Sorunuzun kısa cevabı, sanırım, hayır (sadece yeni bir nesne yaratabilirsiniz).

  1. Bu örnekte, uzunluğun 0 olarak ayarlanmasının çöp toplama için tüm öğeleri bıraktığına inanıyorum.

  2. Sık kullandığınız bir şeyse bunu Object.prototype dosyasına ekleyebilirsiniz. Evet, karmaşıklık açısından doğrusaldır, ancak daha sonra çöp toplama yapmayan herhangi bir şey olacaktır.

  3. Bu en iyi çözümdür. Sorunuzla ilgili olmadığını biliyorum - ama ne kadar süreyle IE6'yı desteklemeye devam etmemiz gerekiyor? Kullanımının kesilmesine yönelik birçok kampanya vardır.

Yukarıda yanlış bir şey varsa beni düzeltmekten çekinmeyin.


4
Bazı şirketler IE6'yı politika olarak desteklemelidir ve çift haneli pazar payına sahip olacaklardır. IE GC sorunu, işlerin toplanmayacağı değil, koleksiyonun her X tahsisini çalıştırdığı ve her seferinde daha uzun sürdüğü anlamına gelir. Böylece nesneleri yeniden kullanma ihtiyacı.
levik

Evet, şirket politikası vb.Nedeniyle hala kullanıldığı birçok durum var. Konu sıra dışı rağmen ben :) Bu yüzden nasıl obj.prop silmek; özelliğin kendisi bir nesne olduğunda gerçekleştirmek? Orada çok fazla verimlilik kazanıp kazanmadığınızı bilmiyorum.
jthompson

2
zayıf GC, IE6'nın daha yavaş çalışacağı anlamına gelir, yani yükseltme için daha fazla teşvik vardır. Hala destekliyorsunuz, sadece yavaş çalışacak.
nickf

1
Bu, uygulamanızın hedef kitlenizin bir kısmı için düşük performans gösterdiği anlamına gelir. Bazı insanlar, kontrollü bir BT ortamında oldukları için yükseltme seçeneğine sahip değildir. Belki de şirketleri sadece IE6 ile çalışan bir Active-X kontrolü kullanıyor.
levik

2
@levik sizi IE6'yı desteklemeye zorlayan bir şirket için çalışmıyor. bu zaten iyi bir şirket olamaz.
low_rents

121

Şeyleri çok kolay hale getirme riski altında ...

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

... nesnenin bir kod satırında minimum korkutucu parantez ile temizlenmesinde oldukça etkili görünmektedir. Çöp olarak bırakılmak yerine tüm üyeler gerçekten silinecek.

Açıkçası, nesnenin kendisini silmek istiyorsanız, bunun için yine de ayrı bir silme () yapmanız gerekecektir.


2
Silme işlemi yalnızca başvuruyu keser, örneğin myObject [üye] 'nin tanımsız olarak değerlendirilmesini sağlar ve nesneye başka bir başvuru olmadıkça nesneyi çöp olarak teknik olarak bırakır.
Joey Carson

Bu cevap çok sorumlu. Atma aşamasında yönetilen tüm nesnelere uygulanırsa, genellikle yanlışlıkla nesne zincirlerinin çoğuyla ilgilenir. Wytze kaya
deepelement

1
OP hasOwnPropertybu döngüde kullanılmasını önerir . Dokümanları okudum, ama yine de kafamı hasOwnProperty()kontrol etmeyi ihmal edersek riskler nelerdir?
logidelic

2
Nesnenin üzerinde yineleme yaparken özellikleri silmenin tehlikeli olduğunu düşündüm - çoğu dilde bu yineleyiciyi geçersiz kılacak ve şeyleri ya incelikle ya da felaketle kıracaktır - ama görünüşe göre bu MDN başına JavaScript'te sorun yok: "Daha önce silinmiş bir özellik daha sonra ziyaret edilmeyecek. " developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/…
Piotr

46

ES5

ES5 çözümü şunlar olabilir:

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

ES6

Ve ES6 çözümü şunlar olabilir:

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

Verim

Özellikler ne olursa olsun, genellikle en hızlı çözümler:

// 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..inYalnızca sığ veya düz nesnelerde gerçekleştirilmesinin nedeni , yalnızca silinebilen kendi özelliklerinin değil, prototip olarak miras alınan özellikleri de geçmesidir. Durumda bir nesne sadedir ve özellikleri, enumerable olduğundan emin olarak bilinmemektedir forile Object.getOwnPropertyNamesdaha iyi bir seçimdir.


7

Bunu deneyebilirsiniz. Aşağıdaki işlev, nesnenin özelliklerinin tüm değerlerini tanımsız olarak ayarlar. İç içe nesnelerle de çalışır.

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

Alanlar yalnızca tanımsız olarak ayarlandığından, alanlar çöp toplayıcı tarafından temizlenmeyeceğinden zaman içinde bellek sızıntılarına neden olacağını unutmayın.
Alexis Tyler

7

Sorunuzu yeniden özetlemek için: IE6 GC hatasıyla mümkün olduğunca sorun yaşamamaktan kaçınmak istersiniz. Bu hatanın iki nedeni vardır:

  1. Çöp Toplama çok fazla tahsisatta bir gerçekleşir ; bu nedenle, ne kadar fazla tahsis yaparsanız, GC o kadar sık ​​çalışır;
  2. 'Havada' ne kadar çok nesneye sahip olursanız, her bir Çöp Toplama işlemi daha fazla zaman alır (hangilerinin çöp olarak işaretlendiğini görmek için tüm nesne listesi boyunca taranacağından).

1'e neden olan çözüm şöyledir: tahsis sayısını düşük tutun; yeni nesneleri ve dizeleri mümkün olduğunca az atayın.

2'ye neden olan çözüm şu şekildedir: 'canlı' nesnelerin sayısını aşağıda tutun; dizelerinizi ve nesnelerinizi artık ihtiyacınız olmadığı anda silin ve gerektiğinde yeniden oluşturun.

Belli bir dereceye kadar bu çözümler çelişkilidir: bellekteki nesnelerin sayısını düşük tutmak daha fazla ayırma ve ayırma gerektirecektir. Tersine, aynı nesneleri sürekli olarak yeniden kullanmak, kesinlikle gerekenden daha fazla nesnenin hafızada tutulması anlamına gelebilir.


Şimdi sorunuz için. Bir nesneyi yeni bir nesne oluşturarak veya tüm özelliklerini silerek sıfırlayıp sıfırlamayacağınız: bu, daha sonra onunla ne yapmak istediğinize bağlı olacaktır.

Muhtemelen yeni özellikler atamak isteyeceksiniz:

  • Bunu hemen yaparsanız, yeni özellikleri hemen atamanızı ve önce silmeyi veya temizlemeyi atlamanızı öneririm. ( Yine de tüm özelliklerin üzerine yazıldığından veya silindiğinden emin olun !)
  • Nesne hemen kullanılmayacak, ancak daha sonraki bir aşamada yeniden doldurulacaksa, onu silmenizi veya null atamanızı ve daha sonra yeni bir tane oluşturmanızı öneririm.

Bir JScript nesnesini yeni bir nesne gibi yeniden kullanmak üzere temizlemenin hızlı ve kullanımı kolay bir yolu yoktur - yeni bir nesne oluşturmadan. Bu, jthompson'un dediği gibi, sorunuzun kısa cevabının 'Hayır' olduğu anlamına gelir.


4

ES7'de Object.observe'i ve genel olarak veri bağlamayı dört gözle beklemek için yeni bir şey. Düşünmek:

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

Bu durumda # 2 en iyi seçim olabilir.


Nesneleri görünümlere bağlayabilen AngularJS gibi çerçeveler kullanıyorsanız (bir veya iki yönlü ciltleme), nesneyi temizlemek, obj = {}çerçeveyi nesnede daha fazla değişiklik yapılmasından habersiz hale getirir, bu nedenle şablonlarınız düzgün şekilde işlenmez. Ancak # 2 seçeneği düzgün şekilde çalışacaktır.
Ivan Hušnjak

İyi bir nokta. Aynı yığın nesnesini tutmam gerekiyor ve bu da aynı başvuruyu saklamak istediğiniz başka bir nedeni gösteriyor.
Cody

1

Propsları silebilirsiniz, ancak değişkenleri silmeyin. delete abc;ES5'te geçersizdir (ve katı kullanımla birlikte atar).

GC'ye silinmek üzere ayarlamak için null değerine atayabilirsiniz (özelliklere başka referanslarınız varsa bunu yapmaz)

lengthBir nesnede özellik ayarlamak hiçbir şeyi değiştirmez. (yalnızca özelliği ayarlar)


Açık olmak gerekirse length, bir dizide 0 olarak ayarlandığında (belirli bir nesne türüdür - bana inanmıyorsanız, çalıştırmayı deneyin typeof []), sadece özelliği ayarlamaz, aslında dizinin içeriğini temizler . Bkz. Stackoverflow.com/questions/1232040/…
Sean the Bean

Sil, diyebilirsiniz window.abcçünkü bu durumda bir pervane.
shaedrich

0

Boş bir nesne istemiyordu bu yüzden burada benim sürümüm beni yaşattı, ben tüm özellikleri ile bir istedim ama bazı varsayılan değere sıfırlayın. Bir sınıfın yeni bir örneği gibi.

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 }

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.