Как быстро очистить объект JavaScript? - PullRequest
150 голосов
/ 26 марта 2009

С помощью массива JavaScript я могу сбросить его в пустое состояние с помощью одного присваивания:

array.length = 0;

Это делает массив «кажущимся» пустым и готовым к повторному использованию, и, насколько я понимаю, это единственная «операция», то есть постоянное время.

Есть ли подобный способ очистки объекта JS? Я знаю, что могу перебирать его поля, удаляя их:

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

но это имеет линейную сложность.

Я также могу просто выбросить объект и создать новый:

obj = {};

Но «беспорядочное» создание новых объектов приводит к проблемам с сборкой мусора в IE6. ( Как описано здесь )

Ответы [ 7 ]

102 голосов
/ 26 февраля 2013

Ну, есть риск, что все будет слишком просто ...

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

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

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

47 голосов
/ 26 марта 2009

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

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

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

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

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

35 голосов
/ 04 декабря 2016

ES5

Решение ES5 может быть:

// for enumerable properties
Object.keys(obj).forEach(function (prop) {
  delete obj[prop];
});

// for all properties
Object.getOwnPropertyNames(obj).forEach(function (prop) {
  delete obj[prop];
});

ES6

И решение ES6 может быть:

// for enumerable properties
for (const prop of Object.keys(obj)) {
  delete obj[prop];
}

// for all properties
for (const prop of Object.getOwnPropertyNames(obj)) {
  delete obj[prop];
}

Производительность

Независимо от характеристик, самые быстрые решения, как правило, будут:

// for enumerable properties
var props = Object.keys(obj);
for (var i = 0; i < props.length; i++) {
  delete obj[props[i]];
}

// for all properties of an object with proto chain
var props = Object.getOwnPropertyNames(obj);
for (var i = 0; i < props.length; i++) {
  delete obj[props[i]];
}

// for all 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 голосов
/ 27 марта 2009

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

  1. Сборка мусора происходит раз в столько выделений ; следовательно, чем больше выделений вы сделаете, тем чаще будет работать GC;
  2. Чем больше объектов у вас «в воздухе», тем больше времени занимает каждый запуск Сборки мусора (поскольку он пролистает весь список объектов, чтобы увидеть, какие из них помечены как мусор).

Решение проблемы 1, по-видимому, заключается в следующем: уменьшить количество выделений; назначайте новые объекты и строки как можно меньше.

Решение проблемы 2, по-видимому, заключается в следующем: уменьшить количество «живых» объектов; удаляйте свои строки и объекты, как только они вам больше не нужны, и создавайте их заново при необходимости.

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


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

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

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

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

6 голосов
/ 19 октября 2016

Вы можете попробовать это. Функция ниже устанавливает все значения свойств объекта на неопределенные. Работает также и с вложенными объектами.

var clearObjectValues = (objToClear) => {
    Object.keys(objToClear).forEach((param) => {
        if ( (objToClear[param]).toString() === "[object Object]" ) {
            clearObjectValues(objToClear[param]);
        } else {
            objToClear[param] = undefined;
        }
    })
    return objToClear;
};
4 голосов
/ 10 июля 2015

Что-то новое, о чем стоит подумать, ожидая 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 может быть лучшим выбором.

1 голос
/ 04 марта 2013

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

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

Установка свойства length для объекта ничего не меняет. (это только, ну, устанавливает свойство)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...