Как быстро очистить объект 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 () работать так, как если бы массив был пустым. Есть ли у вас ссылка на то, что это правда?
Левик
2
@derobert: Это немного самонадеянно. Проблема сборки мусора в IE6 хорошо документирована.
Левик
Код в оригинальном сообщении неверен. Внутренний цикл должен быть: delete obj [prop]. См. Мою публикацию stackoverflow.com/questions/6780315/…
stackoverflowuser2010
6
для любого, кто использует этот фрагмент, используйтеfor (var prop in obj)
bendytree

Ответы:

54

Я думаю, что короткий ответ на ваш вопрос - нет (вы можете просто создать новый объект).

  1. В этом примере я считаю, что установка длины в 0 все равно оставляет все элементы для сборки мусора.

  2. Вы можете добавить это в Object.prototype, если вы часто его используете. Да, он линейный по сложности, но все, что не будет делать сборку мусора позже, будет.

  3. Это лучшее решение. Я знаю, что это не связано с вашим вопросом - но как долго нам нужно продолжать поддерживать IE6? Есть много кампаний, чтобы прекратить использование этого.

Не стесняйтесь поправлять меня, если есть что-то неправильное выше.

jthompson
источник
4
Некоторые компании должны поддерживать IE6 в соответствии с политикой - и пока она будет занимать двузначную долю рынка. Проблема IE GC заключается не в том, что все остается незатронутым, а в том, что сборка выполняется при каждом выделении X и занимает больше времени каждый раз. Таким образом, необходимо повторно использовать объекты.
Левик
Да, во многих случаях он все еще используется из-за политики компании и т. Д. Я был вне темы рейтинга вне зависимости от :) Так как же удалить obj.prop; выполнить, когда свойство само является объектом? Я не знаю, добьетесь ли вы там большой эффективности.
Jthompson
2
плохой сборщик мусора означает, что IE6 будет работать медленнее, что означает, что стимул для обновления еще больше. Вы все еще поддерживаете это, просто это будет работать медленно.
Nickf
1
Это означает, что ваше приложение работает плохо для части вашей целевой аудитории. Некоторые люди не имеют возможности обновления, потому что они находятся в контролируемой ИТ-среде. Возможно, их компания использует элемент управления Active-X, который работает только с IE6.
Левик
2
@levik просто не работает на компанию, которая заставляет вас поддерживать IE6. это не может быть хорошей компанией в любом случае.
low_rents
121

Ну, рискуя сделать вещи слишком простыми ...

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

... может показаться довольно эффективным для очистки объекта в одной строке кода с минимумом страшных скобок. Все участники будут действительно удалены, а не оставлены как мусор.

Очевидно, что если вы хотите удалить сам объект, вам все равно придется сделать для этого отдельную функцию delete ().

Wytze
источник
2
Удаление только разрывает ссылку, например, это делает myObject [member] для оценки неопределенной, и технически оставляет объект как мусор, если не существует другой ссылки на объект.
Джои Карсон
Этот ответ очень ответственный. Применительно ко всем управляемым объектам в фазе утилизации, он обычно заботится о многих цепочках случайных объектов. рок на Wytze
углубление
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 выглядит следующим образом: уменьшить количество «живых» объектов; удаляйте свои строки и объекты, как только они вам больше не нужны, и создавайте их заново при необходимости.

В некоторой степени эти решения противоречивы: сохранение небольшого количества объектов в памяти повлечет за собой большее выделение и перераспределение. И наоборот, постоянное повторное использование одних и тех же объектов может означать сохранение большего количества объектов в памяти, чем это строго необходимо.


Теперь на ваш вопрос. Будете ли вы сбрасывать объект, создавая новый или удаляя все его свойства: это будет зависеть от того, что вы хотите с ним делать потом.

Возможно, вы захотите присвоить ему новые свойства:

  • Если вы сделаете это немедленно, то я предлагаю сразу назначить новые свойства, а сначала пропустить удаление или очистку. (Убедитесь, что все свойства либо перезаписаны, либо удалены!)
  • Если объект не будет использоваться немедленно, но будет повторно заполнен на более позднем этапе, тогда я предлагаю удалить его или присвоить ему значение null, а затем создать новый.

Не существует быстрого, простого в использовании способа очистки объекта JScript для повторного использования, как если бы это был новый объект - без создания нового. Это означает, что краткий ответ на ваш вопрос «Нет», как говорит Джтомпсон.

Мартейн
источник
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свойства для объекта ничего не меняет. (это только, ну, устанавливает свойство)

Вен
источник
Для ясности, при установке значения length0 в массиве (который является конкретным типом объекта - если вы мне не верите, попробуйте запустить typeof []), он не только установит свойство, но и фактически очистит содержимое массива. , См. Stackoverflow.com/questions/1232040/…
Шон Боб
Ну, вы можете сказать, удалить, window.abcпотому что это опора в этом случае.
Шедрих
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 }

ozzy432836
источник