Як швидко очистити об’єкт JavaScript?


170

За допомогою масиву JavaScript я можу скинути його в порожній стан за допомогою одного призначення:

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

2
@derobert: Це трохи самонадійно. Проблема збору сміття IE6 добре задокументована.
levik

Код у початковій публікації невірний. Внутрішній цикл повинен бути: видалити obj [prop]. Дивіться мою публікацію stackoverflow.com/questions/6780315/…
stackoverflowuser2010

6
для тих, хто використовує цей фрагмент, використовуйтеfor (var prop in obj)
bendytree

Відповіді:


54

Я думаю, що коротка відповідь на ваше запитання - ні (ви можете просто створити новий об’єкт).

  1. В цьому прикладі, я вважаю, що встановлення довжини 0 досі залишає всі елементи для збору сміття.

  2. Ви можете додати це до Object.prototype, якщо це щось, що ви часто використовуєте. Так, це лінійно за складністю, але все, що згодом не збирає сміття, буде.

  3. Це найкраще рішення. Я знаю, що це не пов’язано з вашим питанням - але на скільки часу нам потрібно продовжувати підтримувати IE6? Існує багато кампаній, щоб припинити їх використання.

Не соромтеся виправити мене, якщо є щось неправильне вище.


4
Деякі компанії повинні підтримувати IE6 як питання політики - і вони будуть користуватися двозначною часткою ринку. Проблема IE GC полягає не в тому, що речі не збираються, а в тому, що колекція працює кожне X розподілу і щоразу займає більше часу. Таким чином необхідність повторного використання об'єктів.
levik

Так, є багато випадків, коли він все ще використовується через політику компанії / тощо. Я був поза темою ранжирування невдало :) Отже, як видалити obj.prop; виконувати, коли власність сама є об'єктом? Я не знаю, чи багато ви отримуєте там ефективності.
jthompson

2
поганий GC означає, що IE6 працюватиме повільніше, що означає ще більше стимулів до оновлення. Ви все ще підтримуєте це, це просто те, що він буде працювати повільно.
nickf

1
Це означає, що ваш додаток погано працює для частини вашої цільової аудиторії. Деякі люди не мають можливості оновлення, оскільки вони знаходяться в контрольованому ІТ-середовищі. Можливо, їхня компанія використовує елемент управління Active-X, який працює лише з IE6.
levik

2
@levik просто не працює для компанії, яка змушує вас підтримувати IE6. це не може бути хорошою компанією в будь-якому випадку.
low_rents

121

Ну, ризикуючи зробити речі занадто простими ...

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

... здавалося б, досить ефективно очистити об'єкт в одному рядку коду з мінімальними страшними дужками. Усі учасники будуть справді видалені, а не залишені як сміття.

Очевидно, що якщо ви хочете видалити сам об’єкт, вам все одно доведеться зробити окреме delete () для цього.


2
Видалення лише порушує посилання, наприклад, воно змушує myObject [member] оцінювати як невизначений, а технічно він залишає об'єкт як сміття, якщо не існує іншої посилання на об'єкт.
Joey Carson

Ця відповідь дуже відповідальна. Якщо застосовуватись до всіх керованих об'єктів у фазі утилізації, то вона, як правило, піклується про багато випадкових ланцюгів об'єктів. рок на Вайце
заглиблення

1
ОП пропонує використовувати hasOwnPropertyв цьому циклі. Я читав документи, але все ще намагаюся обернути голову навколо того, які, якщо такі є, ризики, якщо ми пропустимо hasOwnProperty()чек?
logidelic

2
Я подумав, що видаляти властивості небезпечно під час ітерації над об'єктом - у більшості мов це призведе до недійсності ітератора та пошкодження речей, або тонко, або катастрофічно - але, мабуть, це в порядку в JavaScript за MDN: "Властивість, видалену раніше його відвідано не буде відвідувано згодом ". 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слід виконувати лише дрібний або звичайний об'єкт, полягає в тому, що він переміщує властивості, які прототипічно успадковуються, а не лише власні властивості, які можна видалити. У разі , якщо не відомо точно , що об'єкт є простим і властивості перелічуваних, 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;
};

Зверніть увагу, оскільки поля встановлюються лише як невизначені, це спричинить протікання пам'яті з часом, оскільки поля не будуть очищені сміттєзбірником.
Олексій Тайлер

7

Тож, щоб скласти резюме свого питання: ви хочете максимально уникнути проблем з помилкою IE6 GC. Ця помилка має дві причини:

  1. Збір сміття відбувається раз на стільки виділень ; отже, чим більше виділень, тим більше запускається офіційний GC;
  2. Чим більше об’єктів у вас "у повітрі", тим більше часу займає кожен цикл збирання сміття (оскільки він проскаже весь список об'єктів, щоб побачити, які позначені як сміття).

Рішення, що викликає 1, здається: зменшити кількість виділень; присвоюйте нові об'єкти та рядки якомога менше.

Як видається, рішення 2: зменшити кількість "живих" об'єктів; видаляйте свої рядки та об’єкти, як тільки вони вам більше не потрібні, і створюйте їх заново, коли це необхідно.

В певній мірі ці рішення суперечливі: утримувати кількість об'єктів у пам'яті низькою, це спричинить за собою більше виділень та дерозподілень. І навпаки, постійне використання одних і тих же об'єктів може означати збереження в пам'яті більше об'єктів, ніж суворо необхідне.


Тепер до вашого питання. Чи будете ви скинути об’єкт, створивши новий, або видаливши всі його властивості: це залежатиме від того, що ви хочете зробити з ним згодом.

Ви, ймовірно, захочете призначити йому нові властивості:

  • Якщо ви це зробите негайно, я пропоную призначити нові властивості відразу, а пропустити видалення чи очищення спочатку. (Переконайтесь, що всі властивості або перезаписані, або видалені!)
  • Якщо об’єкт не буде використаний негайно, але буде повторно заселений на більш пізньому етапі, тоді я пропоную видалити його або призначити його нульовим, а потім створити новий.

Немає швидкого, простого у використанні способу очищення об'єкта JScript для повторного використання, як ніби це новий об’єкт - без створення нового. Що означає, що коротка відповідь на ваше запитання - «Ні», як говорить jthompson.


4

Щось нове, що варто подумати про те, щоб сподіватися на Object.observe в ES7 та загальнозміцнюючі дані взагалі. Поміркуйте:

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, однак, буде працювати належним чином.
Іван Гушняк

Гарна думка. Мені потрібно зберегти той самий об’єкт купи, і це демонструє ще одну причину, за якою ви хочете зберегти ту саму посилання.
Коді

1

Ви можете видалити реквізит, але не видаляти змінні. delete abc;недійсний у ES5 (і кидає з використанням строго).

Ви можете призначити його null, щоб встановити його для видалення в GC (не буде, якщо у вас є інші посилання на властивості)

Встановлення lengthвластивості на об'єкт нічого не змінює. (він встановлює лише властивість)


Щоб було зрозуміло, якщо встановити lengthна масив 0 на 0 (що є певним типом об'єкта - якщо ви мені не вірите, спробуйте запустити typeof []), він не лише встановить властивість, а й очистить вміст масиву. . Дивіться 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 }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.