Для цикла и оператора удаления

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

var obj = {a:0,b:0}, i=0;
for (var k in obj) {
    obj[i++] = 0;
}
alert(i) // 2

demo http://jsfiddle.net/kqzLG/

Приведенный выше код демонстрирует, что я добавляю новые свойства, но новые свойства не будут перечислены.

Однако оператор удаления, похоже, бросает вызов моей теории моментальных снимков. Здесь тот же код, но удаляет свойство до того, как оно будет перечислить.

var obj = {a:0,b:0}, i=0;
for (var k in obj) {
    i++;
    delete obj.b;
}
alert(i) // 1

demo http://jsfiddle.net/Gs2vh/

Приведенный выше код демонстрирует, что тело цикла выполняется только один раз. Он выполнил бы два раза, если бы теория моментальных снимков была правдой.

Что здесь происходит? Есть ли у javascript какой-то скрытый итератор, который он использует, и оператор удаления каким-то образом знает об этом?

- Я понимаю, что я предполагаю что-то об итерационном заказе, в частности, что итерация происходит в зависимости от времени ввода свойства. Я считаю, что все браузеры используют такую ​​реализацию.

Ответ 1

Интересный вопрос. Ответ лежит в спецификации (внимание мое):

Механика и порядок перечисления свойств (шаг 6.a в первом алгоритме, шаг 7.a во втором) не указывается. Свойства перечислимого объекта могут быть удалены во время перечисления. Если свойство, которое еще не было посещено во время перечисления, будет удалено, то оно не будет посещено. Если новые свойства добавляются к объекту, который перечисляется во время перечисления, вновь добавленные свойства не гарантированно будет посещаться в активном перечислении. Имя свойства не должно посещаться более одного раза в любом перечислении.

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

Например, в Chrome кажется, что числовые свойства сохраняются перед алфавитными:

> Object.keys({a:0, 0:1});
  ["0", "a"]

Однако, даже если вы добавляете алфавитные клавиши:

var obj = {a:0,b:0};
for (var k in obj) {
    obj['c'] = 0;
    console.log(k);
}

c, кажется, не пройден, вывод a b.

Firefox показывает то же поведение, хотя ключи хранятся в порядке размещения:

> Object.keys({a:0, 0:1});
  ["a", "0"]

Ответ 2

obj[i++] = 0;

Первая итерация:

Запись: я = 0, Выход, я = 1

Вторая итерация:

Запись: я = 1, Выход, я = 2

Кроме того, циклы javascript for in не гарантируются для выполнения при итерации по объекту. Таким образом, delete obj.b; дает непредсказуемые результаты.