Циклы JavaScript: для ... в сравнении с для - PullRequest
13 голосов
/ 10 марта 2011

Я столкнулся со странным поведением в 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?

Ответы [ 4 ]

45 голосов
/ 10 марта 2011

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

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

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

Структура for..in не работает, как более традиционная for..each/in, которая встречалась бы в других языках (php, python и т. Д.).

Javascript's 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's property (not inherited)
    }
}

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

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

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

Итог

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

7 голосов
/ 10 марта 2011

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 s активны, то есть при каждом обращении, например, length, список обновляется (элементы снова ищутся).Поэтому вам следует избегать ненужных вызовов на length.

Пример:

for(var i = 0, l = buttons.length; i < l, i++)
0 голосов
/ 08 октября 2014

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

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

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

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

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

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

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

a.forEach();

также будет перебирать только два свойства по порядку.

0 голосов
/ 10 марта 2011

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

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

...