Подтвердить что ты не робот

JavaScript Loops: for... in vs for

В Javascript я столкнулся с странным поведением. Я получаю

"Объект не поддерживает это свойство или метод"

для функции removeAttribute в следующем коде:

var buttons = controlDiv.getElementsByTagName("button");
for ( var button in buttons )
    button.removeAttribute('disabled');

Когда я меняю код следующим образом, проблема исчезает:

var buttons = controlDiv.getElementsByTagName("button");
for ( var i = 0; i < buttons.length; i++ )
    buttons[i].removeAttribute('disabled');

Каково значение button внутри for...in?

4b9b3361

Ответ 1

Не используйте for..in для итерации массива.

Важно понимать, что синтаксис квадратной скобки Javascript Array ([]) для доступа к указателям фактически унаследован от Object...

obj.prop === obj['prop']  // true

Структура for..in не работает как более традиционный for..each/in, который можно найти на других языках (php, python и т.д.).

Javascript for..in предназначен для перебора свойств объекта . Создание ключа каждого свойства. Используя этот ключ в сочетании с синтаксисом Object, вы можете легко получить доступ к значениям, которые вы после.

var obj = {
    foo: "bar",
    fizz: "buzz",
    moo: "muck"
};

for ( var prop in obj ) {
    console.log(prop);      // foo / fizz / moo
    console.log(obj[prop]); // bar / buzz / muck
}

И поскольку массив представляет собой просто объект с последовательными числовыми именами свойств (индексы), то for..in работает аналогичным образом, производя числовые знаки так же, как он выдает имена свойств выше.

Важной характеристикой структуры for..in является то, что она продолжает поиск перечислимых свойств в цепочке прототипов. Он также будет выполнять итерацию унаследованных перечислимых свойств. Вы должны убедиться, что текущее свойство существует непосредственно на локальном объекте, а не прототипе, к которому он прикреплен с помощью hasOwnProperty()...

for ( var prop in obj ) {
    if ( obj.hasOwnProperty(prop) ) {
        // prop is actually obj property (not inherited)
    }
}

(Подробнее о прототипальном наследовании)

Проблема с использованием структуры for..in в типе Array заключается в том, что нет никакого garauntee относительно того, в каком порядке создаются свойства... и, вообще говоря, это очень важная функция при обработке массива.

Другая проблема заключается в том, что обычно медленнее, чем стандартная реализация for.

Нижняя линия

Использование массива for...in для итерации похоже на использование приклада отвертки для вождения гвоздя... почему бы вам просто не использовать молоток (for)?

Ответ 2

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

Чтобы перебрать массивы, вы должны использовать обычный цикл for. buttons - это не массив, а NodeList (массив, подобный структуре).

Если для buttons с помощью for...in выполнить следующую команду:

for(var i in a) {
    console.log(i)
}

Вы увидите, что он выводит что-то вроде:

1
2
...
length
item

потому что length и item являются двумя свойствами объекта типа NodeList. Итак, если вы наивно используете for..in, вы попытаетесь получить доступ к buttons['length'].removeAttribute(), который выдает ошибку, поскольку buttons['length'] является функцией, а не элементом DOM.

Таким образом, правильный способ - использовать обычный цикл for. Но есть еще одна проблема:

NodeList являются живыми, что означает каждый раз, когда вы получаете доступ, например. length, список обновляется (элементы снова ищутся). Поэтому вам следует избегать ненужных вызовов length.

Пример:

for(var i = 0, l = buttons.length; i < l, i++)

Ответ 3

for(var key in obj) { } выполняет итерацию по всем элементам объекта, включая те из его прототипов. Поэтому, если вы используете его и не знаете ничего расширенного Object.prototype, вы всегда должны тестировать obj.hasOwnProperty(key) и пропустить ключ, если эта проверка возвращает false.

for(start; continuation; loop) - цикл цикла C: start выполняется до цикла, continuation проверяется и цикл продолжается только тогда, когда он истинен, loop выполняется после каждого цикла.

Ответ 4

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

Как уже отмечалось в других ответах, первичными проблемами для for.in и массивов являются:

  • Свойства не обязательно возвращаются в порядке (то есть не 0, 1, 2 и т.д.)
  • Все возвращаемые свойства возвращаются, включая свойства неиндекса и те, что указаны в цепочке [[Prototype]]. Это приводит к снижению производительности, поскольку тест hasOwnProperty, вероятно, необходим для предотвращения унаследованных свойств.

Одной из причин использования для... до ES5 было улучшение производительности при использовании редких массивов, при условии, что порядок не имеет значения. Например, в следующем:

var a = [0];
a[1000] = 1;

Итерация над использованием для... будет намного быстрее, чем использование цикла for, поскольку он будет посещать только два свойства, тогда как цикл for будет пытаться выполнить 1001.

Однако этот случай избыточен ES5 forEach, который посещает только те члены, которые существуют, поэтому:

a.forEach();

также будет выполнять только итерацию по двум свойствам в порядке.